Wednesday, April 25, 2007

Howto unload LaunchD deamon from itself

Launchd is cool. It can be used to watchdog your process by setting the OnDemand key to false in the launchd plist. This means that if your program exitst launchd will automatically restart it. But what happens if you want to stop the deamon on purpose?!
First you can unload it with using the following terminal command:

sudo launchctl unload /Libarary/LaunchDaemon/mydaemon.plist


But you can use the following class to programatically use the launchd api for a daemon to unload itself.
Usage is like this:

int main()
{
LaunchD ld;

while(...) {
if (needstop)
ld.Stop();
}
}




class LaunchD
{
public:
LaunchD()
{
startedWithLaunchD = false;
me = 0;
}

~LaunchD()
{
if (me) {
launch_data_free(me);
}
}

bool CheckIn(bool allowRunWithoutLaunchd = true)
{
launch_data_t msg, resp;
msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
if ((resp = launch_msg(msg)) == NULL) {
if (allowRunWithoutLaunchd) {
startedWithLaunchD = false;
return false;
}
syslog(LOG_ERR,"Checkin with launchd failed: %m");
exit(EXIT_FAILURE);
}
launch_data_free(msg);
if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) {
errno = launch_data_get_errno(resp);
if (errno == EACCES) {
syslog(LOG_ERR, "Check-in failed. Did you forget to set"
"ServiceIPC == true in your plist?");
} else {
syslog(LOG_ERR, "Check-in failed: %m");
}
exit(EXIT_FAILURE);
}
launch_data_t tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_LABEL);
me = launch_data_copy(tmp);
startedWithLaunchD = true;
if (tmp) {
syslog(LOG_ERR, "My job label:%s\n",launch_data_get_string(tmp));
}
return true;
}

bool Stop()
{
if (startedWithLaunchD) {
launch_data_t resp;
launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
if (! launch_data_dict_insert(msg,me,LAUNCH_KEY_REMOVEJOB)) {
syslog(LOG_ERR, "launch_data_dict_insert failed!\n");
return false;
}
if (! launch_data_dict_insert(msg,me,LAUNCH_KEY_STOPJOB)) {
syslog(LOG_ERR, "launch_data_dict_insert failed!\n");
return false;
}
if ((resp = launch_msg(msg)) == NULL) {
syslog(LOG_ERR, "launch_msg LAUNCH_KEY_STOPJOB failed!\n");
return false;
}
if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) {
errno = launch_data_get_errno(resp);
if (errno == EACCES) {
syslog(LOG_ERR, "Stop request failed EACCESS!");
} else {
syslog(LOG_ERR, "Check-in failed: %m");
}
exit(EXIT_FAILURE);
}
launch_data_free(msg);
}
return true;
}
private:
bool startedWithLaunchD;
launch_data_t me;
};