EACCES Error on binary included in Electron app

Hi,

I have an Electron app that I build, sign, notarize, and staple using electron-builder. It includes Sound Exchange (SoX), which I was invoking from the homebrew installed version. It builds a dmg and works fine.

However, my users are non-technical, thus cannot be expected to install dev tools, homebrew, and sox from the command line and set paths.

Therefore, I need to include a SoX binary in my app. I have a static SoX binary that works. However, when I try to run it from my electron app, I get Error: spawn / <path>/sox EACCES.

Electron-builder is signing the SoX binary codesign --sign <sign number> --force --timestamp --options runtime --entitlements dist/entitlements/entitlements.mac.plist /<app path>Contents/Resources/bin/sox/sox

The app sign/notarize works fine, the dmg mounts, and the app runs until I try to invoke SoX. Also, I verified the sox binary and entire app are signed and the app staple is valid. I am running the app from /Applications.

Please help me!

Answered by DTS Engineer in 807736022

That error message suggests that you’re trying to run the tool as a child process. If so, there are a couple of things to watch out for here:

Having said that, the specific error you’re getting, EACCES, suggests that you have a BSD privileges issue, not a sandbox issue. I talk about that more in On File System Permissions. Check that, in the built app, the executable has the execute permission bit set and that all directories up the chain are both readable and executable.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

That error message suggests that you’re trying to run the tool as a child process. If so, there are a couple of things to watch out for here:

Having said that, the specific error you’re getting, EACCES, suggests that you have a BSD privileges issue, not a sandbox issue. I talk about that more in On File System Permissions. Check that, in the built app, the executable has the execute permission bit set and that all directories up the chain are both readable and executable.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

The whole directory chain has 755. I have the binary in Contents/Libs/bin/sox/sox (binary). The SoX binary is also code-signed.

It is indeed being spawned in a child process (node) within Electron. As I previously mentioned, this works fine if I use the Homebrew version. const audioRecorder = spawn(soxPath, soxArgs)

As an alternative, might I have a script that installs this sox binary to /opt/bin/sox? I tried to make one with pkgbuild, codesign, & xcrun. It does make SoxInstaller.pkg, but fails when I try to run it and the logs are not written (I am just guessing at the script).

`#!/bin/bash

Set the log file path to a specific directory

LOGFILE="/tmp/sox_install.log"

Redirect output to the log file

exec > >(tee -a $LOGFILE) 2>&1

Start logging

echo "===> Starting SoX install script at $(date)" echo "===> Log file: $LOGFILE"

Ensure the script is running with elevated privileges

if [ "$(id -u)" -ne 0 ]; then echo "ERROR: This script must be run as root or with sudo" exit 1 fi

Define the SoX binary path

SOX_BINARY="/tmp/sox" #SOX_BINARY="$(dirname "$0")/sox" echo "===> SoX binary expected at: $SOX_BINARY"

Check if the SoX binary exists

if [ ! -f "$SOX_BINARY" ]; then echo "ERROR: SoX binary not found at $SOX_BINARY" exit 1 else echo "===> SoX binary found." ls -l "$SOX_BINARY" fi

Define the target installation directory

TARGET_DIR="/opt/sox" echo "===> Target directory for SoX: $TARGET_DIR"

Create target directory if it doesn't exist

if [ ! -d "$TARGET_DIR" ]; then echo "===> Target directory does not exist. Attempting to create it..." sudo mkdir -p "$TARGET_DIR" if [ $? -ne 0 ]; then echo "ERROR: Failed to create target directory: $TARGET_DIR" exit 1 else echo "===> Successfully created $TARGET_DIR" fi else echo "===> Target directory already exists: $TARGET_DIR" fi

Copy the SoX binary to the target directory

echo "===> Copying SoX binary to $TARGET_DIR" sudo cp "$SOX_BINARY" "$TARGET_DIR/sox" if [ $? -ne 0 ]; then echo "ERROR: Failed to copy SoX binary to $TARGET_DIR" exit 1 else echo "===> SoX binary successfully copied to $TARGET_DIR" ls -l "$TARGET_DIR/sox" fi

Set executable permissions for the SoX binary

echo "===> Setting executable permissions for SoX" sudo chmod 755 "$TARGET_DIR/sox" if [ $? -ne 0 ]; then echo "ERROR: Failed to set permissions on $TARGET_DIR/sox" exit 1 else echo "===> Permissions successfully set for SoX binary" fi

Verify SoX installation

echo "===> Verifying SoX installation" if command -v "$TARGET_DIR/sox" &> /dev/null; then echo "===> SoX installation verified. SoX binary located at $TARGET_DIR/sox" else echo "ERROR: SoX installation verification failed." exit 1 fi

echo "===> SoX installation complete at $(date)" exit 0`

As an alternative, might I have a script that installs this sox binary to /opt/bin/sox?

Yes, as long as your app isn’t sandboxed. If it is sandboxed, using a location like this is trickier.

Oh, and binaries like this usually go into /usr/local on macOS. See the not-very-well-known hier man page. You can put it into /usr/local/bin directly, or create your own product directory and put it into /usr/local/my-product-name/bin.

But, yeah, the big drawback with this approach is that you can run into conflicts, where the user has the wrong version of the tool installed. That’s one of the advantages of embedding helper tools into your app.


Coming back to the big picture, if you were using Xcode then I’d be able offer more help. As I mentioned before, we have a specific doc that covers the sandboxed case, Embedding a command-line tool in a sandboxed app, and your non-sandboxed case is significantly easier.

However, you’re using third-party tools and I don’t maintain expertise in those. Honestly, I think you’d be better off seeking help via the support channel for those tools.

If you want to continue here, I suggest you prototype this using Xcode:

  • Follow the instructions in the Embed an externally built tool section of Embedding a command-line tool in a sandboxed app.

  • Except skip the entitlements, because those are only needed if your main app is sandboxed.

  • Launch your tool using this snippet of Swift code:

func run() throws {
    let url = Bundle.main.url(forAuxiliaryExecutable: "MyToolName")!
    let p = Process()
    p.executableURL = url
    p.arguments = [ /* … whatever you want … */ ]
    p.terminationHandler = { p in
        print(p.terminationReason)
        print(p.terminationStatus)
    }
    try p.run()
}

Once you have this working, you can compare how it’s packaged to the way that your app is packaged by your third-party tools.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

EACCES Error on binary included in Electron app
 
 
Q