[systemd-commits] 2 commits - .gitignore Makefile.am src/core src/sysv-generator

Thomas H.P. Andersen phomes at kemper.freedesktop.org
Fri Jun 6 15:59:59 PDT 2014


 .gitignore                          |    1 
 Makefile.am                         |   10 
 src/core/service.c                  |  980 ------------------------------------
 src/core/service.h                  |    6 
 src/core/socket.c                   |   14 
 src/sysv-generator/Makefile         |    1 
 src/sysv-generator/sysv-generator.c |  919 +++++++++++++++++++++++++++++++++
 7 files changed, 932 insertions(+), 999 deletions(-)

New commits:
commit 817e224bbce3ed157817961ef19c80a250845a05
Author: Thomas Hindoe Paaboel Andersen <phomes at gmail.com>
Date:   Thu May 29 21:51:50 2014 +0200

    Remove sysv parser from service.c
    
    Parsing sysv files was moved to the sysv-generator in the previous commit.
    This patch removes the sysv parsing from serivce.c.
    
    Note that this patch drops the following now unused sysv-specific info
    from service dump:
    "SysV Init Script has LSB Header: (yes/no)"
    "SysVEnabled: (yes/no)"
    "SysVRunLevels: (levels)"

diff --git a/src/core/service.c b/src/core/service.c
index 244ec11..10dc79c 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -49,41 +49,6 @@
 #include "bus-error.h"
 #include "bus-util.h"
 
-#ifdef HAVE_SYSV_COMPAT
-
-#define DEFAULT_SYSV_TIMEOUT_USEC (5*USEC_PER_MINUTE)
-
-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 */
-};
-
-#define RUNLEVELS_UP "12345"
-#endif
-
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_DEAD] = UNIT_INACTIVE,
         [SERVICE_START_PRE] = UNIT_ACTIVATING,
@@ -138,11 +103,6 @@ static void service_init(Unit *u) {
         s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
         s->restart_usec = u->manager->default_restart_usec;
         s->type = _SERVICE_TYPE_INVALID;
-
-#ifdef HAVE_SYSV_COMPAT
-        s->sysv_start_priority = -1;
-        s->sysv_start_priority_from_rcnd = -1;
-#endif
         s->socket_fd = -1;
         s->guess_main_pid = true;
 
@@ -295,11 +255,6 @@ static void service_done(Unit *u) {
         free(s->pid_file);
         s->pid_file = NULL;
 
-#ifdef HAVE_SYSV_COMPAT
-        free(s->sysv_runlevels);
-        s->sysv_runlevels = NULL;
-#endif
-
         free(s->status_text);
         s->status_text = NULL;
 
@@ -364,734 +319,6 @@ static int service_arm_timer(Service *s, usec_t usec) {
                         service_dispatch_timer, s);
 }
 
-#ifdef HAVE_SYSV_COMPAT
-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 sysv_fix_order(Service *s) {
-        Unit *other;
-        int r;
-
-        assert(s);
-
-        if (s->sysv_start_priority < 0)
-                return 0;
-
-        /* 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_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) {
-                Service *t;
-                UnitDependency d;
-                bool special_s, special_t;
-
-                t = SERVICE(other);
-
-                if (s == t)
-                        continue;
-
-                if (UNIT(t)->load_state != UNIT_LOADED)
-                        continue;
-
-                if (t->sysv_start_priority < 0)
-                        continue;
-
-                /* If both units have modern headers we don't care
-                 * about the priorities */
-                if ((UNIT(s)->fragment_path || s->sysv_has_lsb) &&
-                    (UNIT(t)->fragment_path || t->sysv_has_lsb))
-                        continue;
-
-                special_s = s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels);
-                special_t = t->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, t->sysv_runlevels);
-
-                if (special_t && !special_s)
-                        d = UNIT_AFTER;
-                else if (special_s && !special_t)
-                        d = UNIT_BEFORE;
-                else if (t->sysv_start_priority < s->sysv_start_priority)
-                        d = UNIT_AFTER;
-                else if (t->sysv_start_priority > s->sysv_start_priority)
-                        d = UNIT_BEFORE;
-                else
-                        continue;
-
-                /* FIXME: Maybe we should compare the name here lexicographically? */
-
-                if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-static ExecCommand *exec_command_new(const char *path, const char *arg1) {
-        ExecCommand *c;
-
-        if (!(c = new0(ExecCommand, 1)))
-                return NULL;
-
-        if (!(c->path = strdup(path))) {
-                free(c);
-                return NULL;
-        }
-
-        if (!(c->argv = strv_new(path, arg1, NULL))) {
-                free(c->path);
-                free(c);
-                return NULL;
-        }
-
-        return c;
-}
-
-static int sysv_exec_commands(Service *s, const bool supports_reload) {
-        ExecCommand *c;
-
-        assert(s);
-        assert(s->is_sysv);
-        assert(UNIT(s)->source_path);
-
-        c = exec_command_new(UNIT(s)->source_path, "start");
-        if (!c)
-                return -ENOMEM;
-        exec_command_append_list(s->exec_command+SERVICE_EXEC_START, c);
-
-        c = exec_command_new(UNIT(s)->source_path, "stop");
-        if (!c)
-                return -ENOMEM;
-        exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, 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;
-        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;
-        struct stat st;
-        bool supports_reload = false;
-
-        assert(s);
-        assert(path);
-
-        u = UNIT(s);
-
-        f = fopen(path, "re");
-        if (!f) {
-                r = errno == ENOENT ? 0 : -errno;
-                goto finish;
-        }
-
-        if (fstat(fileno(f), &st) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        free(u->source_path);
-        u->source_path = strdup(path);
-        if (!u->source_path) {
-                r = -ENOMEM;
-                goto finish;
-        }
-        u->source_mtime = timespec_load(&st.st_mtim);
-
-        if (null_or_empty(&st)) {
-                u->load_state = UNIT_MASKED;
-                r = 0;
-                goto finish;
-        }
-
-        s->is_sysv = true;
-
-        while (!feof(f)) {
-                char l[LINE_MAX], *t;
-
-                if (!fgets(l, sizeof(l), f)) {
-                        if (feof(f))
-                                break;
-
-                        r = -errno;
-                        log_error_unit(u->id,
-                                       "Failed to read configuration file '%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->sysv_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 chkconfig headers */
-
-                        if (startswith_no_case(t, "chkconfig:")) {
-                                int start_priority;
-                                char runlevels[16], *k;
-
-                                state = NORMAL;
-
-                                if (sscanf(t+10, "%15s %i %*i", runlevels, &start_priority) != 2) {
-                                        log_warning_unit(u->id, "[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line);
-                                        continue;
-                                }
-
-                                /* A start priority gathered from the
-                                 * symlink farms is preferred over the
-                                 * data from the LSB header. */
-                                if (start_priority < 0 || start_priority > 99)
-                                        log_warning_unit(u->id, "[%s:%u] Start priority out of range. Ignoring.", path, line);
-                                else
-                                        log_debug_unit(u->id, "[%s:%u] Ignoring start priority set in the chkconfig file.", path, line);
-
-                                char_array_0(runlevels);
-                                k = delete_chars(runlevels, WHITESPACE "-");
-                                if (k[0]) {
-                                        char *d;
-
-                                        d = strdup(k);
-                                        if (!d) {
-                                                r = -ENOMEM;
-                                                goto finish;
-                                        }
-
-                                        free(s->sysv_runlevels);
-                                        s->sysv_runlevels = d;
-                                }
-
-                        } else 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))) {
-                                                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_warning_unit(u->id,
-                                                         "[%s:%u] PID file not absolute. Ignoring.",
-                                                         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(path), &m);
-                                        free(n);
-
-                                        if (r < 0)
-                                                goto finish;
-
-                                        if (r == 0)
-                                                continue;
-
-                                        if (unit_name_to_type(m) == UNIT_SERVICE)
-                                                r = unit_merge_by_name(u, m);
-                                        else
-                                                /* 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! */
-                                                r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_WANTS, m, NULL, true);
-
-                                        if (r < 0)
-                                                log_error_unit(u->id,
-                                                               "[%s:%u] Failed to add LSB Provides name %s, ignoring: %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;
-                                        bool is_before;
-
-                                        if (!(n = strndup(w, z))) {
-                                                r = -ENOMEM;
-                                                goto finish;
-                                        }
-
-                                        r = sysv_translate_facility(n, basename(path), &m);
-                                        if (r < 0) {
-                                                log_error_unit(u->id,
-                                                               "[%s:%u] Failed to translate LSB dependency %s, ignoring: %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 */
-                                                r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, m, NULL, true);
-                                        else
-                                                r = unit_add_dependency_by_name(u, is_before ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true);
-
-                                        if (r < 0)
-                                                log_error_unit(u->id, "[%s:%u] Failed to add dependency on %s, ignoring: %s",
-                                                               path, line, m, strerror(-r));
-
-                                        free(m);
-                                }
-                        } else if (startswith_no_case(t, "Default-Start:")) {
-                                char *k, *d;
-
-                                state = LSB;
-
-                                k = delete_chars(t+14, WHITESPACE "-");
-
-                                if (k[0] != 0) {
-                                        if (!(d = strdup(k))) {
-                                                r = -ENOMEM;
-                                                goto finish;
-                                        }
-
-                                        free(s->sysv_runlevels);
-                                        s->sysv_runlevels = d;
-                                }
-
-                        } 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;
-                        }
-                }
-        }
-
-        if ((r = sysv_exec_commands(s, supports_reload)) < 0)
-                goto finish;
-
-        if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
-                /* If there a runlevels configured for this service
-                 * but none of the standard ones, then we assume this
-                 * is some special kind of service (which might be
-                 * needed for early boot) and don't create any links
-                 * to it. */
-
-                UNIT(s)->default_dependencies = false;
-
-                /* Don't timeout special services during boot (like fsck) */
-                s->timeout_start_usec = 0;
-                s->timeout_stop_usec = 0;
-        } else {
-                s->timeout_start_usec = DEFAULT_SYSV_TIMEOUT_USEC;
-                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;
-        s->guess_main_pid = false;
-        s->restart = SERVICE_RESTART_NO;
-        s->exec_context.ignore_sigpipe = false;
-        s->kill_context.kill_mode = KILL_PROCESS;
-
-        /* 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->sysv_has_lsb ? "LSB: " : "SYSV: ", description))) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                u->description = d;
-        }
-
-        /* Initialize the start priority from what has been set in the
-         * /etc/rcN.d/ hierarchies if we load the unit file as SysV
-         * init script. */
-        if (s->sysv_start_priority_from_rcnd >= 0)
-                s->sysv_start_priority = s->sysv_start_priority_from_rcnd;
-
-        u->load_state = UNIT_LOADED;
-        r = 0;
-
-finish:
-
-        if (f)
-                fclose(f);
-
-        free(short_description);
-        free(long_description);
-        free(chkconfig_description);
-
-        return r;
-}
-
-static int service_load_sysv_name(Service *s, const char *name) {
-        char **p;
-
-        assert(s);
-        assert(name);
-
-        /* For SysV services we strip the *.sh suffixes. */
-        if (endswith(name, ".sh.service"))
-                return -ENOENT;
-
-        STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) {
-                char *path;
-                int r;
-
-                path = strjoin(*p, "/", name, NULL);
-                if (!path)
-                        return -ENOMEM;
-
-                assert(endswith(path, ".service"));
-                path[strlen(path)-8] = 0;
-
-                r = service_load_sysv_path(s, path);
-
-                if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
-                        /* Try *.sh source'able init scripts */
-                        strcat(path, ".sh");
-                        r = service_load_sysv_path(s, path);
-                }
-                free(path);
-
-                if (r < 0)
-                        return r;
-
-                if (UNIT(s)->load_state != UNIT_STUB)
-                        break;
-        }
-
-        return 0;
-}
-
-static int service_load_sysv(Service *s) {
-        const char *t;
-        Iterator i;
-        int r;
-
-        assert(s);
-
-        /* Load service data from SysV init scripts, preferably with
-         * LSB headers ... */
-
-        if (strv_isempty(UNIT(s)->manager->lookup_paths.sysvinit_path))
-                return 0;
-
-        if ((t = UNIT(s)->id))
-                if ((r = service_load_sysv_name(s, t)) < 0)
-                        return r;
-
-        if (UNIT(s)->load_state == UNIT_STUB)
-                SET_FOREACH(t, UNIT(s)->names, i) {
-                        if (t == UNIT(s)->id)
-                                continue;
-
-                        if ((r = service_load_sysv_name(s, t)) < 0)
-                                return r;
-
-                        if (UNIT(s)->load_state != UNIT_STUB)
-                                break;
-                }
-
-        return 0;
-}
-#endif
-
 static int service_verify(Service *s) {
         assert(s);
 
@@ -1179,15 +406,6 @@ static int service_load(Unit *u) {
         if (r < 0)
                 return r;
 
-#ifdef HAVE_SYSV_COMPAT
-        /* Load a classic init script as a fallback, if we couldn't find anything */
-        if (u->load_state == UNIT_STUB) {
-                r = service_load_sysv(s);
-                if (r < 0)
-                        return r;
-        }
-#endif
-
         /* Still nothing found? Then let's give up */
         if (u->load_state == UNIT_STUB)
                 return -ENOENT;
@@ -1222,12 +440,6 @@ static int service_load(Unit *u) {
                 if (r < 0)
                         return r;
 
-#ifdef HAVE_SYSV_COMPAT
-                r = sysv_fix_order(s);
-                if (r < 0)
-                        return r;
-#endif
-
                 if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE)
                         s->notify_access = NOTIFY_MAIN;
 
@@ -1243,6 +455,7 @@ static int service_load(Unit *u) {
                 if (u->default_dependencies) {
                         r = service_add_default_dependencies(s);
                         if (r < 0)
+
                                 return r;
                 }
         }
@@ -1325,21 +538,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
         }
 
 #ifdef HAVE_SYSV_COMPAT
-        if (s->is_sysv)
-                fprintf(f,
-                        "%sSysV Init Script has LSB Header: %s\n"
-                        "%sSysVEnabled: %s\n",
-                        prefix, yes_no(s->sysv_has_lsb),
-                        prefix, yes_no(s->sysv_enabled));
-
         if (s->sysv_start_priority >= 0)
                 fprintf(f,
                         "%sSysVStartPriority: %i\n",
                         prefix, s->sysv_start_priority);
-
-        if (s->sysv_runlevels)
-                fprintf(f, "%sSysVRunLevels: %s\n",
-                        prefix, s->sysv_runlevels);
 #endif
 
         if (s->status_text)
@@ -2785,11 +1987,6 @@ static bool service_check_gc(Unit *u) {
             control_pid_good(s) > 0)
                 return true;
 
-#ifdef HAVE_SYSV_COMPAT
-        if (s->is_sysv)
-                return true;
-#endif
-
         return false;
 }
 
@@ -3469,176 +2666,6 @@ static int service_get_timeout(Unit *u, uint64_t *timeout) {
         return 1;
 }
 
-#ifdef HAVE_SYSV_COMPAT
-
-static int service_enumerate(Manager *m) {
-        char **p;
-        unsigned i;
-        _cleanup_closedir_ DIR *d = NULL;
-        _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
-        Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
-        _cleanup_set_free_ Set *shutdown_services = NULL;
-        Unit *service;
-        Iterator j;
-        int r;
-
-        assert(m);
-
-        if (m->running_as != SYSTEMD_SYSTEM)
-                return 0;
-
-        STRV_FOREACH(p, m->lookup_paths.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;
-                                }
-
-                                r = manager_load_unit_prepare(m, name, NULL, NULL, &service);
-                                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(service)->sysv_start_priority_from_rcnd =
-                                                        MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd);
-
-                                                SERVICE(service)->sysv_enabled = true;
-                                        }
-
-                                        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;
-                                }
-                        }
-                }
-
-        /* Now we loaded all stubs and are aware of the lowest
-        start-up priority for all services, not let's actually load
-        the services, this will also tell us which services are
-        actually native now */
-        manager_dispatch_load_queue(m);
-
-        /* If this is a native service, rely on native ways to pull in
-         * a service, don't pull it in via sysv rcN.d links. */
-        for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
-                SET_FOREACH(service, runlevel_services[i], j) {
-                        service = unit_follow_merge(service);
-
-                        if (service->fragment_path)
-                                continue;
-
-                        r = unit_add_two_dependencies_by_name_inverse(
-                                service, UNIT_AFTER, UNIT_WANTS,
-                                rcnd_table[i].target, NULL, true);
-                        if (r < 0)
-                                goto finish;
-                }
-
-        /* We honour K links only for halt/reboot. For the normal
-         * 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. */
-        SET_FOREACH(service, shutdown_services, j) {
-                service = unit_follow_merge(service);
-
-                if (service->fragment_path)
-                        continue;
-
-                r = unit_add_two_dependencies_by_name(
-                        service, UNIT_BEFORE, UNIT_CONFLICTS,
-                        SPECIAL_SHUTDOWN_TARGET, NULL, true);
-                if (r < 0)
-                        goto finish;
-        }
-
-        r = 0;
-
-finish:
-
-        for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
-                set_free(runlevel_services[i]);
-
-        return r;
-}
-#endif
-
 static void service_bus_name_owner_change(
                 Unit *u,
                 const char *name,
@@ -3902,11 +2929,6 @@ const UnitVTable service_vtable = {
         .bus_commit_properties = bus_service_commit_properties,
 
         .get_timeout = service_get_timeout,
-
-#ifdef HAVE_SYSV_COMPAT
-        .enumerate = service_enumerate,
-#endif
-
         .can_transient = true,
 
         .status_message_formats = {
diff --git a/src/core/service.h b/src/core/service.h
index 8c2c819..b8f0e0c 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -175,13 +175,7 @@ struct Service {
         bool forbid_restart:1;
         bool start_timeout_defined:1;
 #ifdef HAVE_SYSV_COMPAT
-        bool is_sysv:1;
-        bool sysv_has_lsb:1;
-        bool sysv_enabled:1;
-        int sysv_start_priority_from_rcnd;
         int sysv_start_priority;
-
-        char *sysv_runlevels;
 #endif
 
         char *bus_name;
diff --git a/src/core/socket.c b/src/core/socket.c
index 4fb4b99..94eb2e7 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -209,13 +209,6 @@ static int socket_instantiate_service(Socket *s) {
         if (r < 0)
                 return r;
 
-#ifdef HAVE_SYSV_COMPAT
-        if (SERVICE(u)->is_sysv) {
-                log_error("Using SysV services for socket activation is not supported. Refusing.");
-                return -ENOENT;
-        }
-#endif
-
         u->no_gc = true;
         unit_ref_set(&s->service, u);
 
@@ -1870,13 +1863,6 @@ static int socket_start(Unit *u) {
                         log_error_unit(u->id, "Socket service %s already active, refusing.", UNIT(service)->id);
                         return -EBUSY;
                 }
-
-#ifdef HAVE_SYSV_COMPAT
-                if (service->is_sysv) {
-                        log_error_unit(u->id, "Using SysV services for socket activation is not supported. Refusing.");
-                        return -ENOENT;
-                }
-#endif
         }
 
         assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);

commit 95ed3294c632f5606327149f10cef1eb34422862
Author: Thomas Hindoe Paaboel Andersen <phomes at gmail.com>
Date:   Fri May 23 00:37:39 2014 +0200

    Move handling of sysv initscripts to a generator
    
    Reuses logic from service.c and the rc-local generator.
    
    Note that this drops reading of chkconfig entirely. 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.
    
    The generator does not know about non-generated units with a value set with
    SysVStartPriority=. These are therefor not taken into account when converting
    start priority to before/after.

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 8d5082f..ee4ced3 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
 
@@ -1915,6 +1916,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..0b8d8f7
--- /dev/null
+++ b/src/sysv-generator/sysv-generator.c
@@ -0,0 +1,919 @@
+/*-*- 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 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;
+                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, " ");
+        if (!before)
+                return log_oom();
+
+        after = strv_join(s->after, " ");
+        if (!after)
+                return log_oom();
+
+        conflicts = strv_join(s->conflicts, " ");
+        if (!conflicts)
+                return log_oom();
+
+        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"
+                "RemainAfterExit=%s\n",
+                yes_no(!s->pid_file));
+
+        if (s->sysv_start_priority > 0)
+                fprintf(f, "SysVStartPriority=%d\n", s->sysv_start_priority);
+
+        if (s->pid_file)
+                fprintf(f, "PidFile=%s\n", s->pid_file);
+
+        fprintf(f,
+                "ExecStart=%s start\n"
+                "ExecStop=%s stop\n",
+                s->path, s->path);
+
+        if (s->reload)
+                fprintf(f, "ExecReload=%s reload\n", s->path);
+
+        STRV_FOREACH(p, s->wants) {
+                r = add_symlink(s->name, *p);
+                if (r < 0)
+                        log_error_unit(s->name, "Failed to create 'Wants' symlink to %s: %s", *p, strerror(-r));
+        }
+
+        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) {
+        _cleanup_fclose_ FILE *f;
+        unsigned line = 0;
+        int r;
+        enum {
+                NORMAL,
+                DESCRIPTION,
+                LSB,
+                LSB_DESCRIPTION,
+                USAGE_CONTINUATION
+        } state = NORMAL;
+        _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
+        char *description;
+        bool supports_reload = false;
+
+        assert(s);
+
+        f = fopen(s->path, "re");
+        if (!f)
+                return errno == ENOENT ? 0 : -errno;
+
+        while (!feof(f)) {
+                char l[LINE_MAX], *t;
+
+                if (!fgets(l, sizeof(l), f)) {
+                        if (feof(f))
+                                break;
+
+                        log_error_unit(s->name,
+                                       "Failed to read configuration file '%s': %m",
+                                       s->path);
+                        return -errno;
+                }
+
+                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;
+                                }
+
+                                j = strstrip(t+12);
+                                if (j && *j) {
+                                        d = strdup(j);
+                                        if (!d)
+                                                return -ENOMEM;
+                                } 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;
+                                }
+
+                                fn = strdup(fn);
+                                if (!fn)
+                                        return -ENOMEM;
+
+                                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;
+
+                        j = strstrip(t);
+                        if (j && *j) {
+                                char *d = NULL;
+
+                                if (chkconfig_description)
+                                        d = strjoin(chkconfig_description, " ", j, NULL);
+                                else
+                                        d = strdup(j);
+
+                                if (!d)
+                                        return -ENOMEM;
+
+                                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) {
+                                        _cleanup_free_ char *n = NULL, *m = NULL;
+
+                                        n = strndup(w, z);
+                                        if (!n)
+                                                return -ENOMEM;
+
+                                        r = sysv_translate_facility(n, basename(s->path), &m);
+
+                                        if (r < 0)
+                                                return r;
+
+                                        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! */
+                                                r = strv_extend(&s->before, m);
+                                                if (r < 0)
+                                                        return log_oom();
+                                                r = strv_extend(&s->wants, m);
+                                                if (r < 0)
+                                                        return log_oom();
+                                        }
+
+                                        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));
+                                }
+
+                        } 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) {
+                                        _cleanup_free_ char *n = NULL, *m = NULL;
+                                        bool is_before;
+
+                                        n = strndup(w, z);
+                                        if (!n)
+                                                return -ENOMEM;
+
+                                        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));
+                                                continue;
+                                        }
+
+                                        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 */
+                                                r = strv_extend(&s->after, m);
+                                                if (r < 0)
+                                                        return log_oom();
+                                                r = strv_extend(&s->wants, m);
+                                                if (r < 0)
+                                                        return log_oom();
+                                        }
+                                        else {
+                                                if (is_before) {
+                                                        r = strv_extend(&s->before, m);
+                                                        if (r < 0)
+                                                                return log_oom();
+                                                }
+                                                else {
+                                                        r = strv_extend(&s->after, m);
+                                                        if (r < 0)
+                                                                return log_oom();
+                                                }
+                                        }
+
+                                        if (r < 0)
+                                                log_error_unit(s->name,
+                                                               "[%s:%u] Failed to add dependency on %s, ignoring: %s",
+                                                               s->path, line, m, strerror(-r));
+                                }
+
+                        } else if (startswith_no_case(t, "Description:")) {
+                                char *d, *j;
+
+                                state = LSB_DESCRIPTION;
+
+                                j = strstrip(t+12);
+                                if (j && *j) {
+                                        d = strdup(j);
+                                        if (!d)
+                                                return -ENOMEM;
+                                } else
+                                        d = NULL;
+
+                                free(long_description);
+                                long_description = d;
+
+                        } else if (startswith_no_case(t, "Short-Description:")) {
+                                char *d, *j;
+
+                                state = LSB;
+
+                                j = strstrip(t+18);
+                                if (j && *j) {
+                                        d = strdup(j);
+                                        if (!d)
+                                                return -ENOMEM;
+                                } else
+                                        d = NULL;
+
+                                free(short_description);
+                                short_description = d;
+
+                        } else if (state == LSB_DESCRIPTION) {
+
+                                if (startswith(l, "#\t") || startswith(l, "#  ")) {
+                                        char *j;
+
+                                        j = strstrip(t);
+                                        if (j && *j) {
+                                                char *d = NULL;
+
+                                                if (long_description)
+                                                        d = strjoin(long_description, " ", t, NULL);
+                                                else
+                                                        d = strdup(j);
+
+                                                if (!d)
+                                                        return -ENOMEM;
+
+                                                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;
+
+                d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
+                if (!d)
+                        return -ENOMEM;
+
+                s->description = d;
+        }
+
+        return 0;
+}
+
+static int fix_order(SysvStub *s, Hashmap *all_services) {
+        SysvStub *other;
+        Iterator j;
+        int r;
+
+        assert(s);
+
+        if (s->sysv_start_priority < 0)
+                return 0;
+
+        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) {
+                        r = strv_extend(&s->after, other->name);
+                        if (r < 0)
+                                return log_oom();
+                }
+                else if (other->sysv_start_priority > s->sysv_start_priority) {
+                        r = strv_extend(&s->before, other->name);
+                        if (r < 0)
+                                return log_oom();
+                }
+                else
+                        continue;
+
+                /* FIXME: Maybe we should compare the name here lexicographically? */
+        }
+
+        return 0;
+}
+
+static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
+        char **path;
+
+        STRV_FOREACH(path, lp.sysvinit_path) {
+                _cleanup_closedir_ DIR *d = NULL;
+                struct dirent *de;
+
+                d = opendir(*path);
+                if (!d) {
+                        if (errno != ENOENT)
+                                log_warning("opendir(%s) failed: %m", *path);
+                        continue;
+                }
+
+                while ((de = readdir(d))) {
+                        SysvStub *service;
+                        struct stat st;
+                        _cleanup_free_ char *fpath = NULL, *name = NULL;
+                        int r;
+
+                        if (ignore_file(de->d_name))
+                                continue;
+
+                        fpath = strjoin(*path, "/", de->d_name, NULL);
+                        if (!fpath)
+                                return log_oom();
+
+                        if (stat(fpath, &st) < 0)
+                                continue;
+
+                        if (!(st.st_mode & S_IXUSR))
+                                continue;
+
+                        name = sysv_translate_name(de->d_name);
+                        if (!name)
+                                return log_oom();
+
+                        if (hashmap_contains(all_services, name))
+                                continue;
+
+                        service = new0(SysvStub, 1);
+                        if (!service)
+                                return log_oom();
+
+                        service->sysv_start_priority = -1;
+                        service->name = name;
+                        service->path = fpath;
+
+                        r = hashmap_put(all_services, service->name, service);
+                        if (r < 0)
+                                return log_oom();
+
+                        name = fpath = NULL;
+                }
+        }
+
+        return 0;
+}
+
+static int set_dependencies_from_rcnd(LookupPaths lp, Hashmap *all_services) {
+        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;
+
+        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)
+                                return -ENOMEM;
+
+                        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(*p, "/", de->d_name, NULL);
+                                if (!fpath) {
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                name = sysv_translate_name(de->d_name + 3);
+                                if (!name) {
+                                        r = log_oom();
+                                        goto finish;
+                                }
+
+                                if (hashmap_contains(all_services, name))
+                                        service = hashmap_get(all_services, name);
+                                else {
+                                        log_warning("Could not find init scirpt for %s", name);
+                                        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) {
+                        r = strv_extend(&service->before, rcnd_table[i].target);
+                        if (r < 0)
+                                return log_oom();
+                        r = strv_extend(&service->wants, rcnd_table[i].target);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+        SET_FOREACH(service, shutdown_services, j) {
+                r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
+                if (r < 0)
+                        return log_oom();
+                r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        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;
+        LookupPaths lp;
+        Hashmap *all_services;
+        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[3];
+
+        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;
+        }
+
+        all_services = hashmap_new(string_hash_func, string_compare_func);
+        if (!all_services) {
+                log_oom();
+                return EXIT_FAILURE;
+        }
+
+        r = enumerate_sysv(lp, all_services);
+        if (r < 0) {
+                log_error("Failed to generate units for all init scripts.");
+                return EXIT_FAILURE;
+        }
+
+        r = set_dependencies_from_rcnd(lp, all_services);
+        if (r < 0) {
+                log_error("Failed to read runlevels from rcnd links.");
+                return EXIT_FAILURE;
+        }
+
+        HASHMAP_FOREACH(service, all_services, j) {
+                q = load_sysv(service);
+                if (q < 0)
+                        continue;
+
+                q = fix_order(service, all_services);
+                if (q < 0)
+                        continue;
+
+                q = generate_unit_file(service);
+                if (q < 0)
+                        continue;
+        }
+
+        return EXIT_SUCCESS;
+}



More information about the systemd-commits mailing list