chiark / gitweb /
service: properly handle who argument on D-Bus kill calls
[elogind.git] / src / service.c
index 728ca0b0180ece820552ad496eff21c227a09b41..0b657679f7391fe4f630ba1f0e48f2dd46f10af8 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)
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_FRUGALWARE) || defined(TARGET_ANGSTROM)
         /* Debian style rcS.d */
         { "rcS.d",  SPECIAL_SYSINIT_TARGET,   RUNLEVEL_SYSINIT },
 #endif
@@ -160,12 +160,16 @@ static int service_set_main_pid(Service *s, pid_t pid) {
         if (pid == getpid())
                 return -EINVAL;
 
-        if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid())
+        s->main_pid = pid;
+        s->main_pid_known = true;
+
+        if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
                 log_warning("%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.",
                             s->meta.id, (unsigned long) pid);
 
-        s->main_pid = pid;
-        s->main_pid_known = true;
+                s->main_pid_alien = true;
+        } else
+                s->main_pid_alien = false;
 
         exec_status_start(&s->main_exec_status, pid);
 
@@ -242,7 +246,7 @@ static char *sysv_translate_name(const char *name) {
         if (!(r = new(char, strlen(name) + sizeof(".service"))))
                 return NULL;
 
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
         if (endswith(name, ".sh"))
                 /* Drop Debian-style .sh suffix */
                 strcpy(stpcpy(r, name) - 3, ".service");
@@ -276,24 +280,24 @@ static int sysv_translate_facility(const char *name, const char *filename, char
                 /* LSB defined facilities */
                 "local_fs",             SPECIAL_LOCAL_FS_TARGET,
 #ifndef TARGET_MANDRIVA
-               /* Due to unfortunate name selection in Mandriva,
-                * $network is provided by network-up which is ordered
-                * after network which actually starts interfaces.
-                * To break the loop, just ignore it */
+                /* Due to unfortunate name selection in Mandriva,
+                 * $network is provided by network-up which is ordered
+                 * after network which actually starts interfaces.
+                 * To break the loop, just ignore it */
                 "network",              SPECIAL_NETWORK_TARGET,
 #endif
                 "named",                SPECIAL_NSS_LOOKUP_TARGET,
                 "portmap",              SPECIAL_RPCBIND_TARGET,
                 "remote_fs",            SPECIAL_REMOTE_FS_TARGET,
                 "syslog",               SPECIAL_SYSLOG_TARGET,
-                "time",                 SPECIAL_RTC_SET_TARGET,
+                "time",                 SPECIAL_TIME_SYNC_TARGET,
 
                 /* common extensions */
                 "mail-transfer-agent",  SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
                 "x-display-manager",    SPECIAL_DISPLAY_MANAGER_SERVICE,
                 "null",                 NULL,
 
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
                 "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
 #endif
 
@@ -368,7 +372,7 @@ static int sysv_fix_order(Service *s) {
         /* For each pair of services where at least one lacks a LSB
          * header, we use the start priority value to order things. */
 
-        LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_SERVICE]) {
+        LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) {
                 Service *t;
                 UnitDependency d;
                 bool special_s, special_t;
@@ -466,6 +470,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 LSB_DESCRIPTION
         } state = NORMAL;
         char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
+        struct stat st;
 
         assert(s);
         assert(path);
@@ -477,12 +482,26 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 goto finish;
         }
 
+        zero(st);
+        if (fstat(fileno(f), &st) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
         free(s->sysv_path);
         if (!(s->sysv_path = strdup(path))) {
                 r = -ENOMEM;
                 goto finish;
         }
 
+        s->sysv_mtime = timespec_load(&st.st_mtim);
+
+        if (null_or_empty(&st)) {
+                u->meta.load_state = UNIT_MASKED;
+                r = 0;
+                goto finish;
+        }
+
         while (!feof(f)) {
                 char l[LINE_MAX], *t;
 
@@ -824,7 +843,7 @@ 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 = true;
+        s->remain_after_exit = !s->pid_file;
         s->restart = SERVICE_RESTART_NO;
         s->exec_context.std_output =
                 (s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY)
@@ -883,7 +902,7 @@ static int service_load_sysv_name(Service *s, const char *name) {
 
         /* For SysV services we strip the boot.*, rc.* and *.sh
          * prefixes/suffixes. */
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
         if (endswith(name, ".sh.service"))
                 return -ENOENT;
 #endif
@@ -910,7 +929,7 @@ static int service_load_sysv_name(Service *s, const char *name) {
 
                 r = service_load_sysv_path(s, path);
 
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
                 if (r >= 0 && s->meta.load_state == UNIT_STUB) {
                         /* Try Debian style *.sh source'able init scripts */
                         strcat(path, ".sh");
@@ -1002,7 +1021,7 @@ static int fsck_fix_order(Service *s) {
         /* For each pair of services where both have an fsck priority
          * we order things based on it. */
 
-        LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_SERVICE]) {
+        LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) {
                 Service *t;
                 UnitDependency d;
 
@@ -1189,8 +1208,12 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
 
         if (s->main_pid > 0)
                 fprintf(f,
-                        "%sMain PID: %lu\n",
-                        prefix, (unsigned long) s->main_pid);
+                        "%sMain PID: %lu\n"
+                        "%sMain PID Known: %s\n"
+                        "%sMain PID Alien: %s\n",
+                        prefix, (unsigned long) s->main_pid,
+                        prefix, yes_no(s->main_pid_known),
+                        prefix, yes_no(s->main_pid_alien));
 
         if (s->pid_file)
                 fprintf(f,
@@ -1256,8 +1279,11 @@ static int service_load_pid_file(Service *s) {
 
         assert(s);
 
+        if (s->main_pid_known)
+                return 0;
+
         if (!s->pid_file)
-                return -ENOENT;
+                return 0;
 
         if ((r = read_one_line_file(s->pid_file, &k)) < 0)
                 return r;
@@ -1470,7 +1496,7 @@ static void service_set_state(Service *s, ServiceState state) {
 
         /* For the inactive states unit_notify() will trim the cgroup,
          * but for exit we have to do that ourselves... */
-        if (state == SERVICE_EXITED)
+        if (state == SERVICE_EXITED && s->meta.manager->n_reloading <= 0)
                 cgroup_bonding_trim_list(s->meta.cgroup_bondings, true);
 
         if (old_state != state)
@@ -1592,8 +1618,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
                                 goto fail;
                         }
 
-                        memcpy(t, rfds, rn_fds);
-                        memcpy(t+rn_fds, cfds, cn_fds);
+                        memcpy(t, rfds, rn_fds * sizeof(int));
+                        memcpy(t+rn_fds, cfds, cn_fds * sizeof(int));
                         free(rfds);
                         free(cfds);
 
@@ -1738,8 +1764,18 @@ static int main_pid_good(Service *s) {
 
         /* If we know the pid file, then lets just check if it is
          * still valid */
-        if (s->main_pid_known)
+        if (s->main_pid_known) {
+
+                /* If it's an alien child let's check if it is still
+                 * alive ... */
+                if (s->main_pid_alien)
+                        return kill(s->main_pid, 0) >= 0 || errno != ESRCH;
+
+                /* .. otherwise assume we'll get a SIGCHLD for it,
+                 * which we really should wait for to collect exit
+                 * status and code */
                 return s->main_pid > 0;
+        }
 
         /* We don't know the pid */
         return -EAGAIN;
@@ -1848,12 +1884,11 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) {
                         if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH)
                                 log_warning("Failed to kill main process %li: %m", (long) s->main_pid);
                         else
-                                wait_for_exit = true;
+                                wait_for_exit = !s->main_pid_alien;
                 }
 
                 if (s->control_pid > 0) {
                         if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
-
                                 log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
                         else
                                 wait_for_exit = true;
@@ -2277,6 +2312,7 @@ static int service_start(Unit *u) {
 
         s->failure = false;
         s->main_pid_known = false;
+        s->main_pid_alien = false;
         s->forbid_restart = false;
 
         service_enter_start_pre(s);
@@ -2550,7 +2586,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         if (s->main_pid == pid) {
 
                 s->main_pid = 0;
-                exec_status_exit(&s->main_exec_status, pid, code, status, s->exec_context.utmp_id);
+                exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status);
 
                 /* If this is not a forking service than the main
                  * process got started and hence we copy the exit
@@ -2629,7 +2665,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 s->control_pid = 0;
 
                 if (s->control_command) {
-                        exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id);
+                        exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
 
                         if (s->control_command->ignore)
                                 success = true;
@@ -2688,16 +2724,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 break;
 
                         case SERVICE_START_POST:
-                                if (success && s->pid_file && !s->main_pid_known) {
-                                        int r;
-
-                                        /* Hmm, let's see if we can
-                                         * load the pid now after the
-                                         * start-post scripts got
-                                         * executed. */
-
-                                        if ((r = service_load_pid_file(s)) < 0)
-                                                log_warning("%s: failed to load PID file %s: %s", s->meta.id, s->pid_file, strerror(-r));
+                                if (success) {
+                                        service_load_pid_file(s);
+                                        service_search_main_pid(s);
                                 }
 
                                 s->reload_failure = !success;
@@ -2848,6 +2877,7 @@ static void service_cgroup_notify_event(Unit *u) {
 
         case SERVICE_STOP_SIGTERM:
         case SERVICE_STOP_SIGKILL:
+
                 if (main_pid_good(s) <= 0 && !control_pid_good(s))
                         service_enter_stop_post(s, true);
 
@@ -2947,6 +2977,9 @@ static int service_enumerate(Manager *m) {
 
         assert(m);
 
+        if (m->running_as != MANAGER_SYSTEM)
+                return 0;
+
         zero(runlevel_services);
 
         STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
@@ -3197,6 +3230,29 @@ static void service_reset_failed(Unit *u) {
         s->failure = false;
 }
 
+static bool service_need_daemon_reload(Unit *u) {
+        Service *s = SERVICE(u);
+
+        assert(s);
+
+#ifdef HAVE_SYSV_COMPAT
+        if (s->sysv_path) {
+                struct stat st;
+
+                zero(st);
+                if (stat(s->sysv_path, &st) < 0)
+                        /* What, cannot access this anymore? */
+                        return true;
+
+                if (s->sysv_mtime > 0 &&
+                    timespec_load(&st.st_mtim) != s->sysv_mtime)
+                        return true;
+        }
+#endif
+
+        return false;
+}
+
 static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
         Service *s = SERVICE(u);
         int r = 0;
@@ -3214,15 +3270,17 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro
                 return -ENOENT;
         }
 
-        if (s->control_pid > 0)
-                if (kill(s->control_pid, signo) < 0)
-                        r = -errno;
+        if (who == KILL_CONTROL || who == KILL_ALL)
+                if (s->control_pid > 0)
+                        if (kill(s->control_pid, signo) < 0)
+                                r = -errno;
 
-        if (s->main_pid > 0)
-                if (kill(s->main_pid, signo) < 0)
-                        r = -errno;
+        if (who == KILL_MAIN || who == KILL_ALL)
+                if (s->main_pid > 0)
+                        if (kill(s->main_pid, signo) < 0)
+                                r = -errno;
 
-        if (mode == KILL_CONTROL_GROUP) {
+        if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
                 int q;
 
                 if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
@@ -3242,7 +3300,7 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro
                         }
 
                 if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, false, pid_set)) < 0)
-                        if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+                        if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
                                 r = q;
         }
 
@@ -3346,6 +3404,8 @@ const UnitVTable service_vtable = {
 
         .reset_failed = service_reset_failed,
 
+        .need_daemon_reload = service_need_daemon_reload,
+
         .cgroup_notify_empty = service_cgroup_notify_event,
         .notify_message = service_notify_message,