[systemd-devel] [PATCH] [RFC] Move handling of sysv initscripts to a generator

Thomas H.P. Andersen phomes at gmail.com
Thu May 29 07:11:25 PDT 2014


On Wed, May 28, 2014 at 3:38 AM, Zbigniew Jędrzejewski-Szmek
<zbyszek at in.waw.pl> wrote:
> On Wed, May 28, 2014 at 01:12:23AM +0200, Thomas H.P. Andersen wrote:
>> From: Thomas Hindoe Paaboel Andersen <phomes at 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.

I cleaned up the code for yours and Peeters comments. Units generated
from initscripts no longer take precedence over native units.

Ripping out the old code was harder than I thought though. The Service
struct contains a few sysv-specific fields that were filled directly
when the initscripts were parsed. Only one of them can be set from a
.service file. If we want to keep them all then we need to add options
for them in the .service file. IMO all but one are irrelevant with the
generator. The relevant one left is is_sysv. It is used to prevent the
sysv unit from garbage collection and to refuse socket activation for
sysv services. The remaining fields (sysv_has_lsb, sysv_enabled,
sysv_start_priority_from_rcnd, sysv_start_priority, sysv_runlevels)
are now only used in service_dump and I cannot see any code in systemd
making use of the dumped values.

Would it be acceptable to just drop the fields only used in service_dump?
Is it okay to add a "SysV=true/false" option to .service?
What about the "SysVStartPriority=" option set in native .service's?
The generator does not know about non-generated units with such a
value set so it cannot take them into account when converting start
priority to before/after. Should the manager itself try to reorder all
units with a sysv priority later?

>> 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
>


More information about the systemd-devel mailing list