Why does E210002 error occur only when launched svnserve via launchctl?
When I start svnserve with
$ sudo /usr/local/bin/svnserve -d -r /Volumes/RAID1disk/svn
and run
$ svn commit -m "test1",
svn commit succeeds, but when I start svnserve with
$ sudo launchctl load -w /Library/LaunchDaemons/com.toshiyuki.svnserve.plist
and run
$ svn commit -m "test2",
svn commit fails and displays the following error:
Committing transaction...
svn: E210002: Commit failed (details follow):
svn: E210002: Network connection closed unexpectedly
After the E210002 error, I ran
$ ps aux | grep svnserve
and got the following result.
toshiyuki 67686 0.0 0.0 34252296 700 s000 S+ 10:13AM 0:00.00 grep svnserve
root 35267 0.0 0.0 34302936 592 ?? Ss 10:01AM 0:00.00 /usr/local/bin/svnserve -d -r /Volumes/RAID1disk/svn
From this, I believe that svnserve is launched as the root user from launchctl.
Also, when I ran
$ls -l /volumes/raid1disk/svn
the following result was obtained.
-rw-rw-r-- 1 root wheel 246 7 23 22:31 README.txt
drwxrwxr-x 6 root wheel 192 7 24 06:31 conf
drwxrwxr-x 17 root wheel 544 7 24 10:01 db
-r--rw-r-- 1 root wheel 2 7 23 22:31 format
drwxrwxr-x 11 root wheel 352 7 23 22:31 hooks
drwxrwxr-x 4 root wheel 128 7 23 22:31 locks
so, svnserve has write access to the repository.
If I start svnserve with
$ sudo /usr/local/bin/svnserve -d -r /Volumes/RAID1disk/svn
instead of
$ sudo launchctl load -w
/Library/LaunchDaemons/com.toshiyuki.svnserve.plist
both svn commit and svn chekout always succeed,
so I think there is no problem with the svnserve configuration file
(/etc/svnserve.conf or the file in /etc/svnserve.conf.d).
I think the plist of launchctl is also correct.
because If I start svnserve with
$ sudo launchctl load -w /Library/LaunchDaemons/com.toshiyuki.svnserve.plist
only svn chekout always succeeds (commit fails, though).
The contents of the plist of launchctl file are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.toshiyuki.svnserve</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/svnserve</string>
<string>-d</string>
<string>-r</string>
<string>/Volumes/RAID1disk/svn</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>/var/log/svnserve.log</string>
<key>StandardOutPath</key>
<string>/var/log/svnserve.log</string>
<key>UserName</key>
<string>root</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin</string>
</dict>
</dict>
</plist>
Also, the execution result of
$ls -l /library/LaunchDaemons/com.toshiyuki.svnserve.plist
is as follows.
-rw-r--r--@ 1 root wheel 929 7 24 10:29 /library/LaunchDaemons/com.toshiyuki.svnserve.plist
But when I start svnserve with
$ sudo launchctl load -w /Library/LaunchDaemons/com.toshiyuki.svnserve.plist
"svn commit" always fails.
Why is this?
When I check the /var/log/svn/svnserve.log file,
svnserve: E000048:Address already in use
errors occured periodically.
Service Management
RSS for tagThe Service Management framework provides facilities to load and unload launched services and read and modify launched dictionaries from within an application.
Posts under Service Management tag
72 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hello,
Currently my macOS application registers itself as a login item in the AppDelegate applicationDidFinishLaunching method (see code below)
However, I'm running into a problem that if the user is auto upgraded (internal 3rd party implementation) that the .pkg postinstall script runs, the last step which is launching the GUI application. Because of this, if a user unselects our app as a LoginItem, when it is relaunched, it will add itself back. I have checked the SMAppService statuses (.enabled, .notRegistered, .notFound) and discovered that when a user disables the app as a login item, the status is returned as .notFound. I am trying to find a way to detect if the user previously removed our app from login items and not register the app as a login item back, but for the first time the user opens the app the app is registered as a login item. Would checking if the status is .notRegistered work in this case for a first time install? What should i do differently?
func applicationDidFinishLaunching(_ aNotification: Notification) {
...
guard !Runtime.isDebug else {
self.logger.debug("Detected Xcode host; Skipping installation of helper components.")
return
}
self.logger.info("Setting UI login item")
if mainApp.status != .enabled { //old code, incorrect. What should go here?
do {
try mainApp.register()
} catch {
logger.error("Failed to initialize UI login item: \(error.localizedDescription)")
}
}
}
I use launch constraints in a project. If I archive the project and save a copy of the app locally, everything works as expected but if I choose "Direct Distribution" and submit the app to Apple for notarization, the notarized app does not contain any launch constraints. What are I am doing wrong? Thanks.
I have this application that is divided in 3 parts.
A server that handles all the networking code
A agent that handles all System related code
A manager (NSApplication) to interact with the other two processes.
Goals
All three process should be kept alive if they crash
All three processes must not restart if the user quits them though the NSApplication
They need to run during the login window.
My current set up using LaunchD is as follows.
My Server process plist (relevant part) saved in System/LaunchDaemons
key>MachServices</key>
<dict>
<key>com.myCompany.Agent.xpc</key>
<true/>
<key>com.myCompany.Manager.xpc</key>
<true/>
</dict>
<key>ProgramArguments</key>
<array>
<string>PathToExecutable</string>
<string>-service</string>
</array>
<key>RunAtLoad</key>
<false/>
My agent plist (saved in System/LaunchAgent)
<key>QueueDirectories</key>
<array>
<string>PathToDirectory</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>PathToExecutable</string>
<string>service</string>
</array>
my Manager app plist (saved in System/LaunchAgent)
<key>LimitLoadToSessionType</key>
<string>Aqua</string>
<key>RunAtLoad</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>PathToExecutable</string>
</array>
<key>MachServices</key>
<dict>
<key>com.myCompany.Agent.manager.xpc</key>
<true/>
</dict>
Currently I have another app that saves a file to the path of the QueueDirectories which triggers the launch of my Agent which then triggers the Server and Manager by starting a XPC Connection. QueueDirectories keeps the Agent alive (and hence all other processes) til file is removed and processes are quited through the manager.
XPC Connections
Server listens for a connection from agent and manager
Manager listens for a connection from agent and starts a connection with server
Agent starts a connection with Manager and Server
Agent and Manager are owned by the user and server by root
The problems
When I start Agent by saving a file in the QueueDirectories path and connect to the Server over xpc I end up with two Agents, one owned by the user (the one I expect) and one owned by root.
But if I manually start the Agent I do not have that problem.
As I mentioned before, the server listens for a connection from the Agent.
How do I stop getting two instances? or what is a better way to approach this?
We are developing a lightweight VPN client inside a daemon process that will run even when no user session is active on machine. The lightweight VPN runs in machine context and does not require user session. We would like to display some basic diagnosis information about our lightweight client on macOS login window before user is logged into their machine (in case users need that).
So, is it possible to display a UI window on login screen with some basic info that user can interact with. If yes, where can I get started?
Please note, this is not an authorization plugin. We are just wanting to display info about our process that runs a lightweight VPN client on macOS login screen.
I created a macOS app that can run in foreground (NSApplicationActivationPolicyRegular) and in background (NSApplicationActivationPolicyAccessory) and can be launched by an Helper (Login Item) and run in background. I use XCode 15.3 (15E204a) on macOS 14.2.1 on a MacBook Pro 16", 2019, 2,3 GHz Intel Core i9 8 core.
I archive my app, I notarize it for direct distribution then I save it to the /Applications folder. Then I delete my Debug app from …/Xcode/DerivedData/MyApp-dal…xu/Build/Products/Debug so I am sure I have one only copy of my app on the disk.
I firstly launch my app as NSApplicationActivationPolicyRegular then I select my menu item "Set the app as Login Item". So I call
SMAppService *service = [SMAppService loginItemServiceWithIdentifier:@"com.mydomain.Helper"];
I get a
service.status == SMAppServiceStatusNotFound
So I am stuck. I notice that my app doesn't show up in the System Preferences/Login Items/ "Allow Background" list. So on Terminal I run the command
sudo sfltool resetbtm
Now the "Allow Background" apps list is empty. Then I reboot. All the apps listed in "Allow Background" list reappear and are all turned "on". I turn them back off. My app is on the list too (turned "on" too). I leave it on. Now I can launch my app as NSApplicationActivationPolicyRegular, select again my menu item "Set the app as Login Item" and call
SMAppService *service = [SMAppService loginItemServiceWithIdentifier:@"com.domain.Helper"];
Now I get a
service.status == SMAppServiceStatusEnabled
so I install the NSStatusItem and switch to background (NSApplicationActivationPolicyAccessory). I can logout and login and my app gets properly launched by the Helper and run in background. Everything works well.
I guess I miss something,
because I think this is not the way to distribute my app to the customers. I can't ask my customers to run the Terminal command sudo sfltool resetbtm, reboot then turn back "off" all the unwanted apps from the "Allow Background" list then turn my app "on".
Furthermore, if I delete my app, it disappears from the "Allow Background" apps list, then if I copy it back to the /Applications folder, it doesn't show up yet in the "Allow Background" apps list. I have to invoke again the sudo sfltool resetbtm Terminal command, reboot… to see it in that list. I tried on 3 machines with macOS 14.2.1. Same result.
I have read several articles and posts about NSApplicationActivationPolicyAccessory, SMAppService, but I didn't find my case. The Info.plist file of the Helper properly contains
<key>LSBackgroundOnly</key><true/>
<key>LSUIElement</key><true/>
• What do I miss?
• Why my app doesn't show up in the "Allow Background" apps list when I simply copy it within the /Applications folder?
Hello,
I was wondering, is it possible to run SMAppService.daemon... as root?
let service = SMAppService.daemon(plistName: "myApp.agent.plist")
Also, is it possible to launch the SMAppService.daemon without the XPC connection? The daemon currently supports grpc.
I was thinking about running it via Process?
Hello,
we are currently working on a plan to migrate our app suite from Developer ID binaries inside a simple pkg installer to macOS app store distribution.
The reason we are using an installer is that there are multiple binaries inside that communicate via XPC and we need to install the respective launchd plist in /Library/LaunchDaemons and /Library/LaunchAgents:
1 root daemon
1 agent that has minimal UI and lives in the system menu bar
1 embedded command line utility in user agent
1 embedded FileProvider extension in user agent
1 embedded Action Extension in user agent
1 agent that only does OAuth stuff
Looking through Updating helper executables from earlier versions of macOS I can install the root daemon with SMAppService.daemon(plistName:) and the OAuth helper with SMAppService.agent(plistName:). For the main application I only found SMAppService.mainApp which does not accept a property list configuration. Therefore, I have no place to put my MachServices array and so the File Provider extension, the Action Extension, and the embedded command line utility have no way to talk to the user agent.
Currently, XPC is used in between these processes:
user agent -> root daemon
command line utility -> user agent
action extension -> user agent
file provider extension -> user agent
user agent -> file provider extension: that already works through NSFileProviderServicing
I know app-to-app communication only works through launchd for security reasons, but these applications are all part of the same app group (except the root daemon obviously).
My question is what is the proper way of starting the user agent so XPC from other binaries just work ™️?
Any input is much appreciated!
Topic:
App & System Services
SubTopic:
Core OS
Tags:
Inter-process communication
macOS
Service Management
XPC
Hi all!
So SMJobBless is deprecated, and I want to my app to do some privileged things, e.g. move file to root user folder with permission dialog. Simple, right?
But how can I do that simple thing? Found example with agent, but it does not have root permission to write a file in root's folder.
Any help?
Service Management framework supports installing and uninstalling services, including Service Management login items, launchd agents, and launchd daemons.
General:
Forums subtopic: App & System Services > Processes & Concurrency
Forums tag: Service Management
Service Management framework documentation
Daemons and Services Programming Guide archived documentation
Technote 2083 Daemons and Agents — It hasn’t been updated in… well… decades, but it’s still remarkably relevant.
EvenBetterAuthorizationSample sample code — This has been obviated by SMAppService.
SMJobBless sample code — This has been obviated by SMAppService.
Sandboxing with NSXPCConnection sample code
WWDC 2022 Session 10096 What’s new in privacy introduces the new SMAppService facility, starting at 07˸07
BSD Privilege Escalation on macOS forums post
Background items showing up with the wrong name forums post
Related forums tags include:
XPC, Apple’s preferred inter-process communication (IPC) mechanism
Inter-process communication, for other IPC mechanisms
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
This week I’m handling a DTS incident from a developer who wants to escalate privileges in their app. This is a tricky problem. Over the years I’ve explained aspects of this both here on DevForums and in numerous DTS incidents. Rather than do that again, I figured I’d collect my thoughts into one place and share them here.
If you have questions or comments, please start a new thread with an appropriate tag (Service Management or XPC are the most likely candidates here) in the App & System Services > Core OS topic area.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
BSD Privilege Escalation on macOS
macOS has multiple privilege models. Some of these were inherited from its ancestor platforms. For example, Mach messages has a capability-based privilege model. Others were introduced by Apple to address specific user scenarios. For example, macOS 10.14 and later have mandatory access control (MAC), as discussed in On File System Permissions.
One of the most important privilege models is the one inherited from BSD. This is the classic users and groups model. Many subsystems within macOS, especially those with a BSD heritage, use this model. For example, a packet tracing tool must open a BPF device, /dev/bpf*, and that requires root privileges. Specifically, the process that calls open must have an effective user ID of 0, that is, the root user. That process is said to be running as root, and escalating BSD privileges is the act of getting code to run as root.
IMPORTANT Escalating privileges does not bypass all privilege restrictions. For example, MAC applies to all processes, including those running as root. Indeed, running as root can make things harder because TCC will not display UI when a launchd daemon trips over a MAC restriction.
Escalating privileges on macOS is not straightforward. There are many different ways to do this, each with its own pros and cons. The best approach depends on your specific circumstances.
Note If you find operations where a root privilege restriction doesn’t make sense, feel free to file a bug requesting that it be lifted. This is not without precedent. For example, in macOS 10.2 (yes, back in 2002!) we made it possible to implement ICMP (ping) without root privileges. And in macOS 10.14 we removed the restriction on binding to low-number ports (r. 17427890). Nice!
Decide on One-Shot vs Ongoing Privileges
To start, decide whether you want one-shot or ongoing privileges. For one-shot privileges, the user authorises the operation, you perform it, and that’s that. For example, if you’re creating an un-installer for your product, one-shot privileges make sense because, once it’s done, your code is no longer present on the user’s system.
In contrast, for ongoing privileges the user authorises the installation of a launchd daemon. This code always runs as root and thus can perform privileged operations at any time.
Folks often ask for one-shot privileges but really need ongoing privileges. A classic example of this is a custom installer. In many cases installation isn’t a one-shot operation. Rather, the installer includes a software update mechanism that needs ongoing privileges. If that’s the case, there’s no point dealing with one-shot privileges at all. Just get ongoing privileges and treat your initial operation as a special case within that.
Keep in mind that you can convert one-shot privileges to ongoing privileges by installing a launchd daemon.
Just Because You Can, Doesn’t Mean You Should
Ongoing privileges represent an obvious security risk. Your daemon can perform an operation, but how does it know whether it should perform that operation?
There are two common ways to authorise operations:
Authorise the user
Authorise the client
To authorise the user, use Authorization Services. For a specific example of this, look at the EvenBetterAuthorizationSample sample code.
Note This sample hasn’t been updated in a while (sorry!) and it’s ironic that one of the things it demonstrates, opening a low-number port, no longer requires root privileges. However, the core concepts demonstrated by the sample are still valid.
The packet trace example from above is a situation where authorising the user with Authorization Services makes perfect sense. By default you might want your privileged helper tool to allow any user to run a packet trace. However, your code might be running on a Mac in a managed environment, where the site admin wants to restrict this to just admin users, or just a specific group of users. A custom authorisation right gives the site admin the flexibility to configure authorisation exactly as they want.
Authorising the client is a relatively new idea. It assumes that some process is using XPC to request that the daemon perform a privileged operation. In that case, the daemon can use XPC facilities to ensure that only certain processes can make such a request.
Doing this securely is a challenge. For specific API advice, see this post.
WARNING This authorisation is based on the code signature of the process’s main executable. If the process loads plug-ins [1], the daemon can’t tell the difference between a request coming from the main executable and a request coming from a plug-in.
[1] I’m talking in-process plug-ins here. Plug-ins that run in their own process, such as those managed by ExtensionKit, aren’t a concern.
Choose an Approach
There are (at least) seven different ways to run with root privileges on macOS:
A setuid-root executable
The sudo command-line tool
The authopen command-line tool
AppleScript’s do shell script command, passing true to the administrator privileges parameter
The osascript command-line tool to run an AppleScript
The AuthorizationExecuteWithPrivileges routine, deprecated since macOS 10.7
The SMJobSubmit routine targeting the kSMDomainSystemLaunchd domain, deprecated since macOS 10.10
The SMJobBless routine, deprecated since macOS 13
An installer package (.pkg)
The SMAppService class, a much-needed enhancement to the Service Management framework introduced in macOS 13
Note There’s one additional approach: The privileged file operation feature in NSWorkspace. I’ve not listed it here because it doesn’t let you run arbitrary code with root privileges. It does, however, have one critical benefit: It’s supported in sandboxed apps. See this post for a bunch of hints and tips.
To choose between them:
Do not use a setuid-root executable. Ever. It’s that simple! Doing that is creating a security vulnerability looking for an attacker to exploit it.
If you’re working interactively on the command line, use sudo, authopen, and osascript as you see fit.
IMPORTANT These are not appropriate to use as API. Specifically, while it may be possible to invoke sudo programmatically under some circumstances, by the time you’re done you’ll have code that’s way more complicated than the alternatives.
If you’re building an ad hoc solution to distribute to a limited audience, and you need one-shot privileges, use either AuthorizationExecuteWithPrivileges or AppleScript.
While AuthorizationExecuteWithPrivileges still works, it’s been deprecated for many years. Do not use it in a widely distributed product.
The AppleScript approach works great from AppleScript, but you can also use it from a shell script, using osascript, and from native code, using NSAppleScript. See the code snippet later in this post.
If you need one-shot privileges in a widely distributed product, consider using SMJobSubmit. While this is officially deprecated, it’s used by the very popular Sparkle update framework, and thus it’s unlikely to break without warning.
If you only need escalated privileges to install your product, consider using an installer package. That’s by far the easiest solution to this problem.
Keep in mind that an installer package can install a launchd daemon and thereby gain ongoing privileges.
If you need ongoing privileges but don’t want to ship an installer package, use SMAppService. If you need to deploy to older systems, use SMJobBless.
For instructions on using SMAppService, see Updating helper executables from earlier versions of macOS.
For a comprehensive example of how to use SMJobBless, see the EvenBetterAuthorizationSample sample code. For the simplest possible example, see the SMJobBless sample code. That has a Python script to help you debug your setup. Unfortunately this hasn’t been updated in a while; see this thread for more.
Hints and Tips
I’m sure I’ll think of more of these as time goes by but, for the moment, let’s start with the big one…
Do not run GUI code as root. In some cases you can make this work but it’s not supported. Moreover, it’s not safe. The GUI frameworks are huge, and thus have a huge attack surface. If you run GUI code as root, you are opening yourself up to security vulnerabilities.
Appendix: Running an AppleScript from Native Code
Below is an example of running a shell script with elevated privileges using NSAppleScript.
WARNING This is not meant to be the final word in privilege escalation. Before using this, work through the steps above to see if it’s the right option for you.
Hint It probably isn’t!
let url: URL = … file URL for the script to execute …
let script = NSAppleScript(source: """
on open (filePath)
if class of filePath is not text then
error "Expected a single file path argument."
end if
set shellScript to "exec " & quoted form of filePath
do shell script shellScript with administrator privileges
end open
""")!
// Create the Apple event.
let event = NSAppleEventDescriptor(
eventClass: AEEventClass(kCoreEventClass),
eventID: AEEventID(kAEOpenDocuments),
targetDescriptor: nil,
returnID: AEReturnID(kAutoGenerateReturnID),
transactionID: AETransactionID(kAnyTransactionID)
)
// Set up the direct object parameter to be a single string holding the
// path to our script.
let parameters = NSAppleEventDescriptor(string: url.path)
event.setDescriptor(parameters, forKeyword: AEKeyword(keyDirectObject))
// The `as NSAppleEventDescriptor?` is required due to a bug in the
// nullability annotation on this method’s result (r. 38702068).
var error: NSDictionary? = nil
guard let result = script.executeAppleEvent(event, error: &error) as NSAppleEventDescriptor? else {
let code = (error?[NSAppleScript.errorNumber] as? Int) ?? 1
let message = (error?[NSAppleScript.errorMessage] as? String) ?? "-"
throw NSError(domain: "ShellScript", code: code, userInfo: nil)
}
let scriptResult = result.stringValue ?? ""
Revision History
2025-03-24 Added info about authopen and osascript.
2024-11-15 Added info about SMJobSubmit. Made other minor editorial changes.
2024-07-29 Added a reference to the NSWorkspace privileged file operation feature. Made other minor editorial changes.
2022-06-22 First posted.
Hi all,
I am having a mysterious problem trying to load a user LaunchAgent under Big Sur - It is the .plist of gniemetz's automount.sh https://github.com/gniemetz/automount
for mounting SMB shares via pwd access from the Keychain -
Placed the .sh into /usr/local/bin, chmod 644 and chown user:staff
Placed the LaunchAgent .plist into ~/Library/LaunchAgents (created LaunchAgents it as it didn't exist), same chmod/chown.
drwxr-xr-x&amp;#9;&amp;#9;3&amp;#9; users&amp;#9;&amp;#9; 96 Nov&amp;#9;1 22:13 LaunchAgents
~/Library/LaunchAgentsrw-r--r--&amp;#9;&amp;#9;1&amp;#9; users&amp;#9; 1038 Nov&amp;#9;1 22:13 it.niemetz.automount.plist
/usr/local
drwxr-xr-x&amp;#9;&amp;#9;4 root&amp;#9;&amp;#9;wheel&amp;#9;&amp;#9;128 Nov&amp;#9;1 21:52 bin
/usr/local/binrwxr-xr-x&amp;#9;&amp;#9;1 root&amp;#9;&amp;#9;wheel&amp;#9;30310 Oct 29 21:58 automount.sh
then the following:
Load failed: 5: Input/output error
For the life of me, I cannot find anywhere what this means...
launchctl start ~/Library/LaunchAgents/it.niemetz.automount.plist
completes with no errors, syntax also parses OK
/Users//Library/LaunchAgents/it.niemetz.automount.plist: OK
I have added Terminal and /bin/bash to Full Disk Access under Security...
Launching the script manually as /usr/local/bin/automount.sh works fine.
Console shows
system.log shows this when load -w is run:
00:27:14 mac-mini-Big-Sur com.apple.xpc.launchd[1] (com.apple.xpc.launchd.user.domain.1000002.100006.Aqua): entering bootstrap mode
Nov&amp;#9;3 00:27:14 mac-mini-Big-Sur com.apple.xpc.launchd[1] (com.apple.xpc.launchd.user.domain.1000002.100006.Aqua): exiting bootstrap mode
For easy reference the .plist is pasted at the end -
Anyone seen this error before?
Thanks!
++
Label
it.niemetz.automount
LimitLoadToSessionType
Aqua
RunAtLoad
WatchPaths
/etc/resolv.conf
/Library/Preferences/SystemConfiguration/NetworkInterfaces.plist
/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist
ProgramArguments
/usr/local/bin/automount.sh
--mountall