diff options
| author | Mike Kasick <mike@kasick.org> | 2012-01-25 23:48:46 -0500 |
|---|---|---|
| committer | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2012-07-10 22:59:44 +0100 |
| commit | 9d1a567eb0d6ac8d0040228e7c41668c8e3eb37e (patch) | |
| tree | 47e1b04354b9eccdeddc8b4945d1a7cd4958994c /init/init.c | |
| parent | d0936023862581c3da6472a049870622f3aa31d3 (diff) | |
| download | system_core-9d1a567eb0d6ac8d0040228e7c41668c8e3eb37e.tar.gz system_core-9d1a567eb0d6ac8d0040228e7c41668c8e3eb37e.tar.bz2 system_core-9d1a567eb0d6ac8d0040228e7c41668c8e3eb37e.zip | |
Safely restart services to avoid race conditions.
At present, service restarts exhibit a race condition whereby the new
(restarting) service process is often spawned before the old (stopping)
process has terminated. This may result in the new service process failing
to acquire a limited resource (file lock, socket bind, etc.) that the old
process has not yet released.
The new method preforms a safe service restart by stopping the service,
waiting for the old service process to terminate, and (only then) start the
new service process. In the event of "restarting" an already stopped
service, the previous behavior is maintained whereby the service is simply
started.
Diffstat (limited to 'init/init.c')
| -rwxr-xr-x | init/init.c | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/init/init.c b/init/init.c index 576d305c..5df254c5 100755 --- a/init/init.c +++ b/init/init.c @@ -172,7 +172,7 @@ void service_start(struct service *svc, const char *dynamic_args) * state and immediately takes it out of the restarting * state if it was in there */ - svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET)); + svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART)); svc->time_started = 0; /* running processes require no additional work -- if @@ -370,15 +370,15 @@ void service_start(struct service *svc, const char *dynamic_args) notify_service_state(svc->name, "running"); } -/* The how field should be either SVC_DISABLED or SVC_RESET */ +/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */ static void service_stop_or_reset(struct service *svc, int how) { /* we are no longer running, nor should we - * attempt to restart + * attempt to restart (yet) */ svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING)); - if ((how != SVC_DISABLED) && (how != SVC_RESET)) { + if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) { /* Hrm, an illegal flag. Default to SVC_DISABLED */ how = SVC_DISABLED; } @@ -410,6 +410,17 @@ void service_stop(struct service *svc) service_stop_or_reset(svc, SVC_DISABLED); } +void service_restart(struct service *svc) +{ + if (svc->flags & SVC_RUNNING) { + /* Stop, wait, then start the service. */ + service_stop_or_reset(svc, SVC_RESTART); + } else if (!(svc->flags & SVC_RESTARTING)) { + /* Just start the service since it's not running. */ + service_start(svc, NULL); + } /* else: Service is restarting anyways. */ +} + void property_changed(const char *name, const char *value) { if (property_triggers_enabled) @@ -476,6 +487,17 @@ static void msg_stop(const char *name) } } +static void msg_restart(const char *name) +{ + struct service *svc = service_find_by_name(name); + + if (svc) { + service_restart(svc); + } else { + ERROR("no such service '%s'\n", name); + } +} + void handle_control_message(const char *msg, const char *arg) { if (!strcmp(msg,"start")) { @@ -483,8 +505,7 @@ void handle_control_message(const char *msg, const char *arg) } else if (!strcmp(msg,"stop")) { msg_stop(arg); } else if (!strcmp(msg,"restart")) { - msg_stop(arg); - msg_start(arg); + msg_restart(arg); } else { ERROR("unknown control msg '%s'\n", msg); } |
