I am very new to using Apple let alone developing an app for them, but looking at creating a simple app to learn the language a little bit.
I am trying to open a file in an app, but the app needs to be opened with admin rights. I've tested running in the command to prove the concept works by running the following:
sudo open /private/etc/apache2/extra/httpd-vhosts.conf -a TextEdit
This as expected asks for the password in the terminal and then opens the app with the file to be editable.
I've then tried doing the following Swift to do the same thing:
let fileToOpen : String = txtFileToOpen.stringValue;
let task = Process();
task.launchPath = "/usr/bin/env"
task.arguments = ["sudo", "open", fileToOpen, "-a", "TextEdit"];
task.launch();
I was expecting that because I am running the above in app and not a terminal, that I would get the Apple dialog asking for my admin password to give permission for the above to run from my app, but unfortunately that doesn't work, instead I get the error:
env: sudo: Operation not permitted
Is there a way I can do the above and prompt the admin password to allow my process to run from my app?
Related
In XCUI based test automation framework, I am trying to run terminal commands via Swift code which involves few internal cli tools and sudo commands. Either sudo or internal commands are getting recognised when executed via Swift process().
Below is the swift function implemented to call and run shell script commands
import Foundation
func runShell(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/zsh"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
print(runShell("sh /Users/../script.sh"))
Ran shell with file
runShell("sh /Users/../script.sh"))
Output:
/Users/../script.sh: line 18: /usr/bin/sudo: Operation not permitted
/Users/../script.sh: line 19: /usr/bin/python3: command not found
Tried running the same shell file via applescript with terminal window open
runShell("sudo su -l admin -c \"osascript /Users/../script.sh\"")
Output via Applescript:
zsh:1: operation not permitted: sudo
The above shell script and apple script works fine when it is executed directly via terminal instead of Swift. Please suggest a solution to run the shell script via Swift with maximum access and privileges.
Create entitlements file and set Sandbox value to No
Menu: File > New > File
Use the Property List template
Name the file as MYPROJECT.entitlements
Open the file via Project navigator
Add new row as below
Key: App Sandbox Type: Boolean Value: No
Select Project in navigator to open project settings
Select the target and navigate to Build Settings
In the list, find CODE_SIGN_ENTITLEMENTS
Set its value to the location of the MYPROJECT.entitlements file.
Do a Clean Build Folder, select a suitable build platform and perform a Product Build or Run.
I am looking to run a command on command line from my SWIFT app in MAC.
var resString = "open \(app.getLocation()) --args 25 40"
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", resString]
task.launch()
print (resString)
when I print the resString on console, I. get the following
open [path to app on my local drive] --args 25 40
which executes normally when I copy paste in the command line. but on the other side, the app is opened but arguments are being ignored.
I also tried this https://stackoverflow.com/a/53725745/12197482 still didn't work, the app is launched but the arguments don't get through
EDIT: Here's the funniest thing that's really frustrating, I created a small script that have a command, which contains
#! /bin/bash
open ./SimulatorDebug.app --args arg1 arg2
I ran it from terminal again, args are passed correctly with no issues. I tried to run at from my app and the same issue happened again, app run play BUT NO ARGS are being passed which is I find really weird.
Try using /bin/sh as executableURL instead:
task.executableURL = URL(fileURLWithPath: "/bin/sh")
That should do it.
You might also want to try using task.run() instead, and waiting for completion:
try task.run()
task.waitUntilExit()
I wrote System to make this much easier. Feel free to use it or copy/paste the code:
https://github.com/eneko/System/blob/master/Sources/System/System.swift#L107
Updated with additional info
Since you are trying to open a macOS app, there is no need to use sh or bash, you can invoke open directly.
To test this, I've written a sample macOS app in Xcode that all it does is display arguments on screen. Then, I proceeded to launch the app from a macOS Playground, as seen in the screenshot:
Here is the code to launch the app with arguments:
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/open")
process.arguments = [
"/Applications/ArgumentTest/ArgumentTest.app",
"-n",
"--args",
"Open",
"From",
"Playground"
]
try process.run()
-n is used to launch an instance of the app, even if the app is already running. Might not be needed in your case.
Hope this helps!
I want to execute commands below by Process Class:
let uninstallTask = Process()
uninstallTask.launchPath = "/bin/bash"
//MarkerPro is an app in launchpad
uninstallTask.arguments = ["-c","sudo rm -rf "+"/Applications/MarkerPro.app"]
uninstallTask.launch()
But get errors below:
sudo: no tty present and no askpass program specified
So probably need to enter password before i execute delete command.
I found the solution by using "STPrivilegedTask" which is a tool to get authorization by users.
Using FileManager API I think also a good idea
I'm trying to make an installation script in Swift for my program (that is also made in Swift).
What it simply does is create a new folder in Library/ and then clones the GitHub repository there.
The problem is that for doing anything in Library/ you need administrator rights. That's why I want to check if the program is running with administrator rights, so if it doesn't, throw an error telling the user to run it using sudo.
My code:
import Foundation
import Darwin
#discardableResult
func shell(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
func Main() {
if CommandLine.arguments[0] == "sudo" { //I tried by using this, but it seems that sudo doesn't appear in CommandLine.arguments
print("Welcome to the MoonFish installation... This will install MoonFish on /Library/MoonFish/ using 59.37 MB")
shell("mkdir", "/Library/MoonFish")
shell("git", "clone https://github.com/iAlex11/MoonFish.git /Library/MoonFish/") //clones MoonFish repo
} else {
print("\u{001B}[1;31mError:\u{001B}[0;0m Please run this program using sudo. Example: sudo ./install")
exit(0)
}
}
Main()
Apple does not allow to run tasks with elevated rights via Process.
I recommend to use AppleScript.
Launch Script Editor.
Copy and paste this code.
try
display dialog "Welcome to the MoonFish installation... This will install MoonFish on /Library/MoonFish/ using 59.37 MB" buttons {"Cancel", "Install"} default button 2
do shell script "/bin/mkdir /Library/MoonFish" with administrator privileges
do shell script "/usr/bin/git clone https://github.com/iAlex11/MoonFish.git /Library/MoonFish/" with administrator privileges
on error
quit
end try
Press enter.
From Menu File choose Export.
In the dialog check Run Only and select Application from the File Format popup.
Specify a file name and a location and Press Save.
You can even use a custom icon. Replace ./Contents/Resources/applet.icns with another applet.icns file.
Recently, I've been trying to make a (very) simple program with Swift that lets you connect to a server through SSH and execute some files. Unfortunately, I could not figure out how to start an SSH session completely within the Swift app. Here is some code that I have written:
var sshConnectCommand = ["-c", "spawn ssh "+sshUsername+"#"+sshHost+"; expect assword:; send "+sshPassword+"\r"]
func sshIn() {
//starting ssh session
let sshConnect = NSTask()
sshConnect.arguments = [testCmd]
//rerouting output through a pipe
sshConnect.standardOutput = logAppend
//launch!
sshConnect.launch();
}
As you can see, I have used NSTask to try and run the 'expect' command to enter the password and everything. I would like to try and avoid using SSH-keygen as much as possible as this is intended to be used a server that the user does not have any access to.
So, to sum up:
How would you connect to SSH without SSH-keygen while remaining completely within the application code?
edit: I should also add, when trying to compile, I get this error:
[Swift._SwiftDeferredNSArray fileSystemRepresentation]: unrecognized selector sent to instance 0x600000032500. I'm not sure what this means.
I've been using something similar to this to ssh into my Raspberry Pi:
func sshIn() {
let task = CommandLine()
task.launchPath = "/usr/bin/ssh"
task.arguments = ["USERNAME#IPADDRESS", "-t", "sudo systemctl stop mediacenter; /opt/vc/bin/tvservice -o"]
task.launch()
}
-t closes the connection when the commands are finished running, and you can pass in all your commands like so command1; command 2 like where I've got sudo systemctl stop mediacenter; /opt/vc/bin/tvservice -o
As for the keygen thing, I don't think you have much of a choice. You can look into locking it down a bit though. Here's a good place to start looking.