On Wed, May 28, 2014 at 01:12:23AM +0200, Thomas H.P. Andersen wrote: > From: Thomas Hindoe Paaboel Andersen <pho...@gmail.com> > > Reuses logic from service.c and the rc-local generator. > > Note that this drops reading of chkconfig entirely. How likely is this to cause regressions in existing distributions?
> It also drops reading > runlevels from the LSB headers. The runlevels were only used to check for > runlevels outside of the normal 1-5 range and then add special dependencies > and settings. Special runlevels were dropped in the past so it seemed to be > unused code. > > Also note that this code behaves differently to the old if an initcsript > and native unit exist with the same name. Before the initscript would be > ignored but now a .service file is created in /run which will override > the native unit. This is a total no-no. This would immediately break existing setups, becuase since forever this has been documented and advertised as a compatibility mechanism. > The old code dealing with initscripts are left in for now as the generator > will run first and the old code will then ignore the initscripts since > unit files exist for them. I will add another patch to rip the old code out > later. It would be nice to have this counterpart too, since then it would be easier to tell how much complexity and existing code we are removing. I think that there's general agreement to the idea of moving sysv support to a generator, the question is only if we can do it without significant breakage. > The patch is WIP and only tested lightly. I am mostly looking for comments > if this is a good idea at all. The code look OK. > --- > .gitignore | 1 + > Makefile.am | 10 + > src/sysv-generator/Makefile | 1 + > src/sysv-generator/sysv-generator.c | 896 > ++++++++++++++++++++++++++++++++++++ > 4 files changed, 908 insertions(+) > create mode 100644 src/sysv-generator/Makefile > create mode 100644 src/sysv-generator/sysv-generator.c > > diff --git a/.gitignore b/.gitignore > index 908c563..061b4af 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -101,6 +101,7 @@ > /systemd-socket-proxyd > /systemd-sysctl > /systemd-system-update-generator > +/systemd-sysv-generator > /systemd-timedated > /systemd-timesyncd > /systemd-tmpfiles > diff --git a/Makefile.am b/Makefile.am > index 5b26bc3..a395969 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -549,6 +549,7 @@ nodist_systemunit_DATA += \ > units/halt-local.service > > systemgenerator_PROGRAMS += \ > + systemd-sysv-generator \ > systemd-rc-local-generator > endif > > @@ -1918,6 +1919,15 @@ UNINSTALL_EXEC_HOOKS += dbus1-generator-uninstall-hook > endif > > # > ------------------------------------------------------------------------------ > +systemd_sysv_generator_SOURCES = \ > + src/sysv-generator/sysv-generator.c > + > +systemd_sysv_generator_LDADD = \ > + libsystemd-core.la \ > + libsystemd-label.la \ > + libsystemd-shared.la > + > +# > ------------------------------------------------------------------------------ > systemd_rc_local_generator_SOURCES = \ > src/rc-local-generator/rc-local-generator.c > > diff --git a/src/sysv-generator/Makefile b/src/sysv-generator/Makefile > new file mode 100644 > index 0000000..530e5e9 > --- /dev/null > +++ b/src/sysv-generator/Makefile > @@ -0,0 +1 @@ > +../Makefile > diff --git a/src/sysv-generator/sysv-generator.c > b/src/sysv-generator/sysv-generator.c > new file mode 100644 > index 0000000..7d153a6 > --- /dev/null > +++ b/src/sysv-generator/sysv-generator.c > @@ -0,0 +1,896 @@ > +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ > + > +/*** > + This file is part of systemd. > + > + Copyright 2014 Thomas H.P. Andersen > + Copyright 2010 Lennart Poettering > + Copyright 2011 Michal Schmidt > + > + systemd is free software; you can redistribute it and/or modify it > + under the terms of the GNU Lesser General Public License as published by > + the Free Software Foundation; either version 2.1 of the License, or > + (at your option) any later version. > + > + systemd is distributed in the hope that it will be useful, but > + WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public License > + along with systemd; If not, see <http://www.gnu.org/licenses/>. > +***/ > + > +#include <errno.h> > +#include <stdio.h> > +#include <unistd.h> > + > +#include "util.h" > +#include "mkdir.h" > +#include "strv.h" > +#include "path-util.h" > +#include "path-lookup.h" > +#include "log.h" > +#include "strv.h" > +#include "unit.h" > +#include "unit-name.h" > +#include "special.h" > +#include "exit-status.h" > +#include "def.h" > +#include "env-util.h" > +#include "fileio.h" > +#include "hashmap.h" > + > +typedef enum RunlevelType { > + RUNLEVEL_UP, > + RUNLEVEL_DOWN > +} RunlevelType; > + > +static const struct { > + const char *path; > + const char *target; > + const RunlevelType type; > +} rcnd_table[] = { > + /* Standard SysV runlevels for start-up */ > + { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP }, > + { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP }, > + { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP }, > + { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP }, > + { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP }, > + > + /* Standard SysV runlevels for shutdown */ > + { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN }, > + { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN } > + > + /* Note that the order here matters, as we read the > + 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 must be read before DOWN */ > +}; > + > +typedef struct SysvStub { > + char *name; > + char *path; > + char *description; > + int sysv_start_priority; > + char *pid_file; > + char **before; > + char **after; > + char **wants; > + char **conflicts; > + bool has_lsb; > + bool reload; > +} SysvStub; > + > +const char *arg_dest = "/tmp"; > + > +static LookupPaths lp; > +static Hashmap *all_services; > + > +static int add_symlink(const char *service, const char *where) { > + _cleanup_free_ char *from = NULL, *to = NULL; > + int r; > + > + assert(service); > + assert(where); > + > + from = strjoin(arg_dest, "/", service, NULL); > + if (!from) > + return log_oom(); > + > + to = strjoin(arg_dest, "/", where, ".wants/", service, NULL); > + if (!to) > + return log_oom(); > + > + mkdir_parents_label(to, 0755); > + > + r = symlink(from, to); > + if (r < 0) { > + if (errno == EEXIST) > + return 0; > + > + log_error("Failed to create symlink %s: %m", to); > + return -errno; > + } > + > + return 1; > +} > + > +static int generate_unit_file(SysvStub *s) { > + char *unit; > + char **p; > + _cleanup_fclose_ FILE *f = NULL; > + _cleanup_free_ char *before = NULL; > + _cleanup_free_ char *after = NULL; > + _cleanup_free_ char *conflicts = NULL; > + int r; > + > + before = strv_join(s->before, " "); > + after = strv_join(s->after, " "); > + conflicts = strv_join(s->conflicts, " "); > + > + unit = strjoin(arg_dest, "/", s->name, NULL); > + if (!unit) > + return log_oom(); > + > + f = fopen(unit, "wxe"); > + if (!f) { > + log_error("Failed to create unit file %s: %m", unit); > + return -errno; > + } > + > + fprintf(f, > + "# Automatically generated by systemd-sysv-generator\n\n" > + "[Unit]\n" > + "SourcePath=%s\n" > + "Description=%s\n", > + s->path, s->description); > + > + if (!isempty(before)) > + fprintf(f, "Before=%s\n", before); > + if (!isempty(after)) > + fprintf(f, "After=%s\n", after); > + if (!isempty(conflicts)) > + fprintf(f, "Conflicts=%s\n", conflicts); > + > + fprintf(f, > + "\n[Service]\n" > + "Type=forking\n" > + "Restart=no\n" > + "TimeoutSec=5min\n" > + "IgnoreSIGPIPE=no\n" > + "KillMode=process\n" > + "GuessMainPID=no\n" > + "SysVStartPriority=%d\n" > + "RemainAfterExit=%s\n", > + s->sysv_start_priority, > + s->pid_file ? "no" : "yes"); > + > + if (s->pid_file) > + fprintf(f, "PidFile=%s\n", s->pid_file); > + > + fprintf(f, "ExecStart=%s start\n", s->path); > + fprintf(f, "ExecStop=%s stop\n", s->path); > + if (s->reload) > + fprintf(f, "ExecReload=%s reload\n", s->path); > + > + STRV_FOREACH(p, s->wants) Generally we keep the brace on the same line. This applies here and below too. > + { > + r = add_symlink(s->name, *p); > + if (r < 0) > + return r; Hm, maybe this is not the best failure mode. Maybe an error should be printed and the function should continue? > + } > + > + return 0; > +} > + > +static int set_sysvinit_path(SysvStub *s) { > + char **p; > + int r = 0; > + > + assert(s); > + > + STRV_FOREACH(p, lp.sysvinit_path) { > + char *path; > + > + path = strjoin(*p, "/", s->name, NULL); > + if (!path) > + return -ENOMEM; > + > + assert(endswith(path, ".service")); > + path[strlen(path)-8] = 0; > + > + r = access(path, F_OK); > + if (r < 0) { > + strcat(path, ".sh"); > + r = access(path, F_OK); > + } > + > + if (r < 0) { > + free(path); > + return r; > + } > + > + s->path = path; > + } > + > + 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 char *sysv_translate_name(const char *name) { > + char *r; > + > + r = new(char, strlen(name) + strlen(".service") + 1); > + if (!r) > + return NULL; > + > + if (endswith(name, ".sh")) > + /* Drop .sh suffix */ > + strcpy(stpcpy(r, name) - 3, ".service"); > + else > + /* Normal init script name */ > + strcpy(stpcpy(r, name), ".service"); > + > + return r; > +} > + > +static int sysv_translate_facility(const char *name, const char *filename, > char **_r) { > + > + /* We silently ignore the $ prefix here. According to the LSB > + * spec it simply indicates whether something is a > + * standardized name or a distribution-specific one. Since we > + * just follow what already exists and do not introduce new > + * uses or names we don't care who introduced a new name. */ > + > + static const char * const table[] = { > + /* LSB defined facilities */ > + "local_fs", NULL, > + "network", SPECIAL_NETWORK_ONLINE_TARGET, > + "named", SPECIAL_NSS_LOOKUP_TARGET, > + "portmap", SPECIAL_RPCBIND_TARGET, > + "remote_fs", SPECIAL_REMOTE_FS_TARGET, > + "syslog", NULL, > + "time", SPECIAL_TIME_SYNC_TARGET, > + }; > + > + unsigned i; > + char *r; > + const char *n; > + > + assert(name); > + assert(_r); > + > + n = *name == '$' ? name + 1 : name; > + > + for (i = 0; i < ELEMENTSOF(table); i += 2) { > + > + if (!streq(table[i], n)) > + continue; > + > + if (!table[i+1]) > + return 0; > + > + r = strdup(table[i+1]); > + if (!r) > + return log_oom(); > + > + goto finish; > + } > + > + /* If we don't know this name, fallback heuristics to figure > + * out whether something is a target or a service alias. */ > + > + if (*name == '$') { > + if (!unit_prefix_is_valid(n)) > + return -EINVAL; > + > + /* Facilities starting with $ are most likely targets */ > + r = unit_name_build(n, NULL, ".target"); > + } else if (filename && streq(name, filename)) > + /* Names equaling the file name of the services are > redundant */ > + return 0; > + else > + /* Everything else we assume to be normal service names */ > + r = sysv_translate_name(n); > + > + if (!r) > + return -ENOMEM; > + > +finish: > + *_r = r; > + > + return 1; > +} > + > +static int load_sysv(SysvStub *s) { > + FILE *f; > + unsigned line = 0; > + int r; > + enum { > + NORMAL, > + DESCRIPTION, > + LSB, > + LSB_DESCRIPTION, > + USAGE_CONTINUATION > + } state = NORMAL; > + char *short_description = NULL, *long_description = NULL, > *chkconfig_description = NULL, *description; > + bool supports_reload = false; > + > + assert(s); > + > + f = fopen(s->path, "re"); > + if (!f) { > + r = errno == ENOENT ? 0 : -errno; > + goto finish; > + } > + > + while (!feof(f)) { > + char l[LINE_MAX], *t; > + > + if (!fgets(l, sizeof(l), f)) { > + if (feof(f)) > + break; > + > + r = -errno; > + log_error_unit(s->name, > + "Failed to read configuration file > '%s': %s", > + s->path, strerror(-r)); > + goto finish; > + } > + > + line++; > + > + t = strstrip(l); > + 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; > + s->has_lsb = true; > + continue; > + } > + > + if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, > "### END INIT INFO")) { > + state = NORMAL; > + continue; > + } > + > + t++; > + t += strspn(t, WHITESPACE); > + > + if (state == NORMAL) { > + > + /* Try to parse Red Hat style description */ > + > + if (startswith_no_case(t, "description:")) { > + > + size_t k = strlen(t); > + char *d; > + const char *j; > + > + if (t[k-1] == '\\') { > + state = DESCRIPTION; > + t[k-1] = 0; > + } > + > + if ((j = strstrip(t+12)) && *j) { > + if (!(d = strdup(j))) { Please split the assignments out. > + r = -ENOMEM; > + goto finish; > + } > + } else > + d = NULL; > + > + free(chkconfig_description); > + chkconfig_description = d; > + > + } else if (startswith_no_case(t, "pidfile:")) { > + > + char *fn; > + > + state = NORMAL; > + > + fn = strstrip(t+8); > + if (!path_is_absolute(fn)) { > + log_error_unit(s->name, > + "[%s:%u] PID file not > absolute. Ignoring.", > + s->path, line); > + continue; > + } > + > + if (!(fn = strdup(fn))) { > + r = -ENOMEM; > + goto finish; > + } > + > + free(s->pid_file); > + s->pid_file = fn; > + } > + > + } else if (state == DESCRIPTION) { > + > + /* Try to parse Red Hat style description > + * continuation */ > + > + size_t k = strlen(t); > + char *j; > + > + if (t[k-1] == '\\') > + t[k-1] = 0; > + else > + state = NORMAL; > + > + if ((j = strstrip(t)) && *j) { > + char *d = NULL; > + > + if (chkconfig_description) > + d = strjoin(chkconfig_description, " > ", j, NULL); > + else > + d = strdup(j); > + > + if (!d) { > + r = -ENOMEM; > + goto finish; > + } > + > + free(chkconfig_description); > + chkconfig_description = d; > + } > + > + } else if (state == LSB || state == LSB_DESCRIPTION) { > + > + if (startswith_no_case(t, "Provides:")) { > + char *i, *w; > + size_t z; > + > + state = LSB; > + > + FOREACH_WORD_QUOTED(w, z, t+9, i) { > + char *n, *m; > + > + if (!(n = strndup(w, z))) { > + r = -ENOMEM; > + goto finish; > + } > + > + r = sysv_translate_facility(n, > basename(s->path), &m); > + free(n); > + > + if (r < 0) > + goto finish; > + > + if (r == 0) > + continue; > + > + if (unit_name_to_type(m) != > UNIT_SERVICE) { > + /* NB: SysV targets > + * which are provided > + * by a service are > + * pulled in by the > + * services, as an > + * indication that the > + * generic service is > + * now available. This > + * is strictly > + * one-way. The > + * targets do NOT pull > + * in the SysV > + * services! */ > + strv_extend(&s->before, m); > + strv_extend(&s->wants, m); > + } > + > + if (r < 0) > + log_error_unit(s->name, > + "[%s:%u] > Failed to add LSB Provides name %s, ignoring: %s", > + s->path, > line, m, strerror(-r)); > + > + free(m); > + } > + > + } else if (startswith_no_case(t, "Required-Start:") > || > + startswith_no_case(t, "Should-Start:") || > + startswith_no_case(t, "X-Start-Before:") > || > + startswith_no_case(t, "X-Start-After:")) { > + char *i, *w; > + size_t z; > + > + state = LSB; > + > + FOREACH_WORD_QUOTED(w, z, strchr(t, ':')+1, > i) { > + char *n, *m; If you use _cleanup_free_ things will become simpler.... > + bool is_before; > + > + if (!(n = strndup(w, z))) { > + r = -ENOMEM; > + goto finish; > + } > + > + r = sysv_translate_facility(n, > basename(s->path), &m); > + if (r < 0) { > + log_error_unit(s->name, > + "[%s:%u] > Failed to translate LSB dependency %s, ignoring: %s", > + s->path, > line, n, strerror(-r)); > + free(n); > + continue; > + } > + > + free(n); > + > + if (r == 0) > + continue; > + > + is_before = startswith_no_case(t, > "X-Start-Before:"); > + > + if (streq(m, > SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) { > + /* the network-online target > is special, as it needs to be actively pulled in */ > + strv_extend(&s->after, m); > + strv_extend(&s->wants, m); > + } > + else { > + if (is_before) > + > strv_extend(&s->before, m); > + else > + > strv_extend(&s->after, m); > + } > + > + if (r < 0) > + log_error_unit(s->name, > + "[%s:%u] > Failed to add dependency on %s, ignoring: %s", > + s->path, > line, m, strerror(-r)); > + > + free(m); > + } > + } else if (startswith_no_case(t, "Description:")) { > + char *d, *j; > + > + state = LSB_DESCRIPTION; > + > + if ((j = strstrip(t+12)) && *j) { > + if (!(d = strdup(j))) { > + r = -ENOMEM; > + goto finish; > + } > + } else > + d = NULL; > + > + free(long_description); > + long_description = d; > + > + } else if (startswith_no_case(t, > "Short-Description:")) { > + char *d, *j; > + > + state = LSB; > + > + if ((j = strstrip(t+18)) && *j) { > + if (!(d = strdup(j))) { > + r = -ENOMEM; > + goto finish; > + } > + } else > + d = NULL; > + > + free(short_description); > + short_description = d; > + > + } else if (state == LSB_DESCRIPTION) { > + > + if (startswith(l, "#\t") || startswith(l, "# > ")) { > + char *j; > + > + if ((j = strstrip(t)) && *j) { > + char *d = NULL; > + > + if (long_description) > + d = > strjoin(long_description, " ", t, NULL); > + else > + d = strdup(j); > + > + if (!d) { > + r = -ENOMEM; > + goto finish; > + } > + > + free(long_description); > + long_description = d; > + } > + > + } else > + state = LSB; > + } > + } > + } > + > + s->reload = supports_reload; > + > + /* We use the long description only if > + * no short description is set. */ > + > + if (short_description) > + description = short_description; > + else if (chkconfig_description) > + description = chkconfig_description; > + else if (long_description) > + description = long_description; > + else > + description = NULL; > + > + if (description) { > + char *d; > + > + if (!(d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", > description))) { > + r = -ENOMEM; > + goto finish; > + } > + > + s->description = d; > + } > + > + r = 0; > + > +finish: > + > + if (f) > + fclose(f); > + > + free(short_description); > + free(long_description); > + free(chkconfig_description); > + Everything below finish should probably be cleaned up automatically. > + return r; > +} > + > +static void fix_order(SysvStub *s) { > + SysvStub *other; > + Iterator j; > + > + assert(s); > + > + if (s->sysv_start_priority < 0) > + return; > + > + HASHMAP_FOREACH(other, all_services, j) { > + if (s == other) > + continue; > + > + if (other->sysv_start_priority < 0) > + continue; > + > + /* If both units have modern headers we don't care > + * about the priorities */ > + if (s->has_lsb && other->has_lsb) > + continue; > + > + if (other->sysv_start_priority < s->sysv_start_priority) > + strv_extend(&s->after, other->name); > + else if (other->sysv_start_priority > s->sysv_start_priority) > + strv_extend(&s->before, other->name); > + else > + continue; > + > + /* FIXME: Maybe we should compare the name here > lexicographically? */ > + } > +} > + > +static int enumerate_sysv(void) { > + char **p; > + unsigned i; > + _cleanup_closedir_ DIR *d = NULL; > + _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL; > + SysvStub *service; > + Iterator j; > + Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; > + _cleanup_set_free_ Set *shutdown_services = NULL; > + int r = 0; > + > + all_services = hashmap_new(string_hash_func, string_compare_func); > + > + STRV_FOREACH(p, lp.sysvrcnd_path) > + for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { > + struct dirent *de; > + > + free(path); > + path = strjoin(*p, "/", rcnd_table[i].path, NULL); > + if (!path) { > + r = -ENOMEM; > + goto finish; > + } > + > + if (d) > + closedir(d); > + > + d = opendir(path); > + if (!d) { > + if (errno != ENOENT) > + log_warning("opendir(%s) failed: > %m", path); > + > + continue; > + } > + > + while ((de = readdir(d))) { > + int a, b; > + > + if (ignore_file(de->d_name)) > + continue; > + > + if (de->d_name[0] != 'S' && de->d_name[0] != > 'K') > + continue; > + > + if (strlen(de->d_name) < 4) > + continue; > + > + a = undecchar(de->d_name[1]); > + b = undecchar(de->d_name[2]); > + > + if (a < 0 || b < 0) > + continue; > + > + free(fpath); > + fpath = strjoin(path, "/", de->d_name, NULL); > + if (!fpath) { > + r = -ENOMEM; > + goto finish; > + } > + > + if (access(fpath, X_OK) < 0) { > + > + if (errno != ENOENT) > + log_warning("access() failed > on %s: %m", fpath); > + > + continue; > + } > + > + free(name); > + name = sysv_translate_name(de->d_name + 3); > + if (!name) { > + r = log_oom(); > + goto finish; > + } > + > + if (!hashmap_contains(all_services, name)) > + { > + service = new0(SysvStub, 1); > + service->sysv_start_priority = -1; > + service->name = strdup(name); > + r = set_sysvinit_path(service); > + if (r < 0) { > + log_warning("Could not find > init scirpt for %s: %s", service->name, strerror(-r)); > + continue; > + } > + > + hashmap_put(all_services, > service->name, service); > + } > + else > + service = hashmap_get(all_services, > name); > + > + > + if (r < 0) { > + log_warning("Failed to prepare unit > %s: %s", name, strerror(-r)); > + continue; > + } > + > + if (de->d_name[0] == 'S') { > + > + if (rcnd_table[i].type == > RUNLEVEL_UP) { > + service->sysv_start_priority > = > + MAX(a*10 + b, > service->sysv_start_priority); > + } > + > + r = > set_ensure_allocated(&runlevel_services[i], > + > trivial_hash_func, trivial_compare_func); > + if (r < 0) > + goto finish; > + > + r = set_put(runlevel_services[i], > service); > + if (r < 0) > + goto finish; > + > + } else if (de->d_name[0] == 'K' && > + (rcnd_table[i].type == > RUNLEVEL_DOWN)) { > + > + r = > set_ensure_allocated(&shutdown_services, > + > trivial_hash_func, trivial_compare_func); > + if (r < 0) > + goto finish; > + > + r = set_put(shutdown_services, > service); > + if (r < 0) > + goto finish; > + } > + } > + } > + > + > + for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) > + SET_FOREACH(service, runlevel_services[i], j) { > + strv_extend(&service->before, rcnd_table[i].target); > + strv_extend(&service->wants, rcnd_table[i].target); > + } > + > + SET_FOREACH(service, shutdown_services, j) { > + strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET); > + strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET); No oom checks? > + } > + > + r = 0; > + > +finish: > + > + for (i = 0; i < ELEMENTSOF(rcnd_table); i++) > + set_free(runlevel_services[i]); > + > + return r; > +} > + > +int main(int argc, char *argv[]) { > + int r, q; > + SysvStub *service; > + Iterator j; > + > + if (argc > 1 && argc != 4) { > + log_error("This program takes three or no arguments."); > + return EXIT_FAILURE; > + } > + > + if (argc > 1) > + arg_dest = argv[1]; > + > + log_set_target(LOG_TARGET_SAFE); > + log_parse_environment(); > + log_open(); > + > + umask(0022); > + > + r = lookup_paths_init( > + &lp, SYSTEMD_SYSTEM, true, > + NULL, NULL, NULL, NULL); > + if (r < 0) { > + log_error("Failed to find lookup paths."); > + return EXIT_FAILURE; > + } > + > + r = enumerate_sysv(); > + if (r < 0) { > + log_error("Failed to generate units for all init scripts."); > + return EXIT_FAILURE; > + } > + > + HASHMAP_FOREACH(service, all_services, j) { > + q = load_sysv(service); > + if (q < 0) > + continue; > + > + fix_order(service); > + > + q = generate_unit_file(service); > + if (q < 0) > + continue; > + } > + > + return EXIT_SUCCESS; > +} Zbyszek _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel