2

I am writing a swift based macOS app in Xcode 9 to be used on my computer (not distributed). I have EXIFtool installed (independent of the app) in /usr/local/bin and can use it successfully from the Terminal app. I am trying to access EXIFtool from my app.

My app has a button that when clicked should run the EXIFtool command by executing this script.

@IBAction func arrowClicked(_ sender: Any) {
    arrow.isEnabled = false
    let task = Process.launchedProcess(launchPath: "/usr/local/bin/exiftool", arguments: [rawURL])
    task.waitUntilExit()
    arrow.isEnabled = true
}

The script fails with a "launch path not accessible" error. It doesn't matter what I enter as the arguments (in the above snippet, rawURL is a string that contains the path to a user identified image file.

The responses I have found for similar questions here focus on the format of the path (e.g., must be the full path, begin with /, etc). My launch path comes from what Terminal gives a response to "which exiftool", so I thought it was correct.


UPDATE: I followed the link Matt provided and rewrote the code to utilize a shell script. I made the script executable and successfully ran it through Terminal and TextWrangler. But accessing it within Xcode resulted in an "operation not permitted" message.

Brad
  • 31
  • 5

2 Answers2

1

Turning off the App Sandbox resolves both the original "launch path not accessible" message and the revised attempt's "operation not permitted" message.

Brad
  • 31
  • 5
1

There are unfortunately numerous reasons why one binary may not run inside a sandboxed environment, whilst another binary may run just fine.

In your case you were able to disable sandboxing, so that was an easy (and sensible) fix, but for anyone without that luxury, below is some information that documents some relevant factors.

There is a question here which asks why /sbin/ping runs fine, yet /usr/sbin/traceroute does not.

From one of the answers there:

ping vs. traceroute - the former is a non-priviledged program, the latter is priviledged and runs as root

You can see the difference in their permissions:

$ ls /sbin/ping
-r-xr-xr-x  1 root  wheel    41K 30 May 11:36 /sbin/ping

$ ls /usr/sbin/traceroute
-r-sr-xr-x  1 root  wheel    37K 30 May 11:36 /usr/sbin/traceroute

The s on the trace route means it will be executed as root, which naturally is not going to be allowed inside a sandboxed environment. The following may be helpful, from https://coderanch.com/t/110770/os/permissions-meaning#558594:

"s", for files, means "setuid exec." If a file has s permission, then it's executable, and furthermore, the user id and/or group id of the process is set to the user or group id of the owner of the file, depending on whether it's the user or group "s" that's set. This is a way to give limited root powers to a user -- a program that runs as root when an ordinary user executes it.

Somewhat beyond my comprehension, but potentially also relevant is the following Apple Technical Q&A QA1773 called Common app sandboxing issues which discusses whether a binary is a Mach-O executable:

You can check if a binary is a Mach-O executable using the file command. If any slice of the binary identifies itself as Mach-O executable or Mach-O 64-bit executable, the binary must be sandboxed.

For ping that would look like:

$ file /sbin/ping
/sbin/ping: Mach-O 64-bit executable x86_64

My take is that if the binary is a Mach-O executable and you want to run it from a sandboxed app, you are going to have to either:

  • compile the binary yourself from source, giving it appropriate entitlements, or
  • import the code into your app and compile it directly into your app
jeff-h
  • 2,184
  • 1
  • 22
  • 33