Merge changes Ib54f39fd,I7e36edd8
* changes:
init: Retain traditional restart behavior for critical and oneshot services.
init: Safely restart services to avoid race conditions.
diff --git a/init/builtins.c b/init/builtins.c
index dc7900e..07bd6d3 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -590,8 +590,7 @@
struct service *svc;
svc = service_find_by_name(args[1]);
if (svc) {
- service_stop(svc);
- service_start(svc, NULL);
+ service_restart(svc);
}
return 0;
}
diff --git a/init/init.c b/init/init.c
index afa4d44..c21a495 100755
--- a/init/init.c
+++ b/init/init.c
@@ -164,7 +164,7 @@
* 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
@@ -359,15 +359,14 @@
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
- */
- 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)) {
+ if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
/* Hrm, an illegal flag. Default to SVC_DISABLED */
how = SVC_DISABLED;
}
@@ -399,6 +398,17 @@
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)
@@ -467,6 +477,17 @@
}
}
+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")) {
@@ -474,8 +495,7 @@
} 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);
}
diff --git a/init/init.h b/init/init.h
index 955e1f0..aa6a4ab 100644
--- a/init/init.h
+++ b/init/init.h
@@ -72,6 +72,7 @@
#define SVC_RESET 0x40 /* Use when stopping a process, but not disabling
so it can be restarted with its class */
#define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */
+#define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */
#define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */
@@ -127,6 +128,7 @@
void (*func)(struct service *svc));
void service_stop(struct service *svc);
void service_reset(struct service *svc);
+void service_restart(struct service *svc);
void service_start(struct service *svc, const char *dynamic_args);
void property_changed(const char *name, const char *value);
diff --git a/init/signal_handler.c b/init/signal_handler.c
index abccb40..d31ad63 100644
--- a/init/signal_handler.c
+++ b/init/signal_handler.c
@@ -63,7 +63,7 @@
NOTICE("process '%s', pid %d exited\n", svc->name, pid);
- if (!(svc->flags & SVC_ONESHOT)) {
+ if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
kill(-pid, SIGKILL);
NOTICE("process '%s' killing any children in process group\n", svc->name);
}
@@ -78,8 +78,9 @@
svc->pid = 0;
svc->flags &= (~SVC_RUNNING);
- /* oneshot processes go into the disabled state on exit */
- if (svc->flags & SVC_ONESHOT) {
+ /* oneshot processes go into the disabled state on exit,
+ * except when manually restarted. */
+ if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
svc->flags |= SVC_DISABLED;
}
@@ -90,7 +91,7 @@
}
now = gettime();
- if (svc->flags & SVC_CRITICAL) {
+ if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
ERROR("critical process '%s' exited %d times in %d minutes; "
@@ -105,6 +106,7 @@
}
}
+ svc->flags &= (~SVC_RESTART);
svc->flags |= SVC_RESTARTING;
/* Execute all onrestart commands for this service. */