How to create symlink in /usr/local/bin on macOS 10.15?

Hi,


I want to install a symlink inside /usr/local/bin

I don't use sandbox (hardened runtime only).


I tried:


NSWorkspace().requestAuthorization(to: .createSymbolicLink) { (auth, error) in
    let fm = FileManager(authorization: auth!)
    try fm.createSymbolicLink(at: URL(fileURLWithPath: "/usr/local/bin/foo"), withDestinationURL: URL(fileURLWithPath: appBundlePath + "/Contents/MacOS/Foo"))
}


it doesn't work though, I always get


com.apple.secinitd.fileoperations: xpc_pipe_routine() returned [5: Input/output error]

The file couldn’t be saved.


What are my options to install symlink at /usr/local/bin?

Replies

You will need to be root. There are a number of ways to accomplish that, none of them pleasant. There are many, many existing questions here on the forums with the answers.

I'm browsing the forum with a hope I'll find a mention to `requestAuthorization` API and how to use it. Didn't find any yet. Do you mind to point me to one of many many existing questions with an answer to that maybe?


eg. I see this https://forums.developer.apple.com/thread/127371 that says one strategy is to use `

NSWorkspaceAuthorization` that is exactly what I'm trying to use, yet I'm failing.

NSWorkspaceAuthorization is only for the sandbox. It sounds like you need an installer package. However, the fact that you are even looking at /usr/local in the first place means you are likely way off base to begin with. What are you trying to do, at a high level?


/usr/local is only for open source software ported to the Mac.

> /usr/local is only for open source software ported to the Mac.


that's not true

/usr/local/bin is for local binaries

You said right in the title of your thread "macOS 10.15". On macOS, /Applications is for local binaries. You can always use a package installer or some kind of custom installer to put your binaries in virtually any other location on the system if you want. It is just a question of how much you want to punish both your customers and yourself. Your call.

it's off-topic now, however /Applications is for app bundles, that's different than /usr/local/bin that is part of PATH env. and is a place for CLI binaries for local users.

/usr/local/bin is not a good place for Mac software. To start off with, I don't think it exists on a fresh install. I can't tell for sure because Parallels does use it my VMs. It only matters for people who use Terminal. Because any open-source project ported to the Mac is going to dump files in /usr/local, it isn't a safe place. Some other software could overwrite your binaries. If the user wants to uninstall something, there is a good chance the only way to do that is to wipe out all of /usr/local.


If you have a developer tool and you want to create symlinks there, the best option is a package installer. The installer and associated scripts run as root. That's the easiest way. You can create a privileged helper tool, launch daemon, and mach ports to do it the "official" way. You could use the old deprecated methods that still work but could be removed at any time. I mentioned another option using AppleScriptin this thread from about a year ago. But again, this could be removed at any time.


The ideal solution is to just put any command-line tools in a "Helpers" directory. Anyone who knows their way around the Terminal could add that location to their path, or add symlinks in /usr/local/bin. You could even include a little shell script to do that for them. That would be a whole lot easier and less risky than any other method. This puts the user in control, which is the way Apple wants it.

Thank you.


If you read my sample code from the initial post, you'll see that I'm trying to create a symlink in /usr/local/bin. /usr/local/bin is there by default, it comes from BSD origins of macOS afair and is in PATH by default. that makes it a perfect place for many CLI tools (and is used for that purpose by many)

Yes. I saw that. But to clarify, while /usr/local does exist and /usr/local/bin is in the default path, the path /usr/local/bin does not actually exist on a default configuration. I just checked two different machines to verify. So, if you were to attempt to create those links, the first thing you would have to do is create /usr/local/bin, if it didn't already exist, and create it correctly.


Don't read too much into the "BSD origin myth". It is true that macOS has a BSD compatibility layer, but that's not quite the same thing. macOS is a "melange" of a number of different origin systems. You and I, as developers, may use the Terminal and have things installed in /usr/local/bin, but that is very unusual for Mac users.


I'm not saying you shouldn't make command-line tools. Just don't forget who your market is, or isn't. The macOS platform itself is strictly niche at this point. You are worried about a tiny fraction of a tiny fraction possible users. You can serve that market with a package installer, or with executables that work on the command line for users who know where to find them. Just don't put too much effort into this endeavour. Put your time and effort towards iOS support, which has the most profitable market share and is the future of the Mac.

The

requestAuthorization(to:completionHandler:)
requires a special entitlement (
com.apple.developer.security.privileged-file-operations
). See the
NSWorkspaceAuthorization
docs.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

This is not a sandbox app.

Indeed. I had assumed that the

NSWorkspaceAuthorization
would work from a non-sandboxed app if you had that entitlement. However, looking at the code it’s clear that it does not; for it to work the process must be both sandboxed and have the
com.apple.developer.security.privileged-file-operations
entitlement.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"