X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fservice.c;h=0110c9f12f37fcbe70aab5b4534caaa65e1d5785;hp=eb475d9cc938dbb305f008340e5ef5432928fc82;hb=57020a3abff20f176e9f0cbb982d7977119d6f08;hpb=1e2579fdeb7608719cb28da0b5061f48ba0efc34 diff --git a/src/service.c b/src/service.c index eb475d9cc..0110c9f12 100644 --- a/src/service.c +++ b/src/service.c @@ -187,11 +187,11 @@ static void service_close_socket_fd(Service *s) { static void service_connection_unref(Service *s) { assert(s); - if (!s->accept_socket) + if (!UNIT_DEREF(s->accept_socket)) return; - socket_connection_unref(s->accept_socket); - s->accept_socket = NULL; + socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); + unit_ref_unset(&s->accept_socket); } static void service_done(Unit *u) { @@ -232,7 +232,7 @@ static void service_done(Unit *u) { service_close_socket_fd(s); service_connection_unref(s); - set_free(s->configured_sockets); + unit_ref_unset(&s->accept_socket); unit_unwatch_timer(u, &s->timer_watch); } @@ -841,7 +841,7 @@ static int service_load_sysv_path(Service *s, const char *path) { s->restart = SERVICE_RESTART_NO; if (s->meta.manager->sysv_console) - s->exec_context.std_output = EXEC_OUTPUT_TTY; + s->exec_context.std_output = EXEC_OUTPUT_JOURNAL_AND_CONSOLE; s->exec_context.kill_mode = KILL_PROCESS; @@ -1290,7 +1290,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { free(p2); } -static int service_load_pid_file(Service *s, bool warn_if_missing) { +static int service_load_pid_file(Service *s, bool may_warn) { char *k; int r; pid_t pid; @@ -1301,9 +1301,9 @@ static int service_load_pid_file(Service *s, bool warn_if_missing) { return -ENOENT; if ((r = read_one_line_file(s->pid_file, &k)) < 0) { - if (warn_if_missing) - log_warning("Failed to read PID file %s after %s. The service might be broken.", - s->pid_file, service_state_to_string(s->state)); + if (may_warn) + log_info("PID file %s not readable (yet?) after %s.", + s->pid_file, service_state_to_string(s->state)); return r; } @@ -1314,8 +1314,9 @@ static int service_load_pid_file(Service *s, bool warn_if_missing) { 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_info("PID %lu read from file %s does not exist.", + (unsigned long) pid, s->pid_file); return -ESRCH; } @@ -1327,7 +1328,8 @@ static int service_load_pid_file(Service *s, bool warn_if_missing) { (unsigned long) s->main_pid, (unsigned long) pid); service_unwatch_main_pid(s); s->main_pid_known = false; - } + } else + log_debug("Main PID loaded: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1358,6 +1360,7 @@ static int service_search_main_pid(Service *s) { if ((pid = cgroup_bonding_search_main_pid_list(s->meta.cgroup_bondings)) <= 0) return -ENOENT; + log_debug("Main PID guessed: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1368,86 +1371,33 @@ static int service_search_main_pid(Service *s) { return 0; } -static int service_get_sockets(Service *s, Set **_set) { - Set *set; - Iterator i; - char *t; - int r; - - assert(s); - assert(_set); - - if (s->socket_fd >= 0) - return 0; - - if (!set_isempty(s->configured_sockets)) - return 0; - - /* Collects all Socket objects that belong to this - * service. Note that a service might have multiple sockets - * via multiple names. */ - - if (!(set = set_new(NULL, NULL))) - return -ENOMEM; - - SET_FOREACH(t, s->meta.names, i) { - char *k; - Unit *p; - - /* Look for all socket objects that go by any of our - * units and collect their fds */ - - if (!(k = unit_name_change_suffix(t, ".socket"))) { - r = -ENOMEM; - goto fail; - } - - p = manager_get_unit(s->meta.manager, k); - free(k); - - if (!p) - continue; - - if ((r = set_put(set, p)) < 0) - goto fail; - } - - *_set = set; - return 0; - -fail: - set_free(set); - return r; -} - -static int service_notify_sockets_dead(Service *s) { +static void service_notify_sockets_dead(Service *s) { Iterator i; - Set *set, *free_set = NULL; - Socket *sock; - int r; + Unit *u; assert(s); /* Notifies all our sockets when we die */ if (s->socket_fd >= 0) - return 0; + return; - if (!set_isempty(s->configured_sockets)) - set = s->configured_sockets; - else { - if ((r = service_get_sockets(s, &free_set)) < 0) - return r; + SET_FOREACH(u, s->meta.dependencies[UNIT_TRIGGERED_BY], i) + if (u->meta.type == UNIT_SOCKET) + socket_notify_service_dead(SOCKET(u)); - set = free_set; - } - - SET_FOREACH(sock, set, i) - socket_notify_service_dead(sock); + return; +} - set_free(free_set); +static void service_unwatch_pid_file(Service *s) { + if (!s->pid_file_pathspec) + return; - return 0; + log_debug("Stopping watch for %s's PID file %s", s->meta.id, s->pid_file_pathspec->path); + path_spec_unwatch(s->pid_file_pathspec, UNIT(s)); + path_spec_done(s->pid_file_pathspec); + free(s->pid_file_pathspec); + s->pid_file_pathspec = NULL; } static void service_set_state(Service *s, ServiceState state) { @@ -1457,6 +1407,8 @@ static void service_set_state(Service *s, ServiceState state) { old_state = s->state; s->state = state; + service_unwatch_pid_file(s); + if (state != SERVICE_START_PRE && state != SERVICE_START && state != SERVICE_START_POST && @@ -1606,8 +1558,7 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { int r; int *rfds = NULL; unsigned rn_fds = 0; - Set *set, *free_set = NULL; - Socket *sock; + Unit *u; assert(s); assert(fds); @@ -1616,18 +1567,15 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { if (s->socket_fd >= 0) return 0; - if (!set_isempty(s->configured_sockets)) - set = s->configured_sockets; - else { - if ((r = service_get_sockets(s, &free_set)) < 0) - return r; - - set = free_set; - } - - SET_FOREACH(sock, set, i) { + SET_FOREACH(u, s->meta.dependencies[UNIT_TRIGGERED_BY], i) { int *cfds; unsigned cn_fds; + Socket *sock; + + if (u->meta.type != UNIT_SOCKET) + continue; + + sock = SOCKET(u); if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0) goto fail; @@ -1660,12 +1608,9 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { *fds = rfds; *n_fds = rn_fds; - set_free(free_set); - return 0; fail: - set_free(set); free(rfds); return r; @@ -2601,6 +2546,95 @@ static bool service_check_snapshot(Unit *u) { return !s->got_socket_fd; } +static int service_retry_pid_file(Service *s) { + int r; + + assert(s->pid_file); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + + r = service_load_pid_file(s, false); + if (r < 0) + return r; + + service_unwatch_pid_file(s); + + service_enter_running(s, true); + return 0; +} + +static int service_watch_pid_file(Service *s) { + int r; + + log_debug("Setting watch for %s's PID file %s", s->meta.id, s->pid_file_pathspec->path); + r = path_spec_watch(s->pid_file_pathspec, UNIT(s)); + if (r < 0) + goto fail; + + /* the pidfile might have appeared just before we set the watch */ + service_retry_pid_file(s); + + return 0; +fail: + log_error("Failed to set a watch for %s's PID file %s: %s", + s->meta.id, s->pid_file_pathspec->path, strerror(-r)); + service_unwatch_pid_file(s); + return r; +} + +static int service_demand_pid_file(Service *s) { + PathSpec *ps; + + assert(s->pid_file); + assert(!s->pid_file_pathspec); + + ps = new0(PathSpec, 1); + if (!ps) + return -ENOMEM; + + ps->path = strdup(s->pid_file); + if (!ps->path) { + free(ps); + return -ENOMEM; + } + + path_kill_slashes(ps->path); + + /* PATH_CHANGED would not be enough. There are daemons (sendmail) that + * keep their PID file open all the time. */ + ps->type = PATH_MODIFIED; + ps->inotify_fd = -1; + + s->pid_file_pathspec = ps; + + return service_watch_pid_file(s); +} + +static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { + Service *s = SERVICE(u); + + assert(s); + assert(fd >= 0); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + assert(s->pid_file_pathspec); + assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd)); + + log_debug("inotify event for %s", u->meta.id); + + if (path_spec_fd_event(s->pid_file_pathspec, events) < 0) + goto fail; + + if (service_retry_pid_file(s) == 0) + return; + + if (service_watch_pid_file(s) < 0) + goto fail; + + return; +fail: + service_unwatch_pid_file(s); + service_enter_signal(s, SERVICE_STOP_SIGTERM, false); +} + static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { Service *s = SERVICE(u); bool success; @@ -2706,7 +2740,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { success = true; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, + log_full(success ? LOG_DEBUG : LOG_NOTICE, "%s: control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); s->failure = s->failure || !success; @@ -2741,30 +2775,46 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_START: assert(s->type == SERVICE_FORKING); - /* Let's try to load the pid - * file here if we can. We - * ignore the return value, - * since the PID file might - * actually be created by a - * START_POST script */ - - if (success) { - service_load_pid_file(s, !s->exec_command[SERVICE_EXEC_START_POST]); - service_search_main_pid(s); + if (!success) { + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + break; + } - service_enter_start_post(s); + if (s->pid_file) { + /* Let's try to load the pid file here if we can. + * The PID file might actually be created by a START_POST + * script. In that case don't worry if the loading fails. */ + bool has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; + int r = service_load_pid_file(s, !has_start_post); + if (!has_start_post && r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + break; + } } else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + service_search_main_pid(s); + service_enter_start_post(s); break; case SERVICE_START_POST: - if (success) { - service_load_pid_file(s, true); - service_search_main_pid(s); + if (!success) { + service_enter_stop(s, false); + break; } - s->reload_failure = !success; + if (s->pid_file) { + int r = service_load_pid_file(s, true); + if (r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_stop(s, false); + break; + } + } else + service_search_main_pid(s); + service_enter_running(s, true); break; @@ -2906,6 +2956,20 @@ static void service_cgroup_notify_event(Unit *u) { * except when we don't know pid which to expect the * SIGCHLD for. */ + case SERVICE_START: + case SERVICE_START_POST: + /* If we were hoping for the daemon to write its PID file, + * we can give up now. */ + if (s->pid_file_pathspec) { + log_warning("%s never wrote its PID file. Failing.", s->meta.id); + service_unwatch_pid_file(s); + if (s->state == SERVICE_START) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + else + service_enter_stop(s, false); + } + break; + case SERVICE_RUNNING: service_enter_running(s, true); break; @@ -3215,7 +3279,7 @@ static int service_enumerate(Manager *m) { r = 0; #ifdef TARGET_SUSE - sysv_facility_in_insserv_conf (m); + sysv_facility_in_insserv_conf (m); #endif finish: @@ -3301,6 +3365,7 @@ static void service_bus_query_pid_done( } int service_set_socket_fd(Service *s, int fd, Socket *sock) { + assert(s); assert(fd >= 0); @@ -3319,9 +3384,10 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock) { s->socket_fd = fd; s->got_socket_fd = true; - s->accept_socket = sock; - return 0; + unit_ref_set(&s->accept_socket, UNIT(sock)); + + return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); } static void service_reset_failed(Unit *u) { @@ -3510,6 +3576,7 @@ const UnitVTable service_vtable = { .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, + .fd_event = service_fd_event, .reset_failed = service_reset_failed,