Service Management

RSS for tag

The 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

86 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Install non-sandboxed Helper app from sandboxed Main app
Hi, I developed a utility app that allows monitoring system activity and usage. It is a sandboxed app distributed via the Mac App Store. Because in the sandbox I cannot fetch enough data about system activity (like processor temperature, fans, etc.), I developed a little Helper app (non-sandboxed), which currently is distributed via my website, and to enable extra features it provides, the user is asked to download and install it manually (it installs itself as a daemon). I'm looking for ways to improve the user experience. Ideally, it would be a button inside the main app, which would download and install the helper app, without asking the user to do more than pressing a button. As far as I understand, in the previous versions of macOS, it would be possible with privileged helpers and SMJobBless, but those are deprecated APIs now. Another way I tried, is simply downloading the installer app from the website, but opening it programmatically from the main app is tricky since it cannot remove it from the quarantine, in other words, it fails with "operation not permitted". Any advice is appreciated!
4
0
195
1w
How to make my daemon run only in pre login mode.
Hi, I'd like to be able to run my daemon process only in pre-logon mode that can be reach by either reboot the machine prior to provide user credentials, or by log out from current user. So far I couldn't find any useful configuration in the plist file under /Library/LaunchDaemon. Perhaps there's a way to get notification programmatically for when the system enter/exit pre-login mode ? Thanks
1
0
159
2w
Daemon needs to get user name
Years ago my daemon was since then using SCDynamicStoreCopyConsoleUser() function and now it longer works. Basically the daemon needs to know the user name of who is using the system. If I restart the daemon,after the login, it gets the user name. I tried run a shell command via my daemon ("id -F") and look likes it still picks the root as user name. So, is there a way to get the current user name using Swift? ProcessInfo.userName fails too
1
0
185
2w
Session, Desktops and login screen
Coming from windows development, I'm trying to understand macOS architecture and how to do certain things. I've already read the Root and Login Sessions AND Service and Daemons AND User Switch Notifications documentation so will frame the questions accordingly. On Windows, there's a concept of User Sessions, each of which contain One or more WindowStations, each of which contain One or more Desktops. Each user gets at least 3 desktops (e.g. Login/Lock/UAC, Screensaver, and default desktop). From what I understand about macOS, it only has Sessions and then a single Desktop. Is that correct? i.e. same display surface is used to display user's desktop, screensaver, sudo prompt and lock screen? What about login screen? Does each user get its own login screen process/window running in their session? or is there a common login screen for all users running in one particular session (root?). How does Fast User switching effect login screen? In a daemon, is it possible to get active console session ID? console meaning the session being displayed on the monitor, whether its login screen, lock screen, user's desktop etc. In a daemon, is it possible to get session switch notifications? E.g. user logged-in and now their desktop is being displayed, user logged-out and now we're back on login screen, or user switched to another user (Fast User switching). How do I get notification of such events in daemon? If no user is logged in which session is pre-login agent running in? and after login does the session ID assigned to pre-login agent stay the same and user's session is assigned a new session ID? Is there always one and only one pre-login agent running? Is it possible to launch pre-login agent and user agents on-demand with custom commandline arguments from a daemon?
7
0
258
2w
Startup Items Cache Issue: New Items from the Same Team Fail
Our team has two products. The first product adds two /Library/LaunchDaemon startup items and one /Library/LaunchAgents startup item, which run normally after installation. A few months later, our team developed another product, which adds two /Library/LaunchDaemon startup items and one /Library/LaunchAgents startup item. However, we found that on some customers' systems, these startup items for the second product do not load correctly, and the processes do not start. Restarting the system does not resolve the issue. This occurs across systems running versions 14.5 to 14.6.1. The app's signatures, notarization, and Gatekeeper validations all pass. Eventually, we discovered that by disabling and then re-enabling our team's startup items in the System Settings - Login Items, all the startup items from our team load correctly. Could this be a caching bug related to new startup items from the same team?
2
0
198
3w
SMAppService.daemon initial pkg install
What I did. Started with the example at https://developer.apple.com/documentation/servicemanagement/updating-your-app-package-installer-to-use-the-new-service-management-api Changed it to configure a system daemon instead let service = SMAppService.daemon(plistName: "com.xpc.example.daemon.plist") Disabled automatic register in the package postinstall script (or else pkg install fails) Built/Installed the package, it just places files in the disk Validated install files Ran the test|register commands by hand sudo ../SMAppServiceSampleCode.app/Contents/MacOS/SMAppServiceSampleCode" register Dealt with System Settings user interaction to do this Validated that com.xpc.example.daemon is installed and ready to work sudo launchctl list | grep example sudo launchctl print system/com.xpc.example.daemon Got it to successfully do some work, YAY sudo ../SMAppServiceSampleCode.app/Contents/MacOS/SMAppServiceSampleCode" test Expectations My users would obviously download and install this pkg, so to make it easy for them. I would expect that I could call SMAppService.daemon(plistName: "...") .register() during the package postinstall installation step and the system daemon would be configured. Observations After getting all my teeth pulled why can't I just do that? Why so many hurdles for the dev and the end user, asking them to code sign this and that, notarize this and that, click here and there, accepting this and that? I understand the job of a developer but for the end user this should be relatively easy. Questions Do I need to start a DTS ticket to get this simple flow to work? It could be I'm missing step 42 in my endeavor :-)
0
0
209
Aug ’24
Daemon has reduced permissions after migrating from SMJobBless to SMAppService
Hello, I am working on updating an app to see if we can remove deprecated API usage, and am running into an issue after migrating from SMJobBless to SMAppService. If there is no current solution, I know that SMJobBless still works, but I wish to move to non-deprecated APIs whenever possible. The app is a text editor that installs a privileged helper for when users need to edit text files with root privileges. The example I'll use here is /etc/ssh/sshd_config. When using SMJobBless, the privileged helper was able to write to this location. When using SMAppService.daemon, the daemon is not able to write to this location. Neither the app nor the daemon are sandboxed. Both use the hardened runtime, and the daemon does not have any hardened runtime exceptions. I'm not sure how to attach a debugger to the daemon, but I was able to add logging to the daemon to confirm that getuid() and geteuid() are both 0, so the daemon appears to be running as root. However, the daemon is returning permission errors when attempting to replace the file. {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"} I've tried both atomic saving and writing directly to the file. When this code is run by the privileged helper installed with SMJobBless, it works without permissions problems. Here is some simplified code I tried for atomic saving. do { let fileManager = FileManager.default try? fileManager.createDirectory(at: originalItemURL.deletingLastPathComponent(), withIntermediateDirectories: true) _ = try fileManager.replaceItemAt(originalItemURL, withItemAt: newItemURL, options: options) completionHandler(nil) } catch { completionHandler(error) } And the code for writing directly to the file do { try data.write(to: url) completionHandler(nil) } catch { completionHandler(error) } One thing I should note is that the privileged helper tool had a launchd plist embedded in the binary. When moving to SMAppService, I removed it from the build settings and added BundleProgram to it. It gets placed in my app bundle in Contents/Library/LaunchDaemons, while the daemon itself gets put in Contents/MacOS. The plist only contains the following keys: BundleProgram, Label, MachServices, and AssociatedBundleIdentifiers. is there anything additional I can do to give my daemon permission to edit these files, or do I need to stick with SMJobBless for the time being?
2
0
226
Aug ’24
Operation not permitted error when sandboxed daemon tries to write System Keychain
Hi, I'm writing a sandboxed Daemon that I register from my sandboxed application via SMAppService. The registration is successful, and the daemon is called based on logs. However when I'm trying to save a keychain item into the keychain, I see entries like this in the logs: (Security) SecItemAdd [com.apple.securityd:atomicfile] create /Library/Keychains/System.keychain.sb-1c133873-RPL9wo: Operation not permitted [com.apple.securityd:security_exception] UNIX error exception: 1 [com.apple.securityd:security_exception] CSSM Exception: 100001 UNIX[Operation not permitted] [com.apple.securityd:security_exception] CSSM Exception: 100001 UNIX[Operation not permitted] I'm attempting to create the item with the regular SecItemAdd function call: var query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrLabel as String: "[redacted string]", kSecAttrAccount as String: "[redacted string]", kSecAttrService as String: "[redacted string]", kSecValueData as String: secretData ] SecItemAdd(query as CFDictionary, nil) I'm guessing this is because the System keychain is outside of the sandbox for the daemon. Is there a way to create items for the System Keychain from a sandboxed daemon?
1
0
307
Aug ’24
Spawn Constraint example
I downloaded the sample code given in: https://developer.apple.com/documentation/servicemanagement/updating-your-app-package-installer-to-use-the-new-service-management-api?language=objc Made necessary changes and I was able to install and test successfully. Next, I watched: https://developer.apple.com/videos/play/wwdc2023/10266/ I noted the example given at: 14:52 Example launchd plist constraint. Applied the KeepAlive, RunAtLoad and SpawnConstraints parameters to the sample code downloaded earlier. I got the log in the console and agent was not allowed: default 11:35:26.885483+0300 kernel AMFI: Launch Constraint Violation (enforcing), error info: c[5]p[1]m[1]e[0], (Constraint not matched) launching proc[vc: 10 pid: 19439]: /Library/Application Support/X/SMAppServiceSampleCode.app/Contents/Resources/SampleLaunchAgent, launch type 0, failure proc [vc: 10 pid: 19439]: /Library/Application Support/X/SMAppServiceSampleCode.app/Contents/Resources/SampleLaunchAgent Is SpawnConstraint not applicable for launch agents? Since launchd is the only parent process that can spawn the launch agent based on the plist, is the example given at 14:52 still valid?
4
1
334
Aug ’24
Service Management how to register handle_checkbox_toggle?
Hi, I noticed in this page, there is no explanation about who/when/how the method handle_checkbox_toggle is called. Page: https://developer.apple.com/documentation/servicemanagement/updating-helper-executables-from-earlier-versions-of-macos?language=objc Ultimately, how should the app come to know when a app service is allowed or disallowed in System Settings > Login Items ?
1
0
420
Aug ’24
Track system event(shutdown/restart) via launchagent
Hi There, I have to achieve following scenario Track system event on macosx for shutdown and restart and update one plist with same event via launchAgent I have tried following code on launchAgent class MyAgent { init() { let notificationCenter = NSWorkspace.shared.notificationCenter // Register for system shutdown notification notificationCenter.addObserver(self, selector: #selector(handleNotification(_:)), name: NSWorkspace.willPowerOffNotification, object: nil) RunLoop.current.run() } @objc func handleNotification(_ notification: Notification) { var logMessage = "" switch notification.name { case NSWorkspace.willPowerOffNotification: os_log("System is going to shut down at", log: log, type: .default) updatePlistFile(event: "shut down") let fileName = "example.txt" let content = "shut down" createAndWriteFile(fileName: fileName, content: content) logMessage = "System is going to shut down at \(Date())\n" } } } loaded the agent, and tried to restart device, I can't see as it is coming to handleNotification Same code is working fine from sample application but not from launchAgent Is there any restriction is there for NSWorkspace, if is that so, how to track shutdown/restart event from launchAgent or LaunchDaemon Any help will be appreciate
3
0
391
2w
How to authorize LaunchDaemons
I referred this(https://developer.apple.com/forums/thread/721737?answerId=739716022#739716022) example, this works for agent, but I am not able to Launch a daemon As documentation says "If your app uses launch daemons, it needs to register those first. Launch daemons require authentication by the user", how do I get user authorizes the LaunchDaemon. In Smjobbless we used AuthorizationRef, but how do i use it with SMAppservice?
6
0
388
Aug ’24
PreLogin Agent and login screen UI
I'm working on a screen sharing app and need to capture Pre-login screen and also foward remote input to login window/screen so remote user can login. Researching online, it looks like I need to use Pre-Login Agent to do that. However, I found these two threads: https://forums.developer.apple.com/forums/thread/45536 https://developer.apple.com/forums/thread/726470 Apparently, there is an unpublished workaround related to (r. 5636091). Can anyone provide details about that?
2
0
285
Aug ’24
get image icon of running applications in daemon
I need to get image icon of running applications in daemon. I have found the method iconForFile. [[NSWorkspace sharedWorkspace] iconForFile: bundlePath]; However, as far as I know, the framework AppKit is not daemon-safe. https://developer.apple.com/library/archive/technotes/tn2083/_index.html So, the only way which I see is to get icon file path via parsing Info.plist. However, the icon is not defined for some system app, e.g.: /System/Applications/Calendar.app /System/Applications/System Settings.app Are there any way to get icons of system application in daemon code? Is it safe to use NSBundle in daemon code? Thank you in advance.
2
0
353
Aug ’24
Do CloudKit subscriptions work from command line tool application?
I am able to fetch CloudKit records from my MacOS command line tool/daemon. However, I would like CloudKit to notify my daemon whenever CKRecords were altered so I would not have to poll periodically. In CloudKit console I see that my app successfully created CloudKit subscription, but the part that confuses me is where in my app do I define callback function that gets called whenever CloudKit attempted to notify my app of CloudKit changes? My first question - do I need to define callback in my implementation of UNUserNotificationCenterDelegate? NSApplicationDelegate? Something else? My second question, would CKSyncEngine work from command line application?
1
0
294
Aug ’24
Why does E210002 error occur only when launched svnserve via launchctl?
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.
1
0
273
Jul ’24
Honoring User's Changed Selection when Registering macOS MainApp with SMAppService
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)") } } }
3
0
368
Jul ’24
Launch constraints disappear
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.
6
0
595
Jul ’24
Issues with LaunchAgent and LaunchDaemon
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?
3
0
373
Aug ’24