Revision: 23040 http://trac.macosforge.org/projects/launchd/changeset/23040 Author: zarzycki@apple.com Date: 2007-02-07 15:39:58 -0800 (Wed, 07 Feb 2007) Log Message: ----------- Add a per job exit timeout. Modified Paths: -------------- trunk/launchd/src/launchd.plist.5 trunk/launchd/src/launchd_core_logic.c trunk/launchd/src/liblaunch_public.h Modified: trunk/launchd/src/launchd.plist.5 =================================================================== --- trunk/launchd/src/launchd.plist.5 2007-02-07 21:07:49 UTC (rev 23039) +++ trunk/launchd/src/launchd.plist.5 2007-02-07 23:39:58 UTC (rev 23040) @@ -176,6 +176,10 @@ The recommended idle time out (in seconds) to pass to the job. If no value is specified, a default time out will be supplied by .Nm launchd for use by the job at check in time. +.It Sy ExitTimeOut <integer> +The amount of time +.Nm launchd +waits before sending a SIGKILL signal. The default value is 20 seconds. The value zero is interpreted as infinity. .It Sy ThrottleInterval <integer> This key lets one override the default throttling policy imposed on jobs by .Nm launchd . Modified: trunk/launchd/src/launchd_core_logic.c =================================================================== --- trunk/launchd/src/launchd_core_logic.c 2007-02-07 21:07:49 UTC (rev 23039) +++ trunk/launchd/src/launchd_core_logic.c 2007-02-07 23:39:58 UTC (rev 23040) @@ -87,6 +87,7 @@ #define LAUNCHD_MIN_JOB_RUN_TIME 10 #define LAUNCHD_ADVISABLE_IDLE_TIMEOUT 30 +#define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20 extern char **environ; @@ -254,8 +255,10 @@ int forkfd; int log_redirect_fd; int nice; - int timeout; + unsigned int timeout; + unsigned int exit_timeout; int stdout_err_fd; + struct timeval sent_sigterm_time; time_t start_time; time_t min_run_time; unsigned int start_interval; @@ -405,6 +408,11 @@ { if (j->p) { job_assumes(j, kill(j->p, SIGTERM) != -1); + job_assumes(j, gettimeofday(&j->sent_sigterm_time, NULL) != -1); + if (j->exit_timeout) { + job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, + EV_ADD|EV_ONESHOT, NOTE_SECONDS, j->exit_timeout, j) != -1); + } } } @@ -516,9 +524,6 @@ if (!job_active(ji)) { job_remove(ji); } else { - if (debug_shutdown_hangs) { - job_assumes(ji, kevent_mod((uintptr_t)ji, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 8, ji) != -1); - } job_stop(ji); } } @@ -662,6 +667,9 @@ if (j->start_interval) { job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1); } + if (j->exit_timeout) { + kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + } kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); free(j); @@ -907,6 +915,7 @@ j->mgr = jm; j->min_run_time = LAUNCHD_MIN_JOB_RUN_TIME; j->timeout = LAUNCHD_ADVISABLE_IDLE_TIMEOUT; + j->exit_timeout = LAUNCHD_DEFAULT_EXIT_TIMEOUT; j->currently_ignored = true; j->ondemand = true; j->checkedin = true; @@ -1162,6 +1171,16 @@ job_import_integer(job_t j, const char *key, long long value) { switch (key[0]) { + case 'e': + case 'E': + if (strcasecmp(key, LAUNCH_JOBKEY_EXITTIMEOUT) == 0) { + if (value < 0) { + job_log(j, LOG_WARNING, "Exit timeout less zero. Ignoring."); + } else { + j->exit_timeout = value; + } + } + break; case 'n': case 'N': if (strcasecmp(key, LAUNCH_JOBKEY_NICE) == 0) { @@ -1577,6 +1596,7 @@ void job_reap(job_t j) { + struct timeval tve, tvd; struct rusage ru; int status; @@ -1596,12 +1616,24 @@ return; } + job_assumes(j, gettimeofday(&tve, NULL) != -1); + if (j->wait_reply_port) { job_log(j, LOG_DEBUG, "MPM wait reply being sent"); job_assumes(j, job_mig_wait_reply(j->wait_reply_port, 0, status) == 0); j->wait_reply_port = MACH_PORT_NULL; } + if (j->sent_sigterm_time.tv_sec) { + double delta; + + timersub(&tve, &j->sent_sigterm_time, &tvd); + + delta = (double)tvd.tv_sec + (double)tvd.tv_usec / (double)1000000; + + job_log(j, tvd.tv_sec ? LOG_NOTICE : LOG_INFO, "Exited %f seconds after SIGTERM was sent", delta); + } + timeradd(&ru.ru_utime, &j->ru.ru_utime, &j->ru.ru_utime); timeradd(&ru.ru_stime, &j->ru.ru_stime, &j->ru.ru_stime); j->ru.ru_maxrss += ru.ru_maxrss; @@ -1724,14 +1756,14 @@ } break; case EVFILT_TIMER: - if ((uintptr_t)j == kev->ident) { - if (j->p && job_assumes(j, debug_shutdown_hangs)) { + if ((uintptr_t)j == kev->ident || (uintptr_t)&j->start_interval == kev->ident) { + job_dispatch(j, true); + } else if ((uintptr_t)&j->exit_timeout == kev->ident) { + if (debug_shutdown_hangs) { job_force_sampletool(j); - } else { - job_dispatch(j, true); } - } else if ((uintptr_t)&j->start_interval == kev->ident) { - job_dispatch(j, true); + job_log(j, LOG_WARNING, "Exit timeout elapsed (%u seconds). Killing.", j->exit_timeout); + job_assumes(j, kill(j->p, SIGKILL) != -1); } else { calendarinterval_callback(j, kev); } @@ -1788,6 +1820,9 @@ job_log(j, LOG_DEBUG, "Starting"); + j->sent_sigterm_time.tv_sec = 0; + j->sent_sigterm_time.tv_usec = 0; + /* FIXME, using stdinpath is a hack for re-reading the conf file */ if (j->stdinpath) { sipc = true; Modified: trunk/launchd/src/liblaunch_public.h =================================================================== --- trunk/launchd/src/liblaunch_public.h 2007-02-07 21:07:49 UTC (rev 23039) +++ trunk/launchd/src/liblaunch_public.h 2007-02-07 23:39:58 UTC (rev 23040) @@ -58,6 +58,7 @@ #define LAUNCH_JOBKEY_USERNAME "UserName" #define LAUNCH_JOBKEY_GROUPNAME "GroupName" #define LAUNCH_JOBKEY_TIMEOUT "TimeOut" +#define LAUNCH_JOBKEY_EXITTIMEOUT "ExitTimeOut" #define LAUNCH_JOBKEY_INITGROUPS "InitGroups" #define LAUNCH_JOBKEY_SOCKETS "Sockets" #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"