I am writing a program that comes with a service. What I did so far is to create a helper tool that can run elevated tasks for my process and can communicate via XPC.
My program comes bundled with a service and I want to use the helper tool to install and start/stop this service so my program can have a checkbox "start service with system" in the settings.
I can successfully copy the plist for the service, but I cannot find any way to enable, disable, start or stop the service programmatically. I think the solution to call system("launchctl load /path/to/service.plist");
pretty ugly. Is there any mechanism in objective C to accomplish this task and get a success or failed result?
Apple has a deprecated C API for starting, stopping, and enabling launchd services in launch.h
. The source code for the API is on their opensource site: https://opensource.apple.com/source/launchd/launchd-442.26.2/liblaunch/
Here's some sample code that asks launchd to start the LittleSnitchUIAgent service:
#include <launch.h>
int main(int argc, const char * argv[]) {
const char *job = "at.obdev.LittleSnitchUIAgent";
launch_data_t resp, msg;
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
launch_data_dict_insert(
msg, launch_data_new_string(job), LAUNCH_KEY_STARTJOB);
resp = launch_msg(msg);
launch_data_free(msg);
return 0;
}
The LittleSnitchUIAgent isn't signification -- I chose it at random from my local list of services. I left error checking out of the sample to keep it straight forward.
If you haven't already I would recommend giving the launchd man pages and the Daemons and Services Programming Guide a very close study. Launchd can start your processes in response to almost anything: a timer, a socket connection, a device being added to the system, among many others. It's rare that you actually need to manage your own services. I haven't been able to confirm this but I suspect that's why they've deprecated the API.
Looks like there is a ServiceManagement API/Framework used to load LaunchAgents. Looks SMJobBless is the only method available
From ObjC you could always use the NSTask to manipulate daemons and agents using the launchctl
command-line.
Examples:
- (BOOL)isDaemonLoaded:(NSString *)daemonLabel {
NSTask *task = [NSTask new];
[task setLaunchPath:@"/bin/launchctl"];
[task setArguments:@[@"list", daemonLabel]];
[task launch];
[task waitUntilExit];
// return code 0 is for loaded daemon, and much info is in stdout, and 113 or other nonzero value when daemon is not loaded
return ([task terminationStatus] == 0);
}
or, starting/stopping a launchd global daemon:
typedef NS_ENUM(BOOL, DaemonState)
{
DaemonStateOff = NO,
DaemonStateOn = YES
};
- (BOOL)switchGlobalDaemon:(NSString *)daemonLabel
state:(DaemonState)newState {
NSString *command = (newState == OITStateOn) ? @"load" : @"unload";
NSString *daemonPath = [[@"/Library/LaunchDaemons" stringByAppendingPathComponent: daemonLabel] stringByAppendingPathExtension:@"plist"];
if (NO == [[NSFileManager defaultManager] fileExistsAtPath:daemonPath])
return NO;
NSTask *task = [NSTask new];
[task setLaunchPath:@"/bin/launchctl"];
[task setArguments:@[command, daemonPath]];
[task launch];
[task waitUntilExit];
return ([task terminationStatus] == 0);
}
Of course any launchctl command can be wrapped like this. It looks silly to do this, but since Apple removed programmatic APIs and left us with launchctl...
Anyways, there are quite a few ways to manage your daemons and agents the "straight" way - by setting the rules in the.plist, and letting launchd apply the rules.
Only sometimes there are business logic consideration outside the MacOS domain (say - a notification from a remote server, via push-notifications, telling you to turn off some service, because the customer stopped paying...) or to change configuration of a running service - but that won't take effect until you relaunched it. So it may be still useful to know this technique.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.