X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fservice.c;h=8f827aa520a0ffce73ef08d71feab822356696a0;hp=f8267541525c486e179397a9c44bf6dc58cb3b69;hb=db01f8b3f870611a013b913636bb7fefaab34018;hpb=1bd8b8184ee3bc7fc023d6d6dfb2ca99fb6612f3 diff --git a/src/service.c b/src/service.c index f82675415..8f827aa52 100644 --- a/src/service.c +++ b/src/service.c @@ -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 @@ -280,10 +280,10 @@ 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, @@ -372,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; @@ -470,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); @@ -481,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; @@ -620,7 +635,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); @@ -768,19 +783,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, "# ")) { @@ -790,7 +792,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); @@ -828,7 +830,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) @@ -906,7 +908,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")); @@ -927,7 +930,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 */ @@ -941,7 +945,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 */ @@ -1006,7 +1011,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; @@ -1264,9 +1269,6 @@ static int service_load_pid_file(Service *s) { assert(s); - if (s->main_pid_known) - return 0; - if (!s->pid_file) return 0; @@ -1285,6 +1287,16 @@ static int service_load_pid_file(Service *s) { 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; @@ -1481,7 +1493,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 && s->meta.manager->n_deserializing <= 0) + if (state == SERVICE_EXITED && s->meta.manager->n_reloading <= 0) cgroup_bonding_trim_list(s->meta.cgroup_bondings, true); if (old_state != state) @@ -1710,6 +1722,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) @@ -2569,9 +2582,14 @@ 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 (s->pid_file && service_load_pid_file(s) == 0) + return; 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 @@ -2650,7 +2668,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; @@ -2950,6 +2968,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 , not used, equivalent to X-Interactive */ + if (parsed && !startswith_no_case (parsed[0], "")) { + 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; @@ -2962,6 +3046,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) @@ -2969,8 +3056,8 @@ static int service_enumerate(Manager *m) { struct dirent *de; free(path); - path = NULL; - if (asprintf(&path, "%s/%s", *p, rcnd_table[i].path) < 0) { + path = join(*p, "/", rcnd_table[i].path, NULL); + if (!path) { r = -ENOMEM; goto finish; } @@ -3004,8 +3091,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 (!path) { r = -ENOMEM; goto finish; } @@ -3095,6 +3182,10 @@ static int service_enumerate(Manager *m) { r = 0; +#ifdef TARGET_SUSE + sysv_facility_in_insserv_conf (m); +#endif + finish: free(path); free(fpath); @@ -3212,6 +3303,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; @@ -3221,23 +3335,25 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro if (s->main_pid <= 0 && who == KILL_MAIN) { dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); - return -EINVAL; + return -ESRCH; } if (s->control_pid <= 0 && who == KILL_CONTROL) { dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ENOENT; + return -ESRCH; } - 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))) @@ -3257,7 +3373,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; } @@ -3329,6 +3445,10 @@ DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); const UnitVTable service_vtable = { .suffix = ".service", + .sections = + "Unit\0" + "Service\0" + "Install\0", .show_status = true, .init = service_init, @@ -3361,6 +3481,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,