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.