static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
+static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
+
static void service_init(Unit *u) {
Service *s = SERVICE(u);
assert(s);
s->watchdog_event_source = sd_event_source_unref(s->watchdog_event_source);
- s->watchdog_timestamp = (struct dual_timestamp) { 0, 0 };
+ s->watchdog_timestamp = DUAL_TIMESTAMP_NULL;
}
-static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
-
-static void service_handle_watchdog(Service *s) {
- usec_t nw;
+static void service_start_watchdog(Service *s) {
int r;
assert(s);
- if (s->watchdog_usec == 0)
+ if (s->watchdog_usec <= 0)
return;
- nw = now(CLOCK_MONOTONIC);
- if (nw >= s->watchdog_timestamp.monotonic + s->watchdog_usec) {
- log_error_unit(UNIT(s)->id, "%s watchdog timeout!", UNIT(s)->id);
- service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_WATCHDOG);
- return;
- }
-
if (s->watchdog_event_source) {
r = sd_event_source_set_time(s->watchdog_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec);
if (r < 0) {
return;
}
- r = sd_event_source_set_enabled(s->watchdog_event_source, SD_EVENT_ON);
- } else
+ r = sd_event_source_set_enabled(s->watchdog_event_source, SD_EVENT_ONESHOT);
+ } else {
r = sd_event_add_monotonic(UNIT(s)->manager->event, s->watchdog_timestamp.monotonic + s->watchdog_usec, 0, service_dispatch_watchdog, s, &s->watchdog_event_source);
+ if (r < 0) {
+ log_warning_unit(UNIT(s)->id, "%s failed to add watchdog timer: %s", UNIT(s)->id, strerror(-r));
+ return;
+ }
+
+ /* Let's process everything else which might be a sign
+ * of living before we consider a service died. */
+ r = sd_event_source_set_priority(s->watchdog_event_source, SD_EVENT_PRIORITY_IDLE);
+ }
if (r < 0)
- log_warning_unit(UNIT(s)->id,
- "%s failed to install watchdog timer: %s",
- UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id, "%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r));
}
static void service_reset_watchdog(Service *s) {
assert(s);
dual_timestamp_get(&s->watchdog_timestamp);
- service_handle_watchdog(s);
+ service_start_watchdog(s);
}
static void service_done(Unit *u) {
s->notify_access = NOTIFY_MAIN;
if (s->type == SERVICE_DBUS || s->bus_name) {
- r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES,
- SPECIAL_DBUS_SOCKET, NULL, true);
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true);
if (r < 0)
return r;
}
static void service_set_state(Service *s, ServiceState state) {
ServiceState old_state;
const UnitActiveState *table;
+
assert(s);
table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table;
service_unwatch_pid_file(s);
- if (state != SERVICE_START_PRE &&
- state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL &&
- state != SERVICE_STOP_POST &&
- state != SERVICE_FINAL_SIGTERM &&
- state != SERVICE_FINAL_SIGKILL &&
- state != SERVICE_AUTO_RESTART)
+ if (!IN_SET(state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+ SERVICE_AUTO_RESTART))
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- if (state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RUNNING &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL) {
+ if (!IN_SET(state,
+ SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING, SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
service_unwatch_main_pid(s);
s->main_command = NULL;
}
- if (state != SERVICE_START_PRE &&
- state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL &&
- state != SERVICE_STOP_POST &&
- state != SERVICE_FINAL_SIGTERM &&
- state != SERVICE_FINAL_SIGKILL) {
+ if (!IN_SET(state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
service_unwatch_control_pid(s);
s->control_command = NULL;
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
- if (state != SERVICE_START_PRE &&
- state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RUNNING &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL &&
- state != SERVICE_STOP_POST &&
- state != SERVICE_FINAL_SIGTERM &&
- state != SERVICE_FINAL_SIGKILL &&
+ if (!IN_SET(state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING, SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
!(state == SERVICE_DEAD && UNIT(s)->job)) {
service_close_socket_fd(s);
service_connection_unref(s);
}
- if (state == SERVICE_STOP || state == SERVICE_STOP_SIGTERM)
+ if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
service_stop_watchdog(s);
/* For the inactive states unit_notify() will trim the cgroup,
}
if (old_state != state)
- log_debug_unit(UNIT(s)->id,
- "%s changed %s -> %s", UNIT(s)->id,
- service_state_to_string(old_state),
- service_state_to_string(state));
+ log_debug_unit(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
s->reload_result = SERVICE_SUCCESS;
if (s->deserialized_state != s->state) {
- if (s->deserialized_state == SERVICE_START_PRE ||
- s->deserialized_state == SERVICE_START ||
- s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RELOAD ||
- s->deserialized_state == SERVICE_STOP ||
- s->deserialized_state == SERVICE_STOP_SIGTERM ||
- s->deserialized_state == SERVICE_STOP_SIGKILL ||
- s->deserialized_state == SERVICE_STOP_POST ||
- s->deserialized_state == SERVICE_FINAL_SIGTERM ||
- s->deserialized_state == SERVICE_FINAL_SIGKILL) {
+ if (IN_SET(s->deserialized_state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
usec_t k;
- k = s->deserialized_state == SERVICE_START_PRE || s->deserialized_state == SERVICE_START ||
- s->deserialized_state == SERVICE_START_POST || s->deserialized_state == SERVICE_RELOAD ?
- s->timeout_start_usec : s->timeout_stop_usec;
+ k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec;
/* For the start/stop timeouts 0 means off */
if (k > 0) {
return r;
}
- if ((s->deserialized_state == SERVICE_START &&
- (s->type == SERVICE_FORKING ||
- s->type == SERVICE_DBUS ||
- s->type == SERVICE_ONESHOT ||
- s->type == SERVICE_NOTIFY)) ||
- s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RUNNING ||
- s->deserialized_state == SERVICE_RELOAD ||
- s->deserialized_state == SERVICE_STOP ||
- s->deserialized_state == SERVICE_STOP_SIGTERM ||
- s->deserialized_state == SERVICE_STOP_SIGKILL)
- if (s->main_pid > 0) {
- r = unit_watch_pid(UNIT(s), s->main_pid);
- if (r < 0)
- return r;
- }
+ if (pid_valid(s->main_pid) &&
+ ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) ||
+ IN_SET(s->deserialized_state,
+ SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING, SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
+ r = unit_watch_pid(UNIT(s), s->main_pid);
+ if (r < 0)
+ return r;
+ }
- if (s->deserialized_state == SERVICE_START_PRE ||
- s->deserialized_state == SERVICE_START ||
- s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RELOAD ||
- s->deserialized_state == SERVICE_STOP ||
- s->deserialized_state == SERVICE_STOP_SIGTERM ||
- s->deserialized_state == SERVICE_STOP_SIGKILL ||
- s->deserialized_state == SERVICE_STOP_POST ||
- s->deserialized_state == SERVICE_FINAL_SIGTERM ||
- s->deserialized_state == SERVICE_FINAL_SIGKILL)
- if (s->control_pid > 0) {
- r = unit_watch_pid(UNIT(s), s->control_pid);
- if (r < 0)
- return r;
- }
+ if (pid_valid(s->control_pid) &&
+ IN_SET(s->deserialized_state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+ r = unit_watch_pid(UNIT(s), s->control_pid);
+ if (r < 0)
+ return r;
+ }
- if (s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RUNNING)
- service_handle_watchdog(s);
+ if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
+ service_start_watchdog(s);
service_set_state(s, s->deserialized_state);
}
assert(s);
service_unwatch_control_pid(s);
-
- if (s->watchdog_usec > 0)
- service_reset_watchdog(s);
+ service_reset_watchdog(s);
s->control_command = s->exec_command[SERVICE_EXEC_START_POST];
if (s->control_command) {
}
}
if (dual_timestamp_is_set(&s->watchdog_timestamp))
- dual_timestamp_serialize(f, "watchdog-timestamp",
- &s->watchdog_timestamp);
+ dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
if (s->forbid_restart)
unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
/* If there is still a control process, wait for that first */
break;
+ case SERVICE_STOP_POST:
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
+
+ if (!control_pid_good(s))
+ service_enter_dead(s, f, true);
+ break;
+
default:
assert_not_reached("Uh, main process died at wrong time.");
}
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
- service_enter_dead(s, f, true);
+ if (main_pid_good(s) <= 0)
+ service_enter_dead(s, f, true);
break;
default:
assert(s);
assert(source == s->watchdog_event_source);
- service_handle_watchdog(s);
+ log_error_unit(UNIT(s)->id, "%s watchdog timeout!", UNIT(s)->id);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_WATCHDOG);
+
return 0;
}
break;
+ case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
if (main_pid_good(s) <= 0 && !control_pid_good(s))
static void service_notify_message(Unit *u, pid_t pid, char **tags) {
Service *s = SERVICE(u);
const char *e;
+ bool notify_dbus = false;
assert(u);
return;
}
- log_debug_unit(u->id,
- "%s: Got message", u->id);
+ log_debug_unit(u->id, "%s: Got message", u->id);
/* Interpret MAINPID= */
if ((e = strv_find_prefix(tags, "MAINPID=")) &&
s->state == SERVICE_RELOAD)) {
if (parse_pid(e + 8, &pid) < 0)
- log_warning_unit(u->id,
- "Failed to parse notification message %s", e);
+ log_warning_unit(u->id, "Failed to parse notification message %s", e);
else {
- log_debug_unit(u->id,
- "%s: got %s", u->id, e);
+ log_debug_unit(u->id, "%s: got %s", u->id, e);
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);
+ notify_dbus = true;
}
}
/* Interpret READY= */
- if (s->type == SERVICE_NOTIFY &&
- s->state == SERVICE_START &&
- strv_find(tags, "READY=1")) {
- log_debug_unit(u->id,
- "%s: got READY=1", u->id);
-
+ if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START && strv_find(tags, "READY=1")) {
+ log_debug_unit(u->id, "%s: got READY=1", u->id);
service_enter_start_post(s);
+ notify_dbus = true;
}
/* Interpret STATUS= */
char *t;
if (e[7]) {
-
if (!utf8_is_valid(e+7)) {
- log_warning_unit(u->id,
- "Status message in notification is not UTF-8 clean.");
+ log_warning_unit(u->id, "Status message in notification is not UTF-8 clean.");
return;
}
+ log_debug_unit(u->id, "%s: got %s", u->id, e);
+
t = strdup(e+7);
if (!t) {
- log_error_unit(u->id,
- "Failed to allocate string.");
+ log_oom();
return;
}
- log_debug_unit(u->id,
- "%s: got %s", u->id, e);
+ } else
+ t = NULL;
+ if (!streq_ptr(s->status_text, t)) {
free(s->status_text);
s->status_text = t;
- } else {
- free(s->status_text);
- s->status_text = NULL;
- }
-
+ notify_dbus = true;
+ } else
+ free(t);
}
+
+ /* Interpet WATCHDOG= */
if (strv_find(tags, "WATCHDOG=1")) {
- log_debug_unit(u->id,
- "%s: got WATCHDOG=1", u->id);
- if (dual_timestamp_is_set(&s->watchdog_timestamp))
- service_reset_watchdog(s);
+ log_debug_unit(u->id, "%s: got WATCHDOG=1", u->id);
+ service_reset_watchdog(s);
}
/* Notify clients about changed status or main pid */
- unit_add_to_dbus_queue(u);
+ if (notify_dbus)
+ unit_add_to_dbus_queue(u);
}
#ifdef HAVE_SYSV_COMPAT
.bus_interface = "org.freedesktop.systemd1.Service",
.bus_vtable = bus_service_vtable,
- .bus_changing_properties = bus_service_changing_properties,
.bus_set_property = bus_service_set_property,
.bus_commit_properties = bus_service_commit_properties,