From 7e36edd80f23a5d34ce7b5b0fbe8ecfea061a826 Mon Sep 17 00:00:00 2001 From: Mike Kasick Date: Mon, 6 Feb 2012 10:32:13 -0500 Subject: init: Safely restart services to avoid race conditions. Previously, service restarts (either an explicit "restart", or a "stop, start" pair) exhibited a race condition whereby the new (restarting) service process was often spawned before the old (stopping) process had terminated. This may have resulted in the new service process failing to acquire a limited resource (file lock, socket bind, etc.) that the old process had not yet released. Now, a stopping service remains in the SVC_RUNNING state until its exiting process has been reaped by waitpid. This prevents a "stop, start" sequence from spawning a second service process before resources held by the first are released. This enables safe service restarts by stopping the service, waiting for the old service process to terminate, and (only then) starting the new service process. In the event of "restarting" an already stopped service, the previous behavior is maintained whereby the service is simply started. This scenario could be special-cased by the restart command, however, we have observed instances where services are, unintentionally, stopped and started "too quickly," and so simultaneous processes for the same service should never be allowed. Note that this commit alters the behaviors for explicit restarts of critical and oneshot services. Previously these serivces would simply be restarted, whereas now, an explicit restart of a critical service counts as a crash (which may result in a recovery reboot) and oneshot services go into the disabled state. --- init/init.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/init/init.c b/init/init.c index 1ee88a7..e0f2cf7 100755 --- a/init/init.c +++ b/init/init.c @@ -369,10 +369,9 @@ void service_start(struct service *svc, const char *dynamic_args) /* The how field should be either SVC_DISABLED or SVC_RESET */ static void service_stop_or_reset(struct service *svc, int how) { - /* we are no longer running, nor should we - * attempt to restart - */ - svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING)); + /* The service is still SVC_RUNNING until its process exits, but if it has + * already exited it shoudn't attempt a restart yet. */ + svc->flags &= (~SVC_RESTARTING); if ((how != SVC_DISABLED) && (how != SVC_RESET)) { /* Hrm, an illegal flag. Default to SVC_DISABLED */ -- cgit v1.1