chiark / gitweb /
service: don't warn if the pidfile still exists after SIGCHLD
[elogind.git] / src / service.c
index 5c17413a5906351978b705e155e0a0c508affbbc..6fc2484f1321be2cc2d274bb932ca36d12f644a3 100644 (file)
@@ -65,7 +65,7 @@ static const struct {
         { "boot.d", SPECIAL_SYSINIT_TARGET,   RUNLEVEL_SYSINIT },
 #endif
 
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_FRUGALWARE) || defined(TARGET_ANGSTROM)
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
         /* Debian style rcS.d */
         { "rcS.d",  SPECIAL_SYSINIT_TARGET,   RUNLEVEL_SYSINIT },
 #endif
@@ -83,7 +83,7 @@ static const struct {
 
 #define RUNLEVELS_UP "12345"
 /* #define RUNLEVELS_DOWN "06" */
-/* #define RUNLEVELS_BOOT "bBsS" */
+#define RUNLEVELS_BOOT "bBsS"
 #endif
 
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
@@ -121,8 +121,6 @@ static void service_init(Unit *u) {
         s->guess_main_pid = true;
 
         exec_context_init(&s->exec_context);
-        s->exec_context.std_output = u->meta.manager->default_std_output;
-        s->exec_context.std_error = u->meta.manager->default_std_error;
 
         RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
 
@@ -279,7 +277,8 @@ static int sysv_translate_facility(const char *name, const char *filename, char
         static const char * const table[] = {
                 /* LSB defined facilities */
                 "local_fs",             SPECIAL_LOCAL_FS_TARGET,
-#ifndef TARGET_MANDRIVA
+#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+#else
                 /* Due to unfortunate name selection in Mandriva,
                  * $network is provided by network-up which is ordered
                  * after network which actually starts interfaces.
@@ -635,7 +634,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
                                 char *d = NULL;
 
                                 if (chkconfig_description)
-                                        asprintf(&d, "%s %s", chkconfig_description, j);
+                                        d = join(chkconfig_description, " ", j, NULL);
                                 else
                                         d = strdup(j);
 
@@ -783,19 +782,6 @@ static int service_load_sysv_path(Service *s, const char *path) {
                                 free(short_description);
                                 short_description = d;
 
-                        } else if (startswith_no_case(t, "X-Interactive:")) {
-                                int b;
-
-                                if ((b = parse_boolean(strstrip(t+14))) < 0) {
-                                        log_warning("[%s:%u] Couldn't parse interactive flag. Ignoring.", path, line);
-                                        continue;
-                                }
-
-                                if (b)
-                                        s->exec_context.std_input = EXEC_INPUT_TTY;
-                                else
-                                        s->exec_context.std_input = EXEC_INPUT_NULL;
-
                         } else if (state == LSB_DESCRIPTION) {
 
                                 if (startswith(l, "#\t") || startswith(l, "#  ")) {
@@ -805,7 +791,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
                                                 char *d = NULL;
 
                                                 if (long_description)
-                                                        asprintf(&d, "%s %s", long_description, t);
+                                                        d = join(long_description, " ", t, NULL);
                                                 else
                                                         d = strdup(j);
 
@@ -826,6 +812,13 @@ static int service_load_sysv_path(Service *s, const char *path) {
 
         if ((r = sysv_exec_commands(s)) < 0)
                 goto finish;
+        if (s->sysv_runlevels &&
+            chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) &&
+            chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
+                /* Service has both boot and "up" runlevels
+                   configured.  Kill the "up" ones. */
+                delete_chars(s->sysv_runlevels, RUNLEVELS_UP);
+        }
 
         if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
                 /* If there a runlevels configured for this service
@@ -844,10 +837,12 @@ static int service_load_sysv_path(Service *s, const char *path) {
         /* Special setting for all SysV services */
         s->type = SERVICE_FORKING;
         s->remain_after_exit = !s->pid_file;
+        s->guess_main_pid = false;
         s->restart = SERVICE_RESTART_NO;
-        s->exec_context.std_output =
-                (s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY)
-                ? EXEC_OUTPUT_TTY : s->meta.manager->default_std_output;
+
+        if (s->meta.manager->sysv_console)
+                s->exec_context.std_output = EXEC_OUTPUT_TTY;
+
         s->exec_context.kill_mode = KILL_PROCESS;
 
         /* We use the long description only if
@@ -921,7 +916,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 char *path;
                 int r;
 
-                if (asprintf(&path, "%s/%s", *p, name) < 0)
+                path = join(*p, "/", name, NULL);
+                if (!path)
                         return -ENOMEM;
 
                 assert(endswith(path, ".service"));
@@ -942,7 +938,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 if (r >= 0 && s->meta.load_state == UNIT_STUB) {
                         /* Try SUSE style boot.* init scripts */
 
-                        if (asprintf(&path, "%s/boot.%s", *p, name) < 0)
+                        path = join(*p, "/boot.", name, NULL);
+                        if (!path)
                                 return -ENOMEM;
 
                         /* Drop .service suffix */
@@ -956,7 +953,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 if (r >= 0 && s->meta.load_state == UNIT_STUB) {
                         /* Try Frugalware style rc.* init scripts */
 
-                        if (asprintf(&path, "%s/rc.%s", *p, name) < 0)
+                        path = join(*p, "/rc.", name, NULL);
+                        if (!path)
                                 return -ENOMEM;
 
                         /* Drop .service suffix */
@@ -1110,6 +1108,24 @@ static int service_add_default_dependencies(Service *s) {
         return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
 }
 
+static void service_fix_output(Service *s) {
+        assert(s);
+
+        /* If nothing has been explicitly configured, patch default
+         * output in. If input is socket/tty we avoid this however,
+         * since in that case we want output to default to the same
+         * place as we read input from. */
+
+        if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT &&
+            s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
+            s->exec_context.std_input == EXEC_INPUT_NULL)
+                s->exec_context.std_error = s->meta.manager->default_std_error;
+
+        if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
+            s->exec_context.std_input == EXEC_INPUT_NULL)
+                s->exec_context.std_output = s->meta.manager->default_std_output;
+}
+
 static int service_load(Unit *u) {
         int r;
         Service *s = SERVICE(u);
@@ -1138,6 +1154,8 @@ static int service_load(Unit *u) {
 
         /* This is a new unit? Then let's add in some extras */
         if (u->meta.load_state == UNIT_LOADED) {
+                service_fix_output(s);
+
                 if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
                         return r;
 
@@ -1272,21 +1290,22 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
         free(p2);
 }
 
-static int service_load_pid_file(Service *s) {
+static int service_load_pid_file(Service *s, bool may_warn) {
         char *k;
         int r;
         pid_t pid;
 
         assert(s);
 
-        if (s->main_pid_known)
-                return 0;
-
         if (!s->pid_file)
-                return 0;
+                return -ENOENT;
 
-        if ((r = read_one_line_file(s->pid_file, &k)) < 0)
+        if ((r = read_one_line_file(s->pid_file, &k)) < 0) {
+                if (may_warn)
+                        log_warning("Failed to read PID file %s after %s. The service might be broken.",
+                                    s->pid_file, service_state_to_string(s->state));
                 return r;
+        }
 
         r = parse_pid(k, &pid);
         free(k);
@@ -1295,11 +1314,22 @@ static int service_load_pid_file(Service *s) {
                 return r;
 
         if (kill(pid, 0) < 0 && errno != EPERM) {
-                log_warning("PID %lu read from file %s does not exist. Your service or init script might be broken.",
-                            (unsigned long) pid, s->pid_file);
+                if (may_warn)
+                        log_warning("PID %lu read from file %s does not exist. Your service or init script might be broken.",
+                                    (unsigned long) pid, s->pid_file);
                 return -ESRCH;
         }
 
+        if (s->main_pid_known) {
+                if (pid == s->main_pid)
+                        return 0;
+
+                log_debug("Main PID changing: %lu -> %lu",
+                          (unsigned long) s->main_pid, (unsigned long) pid);
+                service_unwatch_main_pid(s);
+                s->main_pid_known = false;
+        }
+
         if ((r = service_set_main_pid(s, pid)) < 0)
                 return r;
 
@@ -1725,6 +1755,7 @@ static int service_spawn(
                        apply_tty_stdin,
                        s->meta.manager->confirm_spawn,
                        s->meta.cgroup_bondings,
+                       s->meta.cgroup_attributes,
                        &pid);
 
         if (r < 0)
@@ -2584,6 +2615,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 success = is_clean_exit(code, status);
 
         if (s->main_pid == pid) {
+                /* Forking services may occasionally move to a new PID.
+                 * As long as they update the PID file before exiting the old
+                 * PID, they're fine. */
+                if (service_load_pid_file(s, false) == 0)
+                        return;
 
                 s->main_pid = 0;
                 exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status);
@@ -2714,7 +2750,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                  * START_POST script */
 
                                 if (success) {
-                                        service_load_pid_file(s);
+                                        service_load_pid_file(s, !s->exec_command[SERVICE_EXEC_START_POST]);
                                         service_search_main_pid(s);
 
                                         service_enter_start_post(s);
@@ -2725,7 +2761,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
 
                         case SERVICE_START_POST:
                                 if (success) {
-                                        service_load_pid_file(s);
+                                        service_load_pid_file(s, true);
                                         service_search_main_pid(s);
                                 }
 
@@ -2735,7 +2771,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
 
                         case SERVICE_RELOAD:
                                 if (success) {
-                                        service_load_pid_file(s);
+                                        service_load_pid_file(s, true);
                                         service_search_main_pid(s);
                                 }
 
@@ -2965,6 +3001,72 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
 }
 
 #ifdef HAVE_SYSV_COMPAT
+
+#ifdef TARGET_SUSE
+static void sysv_facility_in_insserv_conf(Manager *mgr) {
+        FILE *f=NULL;
+        int r;
+
+        if (!(f = fopen("/etc/insserv.conf", "re"))) {
+                r = errno == ENOENT ? 0 : -errno;
+                goto finish;
+        }
+
+        while (!feof(f)) {
+                char l[LINE_MAX], *t;
+                char **parsed = NULL;
+
+                if (!fgets(l, sizeof(l), f)) {
+                        if (feof(f))
+                                break;
+
+                        r = -errno;
+                        log_error("Failed to read configuration file '/etc/insserv.conf': %s", strerror(-r));
+                        goto finish;
+                }
+
+                t = strstrip(l);
+                if (*t != '$' && *t != '<')
+                        continue;
+
+                parsed = strv_split(t,WHITESPACE);
+                /* we ignore <interactive>, not used, equivalent to X-Interactive */
+                if (parsed && !startswith_no_case (parsed[0], "<interactive>")) {
+                        char *facility;
+                        Unit *u;
+                        if (sysv_translate_facility(parsed[0], NULL, &facility) < 0)
+                                continue;
+                        if ((u = manager_get_unit(mgr, facility)) && (u->meta.type == UNIT_TARGET)) {
+                                UnitDependency e;
+                                char *dep = NULL, *name, **j;
+
+                                STRV_FOREACH (j, parsed+1) {
+                                        if (*j[0]=='+') {
+                                                e = UNIT_WANTS;
+                                                name = *j+1;
+                                        }
+                                        else {
+                                                e = UNIT_REQUIRES;
+                                                name = *j;
+                                        }
+                                        if (sysv_translate_facility(name, NULL, &dep) < 0)
+                                                continue;
+
+                                        r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, e, dep, NULL, true);
+                                        free(dep);
+                                }
+                        }
+                        free(facility);
+                }
+                strv_free(parsed);
+        }
+finish:
+        if (f)
+                fclose(f);
+
+}
+#endif
+
 static int service_enumerate(Manager *m) {
         char **p;
         unsigned i;
@@ -2987,7 +3089,6 @@ static int service_enumerate(Manager *m) {
                         struct dirent *de;
 
                         free(path);
-                        path = NULL;
                         path = join(*p, "/", rcnd_table[i].path, NULL);
                         if (!path) {
                                 r = -ENOMEM;
@@ -3023,8 +3124,8 @@ static int service_enumerate(Manager *m) {
                                         continue;
 
                                 free(fpath);
-                                fpath = NULL;
-                                if (asprintf(&fpath, "%s/%s/%s", *p, rcnd_table[i].path, de->d_name) < 0) {
+                                fpath = join(path, "/", de->d_name, NULL);
+                                if (!fpath) {
                                         r = -ENOMEM;
                                         goto finish;
                                 }
@@ -3114,6 +3215,10 @@ static int service_enumerate(Manager *m) {
 
         r = 0;
 
+#ifdef TARGET_SUSE
+       sysv_facility_in_insserv_conf (m);
+#endif
+
 finish:
         free(path);
         free(fpath);