Could not start up a DiskManagement session - swift

I wanted to eject a pendrive using Swift.
First I get the name of the pendrive by inspecting the "Volumes" folder.
Before trying to eject it, I wanted to make sure the code I was using was correct. For this reason I tried to execute a diskutil list command using this script:
let task = Process()
let pipe = Pipe()
task.standardError = pipe
task.standardOutput = pipe
task.executableURL = URL(fileURLWithPath: "/usr/sbin/diskutil")
task.arguments = [""]
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
print(output)
The output I get is "Could not start up a DiskManagement session". But if I change this lines
task.executableURL = URL(fileURLWithPath: "/bin/zsh")
task.arguments = ["-c", "echo hello"]
it prints the right output ("hello").
I get the same error if I try to use
task.executableURL = URL(fileURLWithPath: "/bin/zsh")
task.arguments = ["-c", "diskutil list"]
Any solution?

Related

How to launch terminal and pass it a command using Swift

I want to programmatically open a terminal and paste a command into it like "cd /Users/...".
I can start a terminal using this code, but I don't know how to execute command
guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Terminal") else { return }
let path = "/bin"
let configuration = NSWorkspace.OpenConfiguration()
configuration.arguments = \[path\]
NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil)
It is very important to use the sandbox, the Process command is not suitable.
If all you are trying to achieve is to open a terminal on a specific location all you have to do is to use this code:
let pathToOpen = "/Users/admin/Desktop"
let url = URL(string:"terminal://"+pathToOpen)!
NSWorkspace.shared.open(url)
If you are trying just to run a command in terminal and show the output in your app, here is another useful code snippet:
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.standardInput = nil
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
//Usage:
shell("yourCommandHere")
//please note that you are not allowed to use commands that require sudo

Running shell commands in a swift script

I am looking for a solutuion to run shell commands in a Swift script.
Here is my code:
func shellEnv(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
It works with built-in commands but it cannot deal with commands like "brew", "node" and other commands that were installed manually.
So how can I solve it?
You need to set the PATH environment variable for the task. This has been set in your terminal, which is why you are able to do brew and node and other cool things directly, without specifying their full path.
You can see what this is set to in your terminal by doing:
echo $PATH
and it will print something like (for me it has a bunch more things. This is only an excerpt):
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
If you copy and paste the entire output of echo $PATH, and put it into the environment property of task, then you will be able to use the same commands in your swift script as in your terminal.
task.environment = ["PATH": "<paste the output here>"]
As Alexander said in the comments, another way is to add the l option. Here is a MCVE:
#!/usr/bin/swift
// main.swift
import AppKit
func shellEnv(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-cl", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
print(shellEnv("brew list")) // assuming you have brew
To run, chmod +x main.swift then ./main.swift, and you will see all your homebrew packages listed.

How can I create Daemon in LaunchAgents from macOS APP?

I want to create Daemon (Plist in ~/Library/LaunchAgents/) from my macOS application.
import Foundation
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/bash"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
// Example usage:
shell("ls -la")
I know using this I can create a daemon but at this location
~/Library/LaunchAgents/
I am not able to create due to permission issues. Operation not permitted
But using terminal I am able to Create Plist inside
~/Library/LaunchAgents/

Kill process in swift

I am trying to make a Mac application that will automatically close a code designated application running on the OS. I am trying to use killall (like in Terminal). Whenever I try to run the program, I get, "sysctl: unknown oid 'killall'".
Here's my code:
let task = Process()
task.launchPath = "/usr/sbin/sysctl"
///usr/sbin/sysctl
task.arguments = ["killall","iTunes"]
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
print(output)
Thanks in advance!
my 2 cents:
You succeed in killing "iTunes" only if You Xcode App will run with SandoBox DISABLED
All examples on stack overflow about Process are misleading as they call "ls" or "echo" that are always executed in system folders.
I'd suggest you first read the man page for sysctl -- it's used to get and set kernel state. Does that sound like something you want?
The path to killall is /usr/bin/killall, which you can find from Terminal:
> which killall
/usr/bin/killall
Here's the full Swift code:
let pipe = Pipe()
let task = Process()
task.launchPath = "/usr/bin/killall"
task.arguments = ["iTunes"]
task.standardOutput = pipe
task.standardError = pipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let output = String(data: data, encoding: .utf8) {
print(output)
}

Using NSTask in swift to execute a terminal command

Hi i'm trying to make a simple program with swift to execute this command that adds a white space in Dock:
defaults write com.apple.dock persistent-apps -array-add '{"tile-type"="spacer-tile";}'; killall Dock
this is the code i use:
let task = NSTask()
task.launchPath = "/usr/bin/defaults"
task.arguments = ["write","com.apple.dock","persistent-apps","-array-add","'{\"tile-type\"=\"spacer-tile\";}';","killall Dock"]
let pipe = NSPipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
print(output)
I get no errors but nothing happens. Can someone help me please?
This is the code that worked for me:
let task = NSTask()
task.launchPath = "/usr/bin/defaults"
task.arguments = ["write","com.apple.dock","persistent-apps","-array-add","{\"tile-type\"=\"spacer-tile\";}"]
let pipe = NSPipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
task.waitUntilExit()
let task2 = NSTask()
task2.launchPath = "/usr/bin/killall"
task2.arguments = ["Dock"]
let pipe2 = NSPipe()
task2.standardOutput = pipe2
task2.standardError = pipe2
task2.launch()
task2.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
print(output)
defaults write ... ; killall Dock
are two commands. When you type this line in the Terminal, it is passed
to your shell (usually "bash" on OS X), and the shell then executes
both commands sequentially.
On the other hand ,NSTask executes just a single command and does
nothing of the magic that a shell usually does. In your case
all the arguments, including the final "killall Dock", are passed as
arguments to /usr/bin/defaults.
A possible solution would be to execute two NSTasks sequentially,
one for the defaults command, and one for the killall command.