X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=a4e419b7953f7db3bbe7bcc49675df222db17e80;hb=425c608d5319cae993d786a3915064bbb580c17b;hp=e74da54eac5b288ed54c7c839335fb73a460807e;hpb=9ed63f16131637fc0e76bd40b5452cfc45bccfc7;p=elogind.git diff --git a/src/core/service.c b/src/core/service.c index e74da54ea..a4e419b79 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -33,6 +33,7 @@ #include "log.h" #include "strv.h" #include "unit-name.h" +#include "unit-printf.h" #include "dbus-service.h" #include "special.h" #include "bus-errors.h" @@ -48,8 +49,7 @@ typedef enum RunlevelType { RUNLEVEL_UP, - RUNLEVEL_DOWN, - RUNLEVEL_SYSINIT + RUNLEVEL_DOWN } RunlevelType; static const struct { @@ -64,16 +64,6 @@ static const struct { { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP }, { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP }, -#ifdef TARGET_SUSE - /* SUSE style boot.d */ - { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, -#endif - -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) - /* Debian style rcS.d */ - { "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, -#endif - /* Standard SysV runlevels for shutdown */ { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN }, { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN } @@ -82,12 +72,10 @@ static const struct { directories in this order, and we want to make sure that sysv_start_priority is known when we first load the unit. And that value we only know from S links. Hence - UP/SYSINIT must be read before DOWN */ + UP must be read before DOWN */ }; #define RUNLEVELS_UP "12345" -/* #define RUNLEVELS_DOWN "06" */ -#define RUNLEVELS_BOOT "bBsS" #endif static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { @@ -139,9 +127,9 @@ static void service_init(Unit *u) { s->restart_usec = DEFAULT_RESTART_USEC; s->type = _SERVICE_TYPE_INVALID; - s->watchdog_watch.type = WATCH_INVALID; + watch_init(&s->watchdog_watch); + watch_init(&s->timer_watch); - s->timer_watch.type = WATCH_INVALID; #ifdef HAVE_SYSV_COMPAT s->sysv_start_priority = -1; s->sysv_start_priority_from_rcnd = -1; @@ -261,7 +249,7 @@ static void service_handle_watchdog(Service *s) { return; } - r = unit_watch_timer(UNIT(s), s->watchdog_usec - offset, &s->watchdog_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->watchdog_usec - offset, &s->watchdog_watch); if (r < 0) log_warning("%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r)); } @@ -294,6 +282,16 @@ static void service_done(Unit *u) { s->control_command = NULL; s->main_command = NULL; + set_free(s->restart_ignore_status.code); + s->restart_ignore_status.code = NULL; + set_free(s->restart_ignore_status.signal); + s->restart_ignore_status.signal = NULL; + + set_free(s->success_status.code); + s->success_status.code = NULL; + set_free(s->success_status.signal); + s->success_status.signal = NULL; + /* This will leak a process, but at least no memory or any of * our resources */ service_unwatch_main_pid(s); @@ -323,21 +321,12 @@ 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) || defined(TARGET_ANGSTROM) if (endswith(name, ".sh")) /* Drop Debian-style .sh suffix */ strcpy(stpcpy(r, name) - 3, ".service"); -#endif -#ifdef TARGET_SUSE - if (startswith(name, "boot.")) - /* Drop SuSE-style boot. prefix */ - strcpy(stpcpy(r, name + 5), ".service"); -#endif -#ifdef TARGET_FRUGALWARE if (startswith(name, "rc.")) /* Drop Frugalware-style rc. prefix */ strcpy(stpcpy(r, name + 3), ".service"); -#endif else /* Normal init scripts */ strcpy(stpcpy(r, name), ".service"); @@ -356,14 +345,11 @@ 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, -#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. * 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, @@ -374,20 +360,8 @@ static int sysv_translate_facility(const char *name, const char *filename, char "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE, "null", NULL, - -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, -#endif - -#ifdef TARGET_FEDORA - "MTA", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, - "smtpdaemon", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, - "httpd", SPECIAL_HTTP_DAEMON_TARGET, -#endif - -#ifdef TARGET_SUSE "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, -#endif }; unsigned i; @@ -515,7 +489,7 @@ static ExecCommand *exec_command_new(const char *path, const char *arg1) { return c; } -static int sysv_exec_commands(Service *s) { +static int sysv_exec_commands(Service *s, const bool supports_reload) { ExecCommand *c; assert(s); @@ -532,14 +506,25 @@ static int sysv_exec_commands(Service *s) { return -ENOMEM; exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c); - c = exec_command_new(UNIT(s)->source_path, "reload"); - if (!c) - return -ENOMEM; - exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c); + if (supports_reload) { + c = exec_command_new(UNIT(s)->source_path, "reload"); + if (!c) + return -ENOMEM; + exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c); + } return 0; } +static bool usage_contains_reload(const char *line) { + return (strcasestr(line, "{reload|") || + strcasestr(line, "{reload}") || + strcasestr(line, "{reload\"") || + strcasestr(line, "|reload|") || + strcasestr(line, "|reload}") || + strcasestr(line, "|reload\"")); +} + static int service_load_sysv_path(Service *s, const char *path) { FILE *f; Unit *u; @@ -549,10 +534,12 @@ static int service_load_sysv_path(Service *s, const char *path) { NORMAL, DESCRIPTION, LSB, - LSB_DESCRIPTION + LSB_DESCRIPTION, + USAGE_CONTINUATION } state = NORMAL; char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description; struct stat st; + bool supports_reload = false; assert(s); assert(path); @@ -601,8 +588,23 @@ static int service_load_sysv_path(Service *s, const char *path) { line++; t = strstrip(l); - if (*t != '#') + if (*t != '#') { + /* Try to figure out whether this init script supports + * the reload operation. This heuristic looks for + * "Usage" lines which include the reload option. */ + if ( state == USAGE_CONTINUATION || + (state == NORMAL && strcasestr(t, "usage"))) { + if (usage_contains_reload(t)) { + supports_reload = true; + state = NORMAL; + } else if (t[strlen(t)-1] == '\\') + state = USAGE_CONTINUATION; + else + state = NORMAL; + } + continue; + } if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) { state = LSB; @@ -895,15 +897,8 @@ static int service_load_sysv_path(Service *s, const char *path) { } } - if ((r = sysv_exec_commands(s)) < 0) + if ((r = sysv_exec_commands(s, supports_reload)) < 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 @@ -922,7 +917,6 @@ static int service_load_sysv_path(Service *s, const char *path) { s->timeout_stop_usec = DEFAULT_SYSV_TIMEOUT_USEC; } - /* Special setting for all SysV services */ s->type = SERVICE_FORKING; s->remain_after_exit = !s->pid_file; @@ -981,22 +975,11 @@ static int service_load_sysv_name(Service *s, const char *name) { assert(s); assert(name); - /* For SysV services we strip the boot.*, rc.* and *.sh + /* For SysV services we strip the rc.* and *.sh * prefixes/suffixes. */ -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) if (endswith(name, ".sh.service")) return -ENOENT; -#endif - -#ifdef TARGET_SUSE - if (startswith(name, "boot.")) - return -ENOENT; -#endif - -#ifdef TARGET_FRUGALWARE if (startswith(name, "rc.")) - return -ENOENT; -#endif STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) { char *path; @@ -1011,31 +994,13 @@ 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) || defined(TARGET_ANGSTROM) if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try Debian style *.sh source'able init scripts */ strcat(path, ".sh"); r = service_load_sysv_path(s, path); } -#endif free(path); -#ifdef TARGET_SUSE - if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { - /* Try SUSE style boot.* init scripts */ - - path = strjoin(*p, "/boot.", name, NULL); - if (!path) - return -ENOMEM; - - /* Drop .service suffix */ - path[strlen(path)-8] = 0; - r = service_load_sysv_path(s, path); - free(path); - } -#endif - -#ifdef TARGET_FRUGALWARE if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try Frugalware style rc.* init scripts */ @@ -1048,12 +1013,11 @@ static int service_load_sysv_name(Service *s, const char *name) { r = service_load_sysv_path(s, path); free(path); } -#endif if (r < 0) return r; - if ((UNIT(s)->load_state != UNIT_STUB)) + if (UNIT(s)->load_state != UNIT_STUB) break; } @@ -1151,12 +1115,6 @@ static int service_verify(Service *s) { return -EINVAL; } - if (s->type == SERVICE_ONESHOT && - s->exec_command[SERVICE_EXEC_RELOAD]) { - log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", UNIT(s)->id); - return -EINVAL; - } - if (s->type == SERVICE_DBUS && !s->bus_name) { log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id); return -EINVAL; @@ -1182,12 +1140,12 @@ static int service_add_default_dependencies(Service *s) { * majority of services. */ /* First, pull in base system */ - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; - } else if (UNIT(s)->manager->running_as == MANAGER_USER) { + } else if (UNIT(s)->manager->running_as == SYSTEMD_USER) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) return r; @@ -1582,7 +1540,12 @@ static void service_set_state(Service *s, ServiceState state) { cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true); if (old_state != state) - log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state)); + log_struct(LOG_DEBUG, + "UNIT=%s", UNIT(s)->id, + "MESSAGE=%s changed %s -> %s", UNIT(s)->id, + service_state_to_string(old_state), + service_state_to_string(state), + NULL); unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS); s->reload_result = SERVICE_SUCCESS; @@ -1613,7 +1576,8 @@ static int service_coldplug(Unit *u) { k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_start_usec; - if ((r = unit_watch_timer(UNIT(s), k, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, k, &s->timer_watch); + if (r < 0) return r; } } @@ -1758,7 +1722,7 @@ static int service_spawn( } if (timeout && s->timeout_start_usec) { - r = unit_watch_timer(UNIT(s), s->timeout_start_usec, &s->timer_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_start_usec, &s->timer_watch); if (r < 0) goto fail; } else @@ -1769,7 +1733,8 @@ static int service_spawn( goto fail; } - if (!(our_env = new0(char*, 4))) { + our_env = new0(char*, 5); + if (!our_env) { r = -ENOMEM; goto fail; } @@ -1792,10 +1757,14 @@ static int service_spawn( goto fail; } - if (!(final_env = strv_env_merge(2, - UNIT(s)->manager->environment, - our_env, - NULL))) { + if (s->meta.manager->running_as != SYSTEMD_SYSTEM) + if (asprintf(our_env + n_env++, "MANAGERPID=%lu", (unsigned long) getpid()) < 0) { + r = -ENOMEM; + goto fail; + } + + final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL); + if (!final_env) { r = -ENOMEM; goto fail; } @@ -1819,7 +1788,6 @@ static int service_spawn( if (r < 0) goto fail; - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) /* FIXME: we need to do something here */ goto fail; @@ -1902,9 +1870,14 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) || (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) || (s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL || - s->result == SERVICE_FAILURE_CORE_DUMP)))) { - - r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); + s->result == SERVICE_FAILURE_CORE_DUMP))) && + (s->result != SERVICE_FAILURE_EXIT_CODE || + !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) && + (s->result != SERVICE_FAILURE_SIGNAL || + !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status))) + ) { + + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->restart_usec, &s->timer_watch); if (r < 0) goto fail; @@ -2017,7 +1990,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if (wait_for_exit) { if (s->timeout_stop_usec > 0) { - r = unit_watch_timer(UNIT(s), s->timeout_stop_usec, &s->timer_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch); if (r < 0) goto fail; } @@ -2267,7 +2240,7 @@ static void service_enter_restart(Service *s) { /* Don't restart things if we are going down anyway */ log_info("Stop job pending for unit, delaying automatic restart."); - r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->restart_usec, &s->timer_watch); if (r < 0) goto fail; @@ -2445,6 +2418,7 @@ static int service_start_limit_test(Service *s) { case SERVICE_START_LIMIT_REBOOT_IMMEDIATE: log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id); + sync(); reboot(RB_AUTOBOOT); break; @@ -2493,7 +2467,7 @@ static int service_start(Unit *u) { /* Make sure we don't enter a busy loop of some kind. */ r = service_start_limit_test(s); if (r < 0) { - service_notify_sockets_dead(s, true); + service_enter_dead(s, SERVICE_FAILURE_START_LIMIT, false); return r; } @@ -2874,7 +2848,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(s); assert(pid >= 0); - if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status)) + if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) : + is_clean_exit_lsb(code, status, &s->success_status)) f = SERVICE_SUCCESS; else if (code == CLD_EXITED) f = SERVICE_FAILURE_EXIT_CODE; @@ -2906,8 +2881,16 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { f = SERVICE_SUCCESS; } - log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - "%s: main process exited, code=%s, status=%i", u->id, sigchld_code_to_string(code), status); + log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "MESSAGE=%s: main process exited, code=%s, status=%i/%s", + u->id, sigchld_code_to_string(code), status, + strna(code == CLD_EXITED + ? exit_status_to_string(status, EXIT_STATUS_FULL) + : signal_to_string(status)), + "UNIT=%s", u->id, + "EXIT_CODE=%s", sigchld_code_to_string(code), + "EXIT_STATUS=%i", status, + NULL); if (f != SERVICE_SUCCESS) s->result = f; @@ -2945,12 +2928,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { else service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); break; - } else { - assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY); - - /* Fall through */ } + /* Fall through */ + case SERVICE_RUNNING: service_enter_running(s, f); break; @@ -3020,7 +3001,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; case SERVICE_START: - assert(s->type == SERVICE_FORKING); + if (s->type != SERVICE_FORKING) + /* Maybe spurious event due to a reload that changed the type? */ + break; if (f != SERVICE_SUCCESS) { service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); @@ -3336,71 +3319,6 @@ 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->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; @@ -3413,7 +3331,7 @@ static int service_enumerate(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return 0; zero(runlevel_services); @@ -3485,7 +3403,7 @@ static int service_enumerate(Manager *m) { if (de->d_name[0] == 'S') { - if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) { + if (rcnd_table[i].type == RUNLEVEL_UP) { SERVICE(service)->sysv_start_priority_from_rcnd = MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd); @@ -3499,8 +3417,7 @@ static int service_enumerate(Manager *m) { goto finish; } else if (de->d_name[0] == 'K' && - (rcnd_table[i].type == RUNLEVEL_DOWN || - rcnd_table[i].type == RUNLEVEL_SYSINIT)) { + (rcnd_table[i].type == RUNLEVEL_DOWN)) { if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0) goto finish; @@ -3534,9 +3451,7 @@ static int service_enumerate(Manager *m) { * runlevels we assume the stop jobs will be implicitly added * by the core logic. Also, we don't really distinguish here * between the runlevels 0 and 6 and just add them to the - * special shutdown target. On SUSE the boot.d/ runlevel is - * also used for shutdown, so we add links for that too to the - * shutdown target.*/ + * special shutdown target. */ SET_FOREACH(service, shutdown_services, j) { service = unit_follow_merge(service); @@ -3549,10 +3464,6 @@ static int service_enumerate(Manager *m) { r = 0; -#ifdef TARGET_SUSE - sysv_facility_in_insserv_conf (m); -#endif - finish: free(path); free(fpath); @@ -3805,7 +3716,8 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { [SERVICE_FAILURE_EXIT_CODE] = "exit-code", [SERVICE_FAILURE_SIGNAL] = "signal", [SERVICE_FAILURE_CORE_DUMP] = "core-dump", - [SERVICE_FAILURE_WATCHDOG] = "watchdog" + [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT] = "start-limit" }; DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); @@ -3820,6 +3732,8 @@ DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); const UnitVTable service_vtable = { .object_size = sizeof(Service), + .exec_context_offset = offsetof(Service, exec_context), + .sections = "Unit\0" "Service\0"