Why does my Swift code work in playground but not in the real cocoa app? - swift

I'm currently trying to automate things in a macOS status bar application.
Now I had tried to make the Kerberos Login in a Process (previous called NSTask). In my playground, the code creates successfully the token. But when I move the code to the real app, it failed. I get this error message: "kinit: resolving credentials cache: malloc: out of memory"
Here is my code:
import Cocoa
// user credentials
let username = "user#example.com"
let password = "password"
// previous called NSTask
let process = Process()
// set process parameters
process.launchPath = "/bin/sh"
process.arguments = ["-c", "/bin/echo \(password) | /usr/bin/kinit --password-file=STDIN \(username)"]
// create a pipe to lauch process
let pipe = Pipe()
process.standardOutput = pipe
// launch process
process.launch()
// get outcome
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
print(output!)
I think that it is a problem with the Credential cache. When I enter the command "klist -A", I get the following error: "klist: krb5_cc_cache_get_first: Failed to open kcm init".
Does anybody know what can I do to get this code running?

Related

Executing python from swift via bash: "can't open file...[Errno 1] Operation not permitted"

I am learning how to make apps for mac, and I am starting with an app to manage my many discord bots. Essentially, the goal is to have many switches to turn bots on and off, which requires executing the python files for the bots, written using discord.py. I read about a PythonKit module for swift, but when I tried to run a discord bot from the files using that, the build continuously failed, so I decided to use the bash shell to excecute the python. Here is my swift code for using bash shell commands:
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.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}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.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
I then called shell("/usr/local/bin/python3.9 path/to/file.py"), and made it print the output to the console. This was the output:
/usr/local/bin/python3.9: can't open file 'path/to/file.py': [Errno 1] Operation not permitted. I am running this from both AppCode and Xcode, and made sure to give both of those apps full disk access, as well as giving terminal full disk access to be sure. In addition, I tried running /usr/local/bin/python3.9 path/to/file.pyin my terminal, and it works fine. What is happening here? Why can swift not open this file in the bash shell while I can? What do I do to fix it? Let me know if you need more info(if you think PythonKit is the answer, I'll send the build error info from that to debug that process)
Thanks!
Yonatan Vainer was right, turns out sandboxing was on in the entitlements file, so I had to set it to off, which fixed it.

Process.run Returns 'The file “<command>” doesn’t exist.'

I'm trying to write a small app to start/stop and display data from a command line "app" someone else wrote. The command executable is installed in '/usr/local/bin'. It outputs status text data to standardOutput while running. I can execute this "command" from the Terminal.app without issue. From swiftUI code I can successfully execute "built-in" commands like ls. However, when (in swiftUI code) I attempt to execute Process.run WITH the new command it throws the exception 'The file “” doesn’t exist.'
Anyone have any ideas? Thanks in advance!
Here's a code snip:
// NOTE: "installedCommand" is just a placeholder for the actual command.
let task = Process()
let connection = Pipe()
let exeUrl = URL(fileURLWithPath: "/usr/local/bin/installedCommand")
//let exeUrl = URL(fileURLWithPath: "/bin/ls") <--works fine
task.executableURL = exeUrl
task.standardOutput = connection
do
{
try task.run()
}
catch
{
print("Error: \(error.localizedDescription)")
return
}

Redirect Process stdout to Apple System Log Facility in Swift

I'm building a Swift app for macOS that launch a sub-process. This subprocess logs useful information to stdout, and I see it in the Xcode console.
What I would want to achieve now is to redirect the sub-process stdout to the Apple Log Facility so that we can access the data when the app is deployed.
Basically, this:
let task = Process()
task.launchPath = "subprocess"
task.standardOutput = // AppleSystemLogPipe
task.launch()
But I don't know how to refer to the Apple System Log Pipe. Any clues for this?
Best regards!
I finally managed to do this. Basically, we define a Pipe to catch the process output and, in an handler, call the OS function os_log with the data read:
let pipe = Pipe()
let outHandle = pipe.fileHandleForReading
outHandle.readabilityHandler = { pipe in
if let line = String(data: pipe.availableData, encoding: .utf8) {
// Define the placeholder as public, otherwise the Console obfuscate it
os_log("%{public}#", line)
}
}
let task = Process()
task.launchPath = "subprocess"
task.standardOutput = pipe // Redirect stdout to our pipe
task.launch()
In development, it is displayed in the Xcode console ; and when deployed it is redirected to the Apple Log System.

Finding if an SSH connection succeeded

I am trying to make my program connect to another computer using SSH. It would be generic (the user would provide the hostname, IP, and password), so keys can't be used. This is the code I have so far:
func terminalSSH(host:String, password:String, IP:String) {
let pipe = Pipe()
let args = ["-p", password, "ssh", "\(host)#\(IP)"]
let task = Process()
task.launchPath = "/usr/local/bin/sshpass"
task.arguments = args
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
print(output)
}
My issue is that I can't see if the connection was successful using sshpass. I want the user to be notified the moment the connection succeeded or failed. Also, it seems like sshpass is the only command I can use to SSH because Terminal forces the user to input the password. I know this code works to establish a connection because the system.log on my test target computer displays it. Thanks.
Firstly, you should not be using sshpass and a plain text password, but private keys.
To get the tasks exit code, you can use the following:
task.waitUntilExit()
let status = task.terminationStatus
Do know however, that waitUntilExit will block the return of this method until the session has ended.
Whether the session ends in 10 minutes, or immediately due to an error, you can evaluate the terminationStatus to see how the program exited.
If the return code is 0, everything exited nicely; if there is a non-zero value, you can take different actions based on the reason for failure.
task.waitUntilExit()
let status = task.terminationStatus
if status == 0 {
print("Graceful exit.")
} else {
print("Failed with code " + status)
}
Detecting and responding to a successful connection will be more difficult here, as the waitUntilExit call may block for as long as the ssh session is open.
You also have the option of using a loop, and checking task.isRunning.

Run pdflatex command in Swift with App Sandbox turned ON

I want to compile a latex file in swift, I am able to compile the file if the application has the App Sandbox OFF.
If I turn App Sandbox ON and try to compile I get the error: "launch path not accessible"
How can I run the pdflatex command to compile the latex file with the App Sandbox turned ON?
Below is the method the I use and it works with App Sandbox turned OFF.
let latexCmd = "/Users/user/Desktop/test/article.tex"
let logText:String = self.runTerminal(command: latexCmd)
func runTerminal(command: String)->String{
let pipe:Pipe = Pipe()
let task:Process = Process()
task.launchPath = "/Library/TeX/texbin/pdflatex"
task.arguments = ["-file-line-error", "-interaction=nonstopmode", "-synctex=1", command]
task.standardOutput = pipe
task.currentDirectoryPath = "/Users/user/Desktop/test"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
return output!
}