I facing a problem when running my code on cocoa app to run some command line scripts
This function run smoothly when using Command line tool but when using full cocoa app with some On Off UI it not working at all
My script should turn on/off the http & https proxy
Here is my function:
private func runTask(_ cmd: String) {
// Create a Task instance
let task = Process()
// Set the task parameters
task.launchPath = "/bin/sh"
task.arguments = ["-c", String(format:"%#", cmd)]
// Create a Pipe and make the task
// put all the output there
let pipe = Pipe()
task.standardOutput = pipe
// Launch the task
task.launch()
// Get the data
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else { return }
print(output)
}
And here is my full ViewController class:
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func onButtonTapped(_ sender: NSButton) {
print("onButtonTapped")
let selected: Switch = .on
let listOfNetworkCommands: String = [
#"networksetup -setwebproxystate "Wi-fi" \#(selected)"#, // switch http proxy
#"networksetup -setsecurewebproxystate "Wi-fi" \#(selected)"#, // switch https proxy
#"networksetup -setpassiveftp "Wi-fi" \#(selected)"# // switch passive ftp
].joined(separator: " && ")
runTask(listOfNetworkCommands)
}
#IBAction func offButtonTapped(_ sender: NSButton) {
print("onButtonTapped")
let selected: Switch = .off
let listOfNetworkCommands: String = [
#"networksetup -setwebproxystate "Wi-fi" \#(selected)"#, // switch http proxy
#"networksetup -setsecurewebproxystate "Wi-fi" \#(selected)"#, // switch https proxy
#"networksetup -setpassiveftp "Wi-fi" \#(selected)"# // switch passive ftp
].joined(separator: " && ")
runTask(listOfNetworkCommands)
}
enum Switch: String {
case on, off
}
private func runTask(_ cmd: String) {
// Create a Task instance
let task = Process()
// Set the task parameters
task.launchPath = "/bin/sh"
task.arguments = ["-c", String(format:"%#", cmd)]
// Create a Pipe and make the task
// put all the output there
let pipe = Pipe()
task.standardOutput = pipe
// Launch the task
task.launch()
// Get the data
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else { return }
print(output)
}
}
Any idea why my function not triggered in the cocoa app?
Simple answer is found by disabling App Sandbox in your Cocoa Application (found under your Project app target > Capabilities tab > App Sandbox switch). You'll find that you're being blocked by a sandbox exception. Disabling sandboxing should fix your issue.
You can also see this in Console.app if you filter for your app name or the sandboxd process. You'll likely have an entry like this when sandboxing is enabled:
error 00:21:57.502273 +0000 sandboxd Sandbox: sh(17363) deny(1) file-read-data /dev/ttys003
Related
I want to execute a Terminal command in my Application and redirect the Terminal output of this command to a TextView (content_scroller). If I run the Application with Apple+R from within Xcode the Progress of this Terminal command is refreshed as it should. But ... If I started the Application the normal way only the first line of terminal output is shown but there is no refresh/new lines anymore. But why? Is there a way to loop the request of the actual output? Here is mit Swift 5 Code:
func syncShellExec(path: String, args: [String] = []) {
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = [path] + args
let outputPipe = Pipe()
let filelHandler = outputPipe.fileHandleForReading
process.standardOutput = outputPipe
process.launch()
filelHandler.readabilityHandler = { pipe in
let data = pipe.availableData
if let line = String(data: data, encoding: .utf8) {
DispatchQueue.main.sync {
self.content_scroller.string += line
self.content_scroller.scrollToEndOfDocument(nil)
}
}
process.waitUntilExit()
filelHandler.readabilityHandler = nil
}
Should be able to direct output straight to text view if I understand your question correctly. Something like the following outputs an error (I didn't test it.)
import Cocoa
func syncShellExec(path: String, args: [String] = []) {
var status : Int32
var dataRead : Data
var stringRead :String?
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = [path] + args
let outputPipe = Pipe()
let txtView = NSTextView()
let fileHandler = outputPipe.fileHandleForReading
process.standardOutput = outputPipe
process.launch()
process.waitUntilExit()
status = process.terminationStatus
dataRead = fileHandler.readDataToEndOfFile()
stringRead = String.init(data: dataRead, encoding: String.Encoding.utf8)
if (status != 0) {
txtView.string.append("Terminated with error.\n")
txtView.string.append(stringRead!)
}
}
I'm trying to get a button in Xcode to run a shell script with clicked.
This works
#IBAction func test(_ sender: NSButton) {
let path = "/usr/bin/say"
let arguments = ["hello world"]
sender.isEnabled = false
let task = Process.launchedProcess(launchPath: path, arguments: arguments)
task.waitUntilExit()
sender.isEnabled = true
}
But when I try this it does not work to run a script from the Desktop
#IBAction func test(_ sender: NSButton) {
let path = "/bin/bash"
let arguments = ["~/Desktop/test.sh"]
sender.isEnabled = false
let task = Process.launchedProcess(launchPath: path, arguments: arguments)
task.waitUntilExit()
sender.isEnabled = true
}
I get this error output in Xcode
/bin/bash: ~/Desktop/test.sh: No such file or directory
If anyone can help me with some help or example that would great. Thank you.
Turn off Xcode sandbox mode, it will fix the issue
func shell(_ args: String) -> String {
var outstr = ""
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", args]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
outstr = output as String
}
task.waitUntilExit()
return outstr
}
This Function returns output of Bash Script you're trying to run
let cmd = "for i in $(ifconfig -lu); do if ifconfig $i | grep -q \"status: active\" ; then echo $i; fi; done"
Above Code Demonstrate how to use it.
I have built a command line tool, at some point, I need to execute a curl command. I'm creating the script that should be executed, but I don't know how.
I'm able to create the script and printing it out, but I'm not being able to execute it.
It looks something like: curl https://api.github.com/zen
Please ask me anything if it's not clear. I appreciate your help.
#!/usr/bin/env swift
import Foundation
func run(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
run("curl", "https://api.github.com/zen")
You can run a Terminal command from Swift using NSTask (now called Process in Swift 3): If you need output, add let output = handle.readDataToEndOfFile() at the end. Here's the whole thing wrapped in a function (the launchPath would be /usr/bin/curl):
func runTask(launchPath: String, flags: [String]) -> String {
let task = Process()
let pipe = Pipe()
task.launchPath = launchPath
task.arguments = flags
task.standardOutput = pipe
let handle = pipe.fileHandleForReading
task.launch()
return String(data: handle.readDataToEndOfFile(), encoding: .utf8) ?? ""
}
In your case though, you might want to have a look at URLSession and URLRequest (superseding NSURLRequest). To create a request to your URL and credentials, you would simply do:
var request = URLRequest(url:URL(string: "https://api.github.com/zen")!)
request.setValue("application/vnd.github.v3.raw", forHTTPHeaderField: "Accept")
request.setValue("token USERTOKEN", forHTTPHeaderField: "Authorization")
let session = URLSession(configuration: .default)
session.dataTask(with: request, completionHandler: {(data, response, error) in
guard let data = data, error == nil else {
print("Error: \(error.debugDescription)")
return
}
guard let output = String(data: data, encoding: .utf8) as String? else {
print("Unable to format output data")
return
}
print(output)
}).resume()
Our current application needs to reboot the system and exit the application cleanly on a button click however once either of the code runs i.e. code to restart or code to exit app , the other code will not run.Our application currently reloads after the reboot since it is not close properly before the system reboot.
Button code which needs to restart and exit app:
#IBAction func exit2(sender: AnyObject) {
let task = NSTask()
let pipe = NSPipe()
task.standardOutput = pipe
//Code to reboot the system
task.launchPath = "/bin/bash/"
task.arguments = ["-c", "osascript -e 'tell app \"System Events\" to restart'"]
let file:NSFileHandle = pipe.fileHandleForReading
task.launch()
task.waitUntilExit()
let data = file.readDataToEndOfFile()
datastring1 = NSString(data: data, encoding: NSUTF8StringEncoding)!
//Code to close the application
NSApplication.sharedApplication().terminate(self)
}
#IBAction func restartAppButton(_ sender: Any) {
if let path = Bundle.main.resourceURL?.deletingLastPathComponent().deletingLastPathComponent().absoluteString {
NSLog("restart \(path)")
_ = Process.launchedProcess(launchPath: "/usr/bin/open", arguments: [path])
NSApp.terminate(self)
}
}
I currently have:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
var task = NSTask()
task.launchPath = "/usr/bin/sudo"
task.arguments = ["tcpdump"]
var pipe = NSPipe()
task.standardOutput = pipe
var handle = pipe.fileHandleForReading
handle.waitForDataInBackgroundAndNotify()
var observer = NSNotificationCenter.defaultCenter().addObserverForName(NSFileHandleDataAvailableNotification, object: handle, queue: nil, usingBlock: { (note: NSNotification!) -> Void in
var dataRead = handle.availableData
var str = NSString(data: dataRead, encoding: NSUTF8StringEncoding)
println("debug \(str)")
})
task.launch()
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}
I have been looking for hours on this, and I can't find anything. Also, weirdly, sudo runs without requiring a password, but no packets are intercepted by tcpdump, hence I also need to know how to input a password. Thank you!