]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/sysv-generator/sysv-generator.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / sysv-generator / sysv-generator.c
index 9392a3d2b56d23f0568e2bc6ea9b28c7a7c0f6b6..5b30baee8422213665c8c2bfc81fd20152816d77 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 
 #include "alloc-util.h"
 #include "dirent-util.h"
+#include "exit-status.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "generator.h"
 #include "hashmap.h"
 #include "hexdecoct.h"
 #include "install.h"
 #include "path-util.h"
 #include "set.h"
 #include "special.h"
+#include "specifier.h"
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "unit-name.h"
 #include "util.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_MULTI_USER_TARGET, RUNLEVEL_UP },
-        { "rc3.d",  SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
-        { "rc4.d",  SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
-        { "rc5.d",  SPECIAL_GRAPHICAL_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 */
+        { "rc1.d",  SPECIAL_RESCUE_TARGET     },
+        { "rc2.d",  SPECIAL_MULTI_USER_TARGET },
+        { "rc3.d",  SPECIAL_MULTI_USER_TARGET },
+        { "rc4.d",  SPECIAL_MULTI_USER_TARGET },
+        { "rc5.d",  SPECIAL_GRAPHICAL_TARGET  },
+
+        /* We ignore the SysV runlevels for shutdown here, as SysV services get default dependencies anyway, and that
+         * means they are shut down anyway at system power off if running. */
 };
 
 static const char *arg_dest = "/tmp";
@@ -82,7 +73,6 @@ typedef struct SysvStub {
         char **after;
         char **wants;
         char **wanted_by;
-        char **conflicts;
         bool has_lsb;
         bool reload;
         bool loaded;
@@ -100,42 +90,13 @@ static void free_sysvstub(SysvStub *s) {
         strv_free(s->after);
         strv_free(s->wants);
         strv_free(s->wanted_by);
-        strv_free(s->conflicts);
         free(s);
 }
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(SysvStub*, free_sysvstub);
 
 static void free_sysvstub_hashmapp(Hashmap **h) {
-        SysvStub *stub;
-
-        while ((stub = hashmap_steal_first(*h)))
-                free_sysvstub(stub);
-
-        hashmap_free(*h);
-}
-
-static int add_symlink(const char *service, const char *where) {
-        const char *from, *to;
-        int r;
-
-        assert(service);
-        assert(where);
-
-        from = strjoina(arg_dest, "/", service);
-        to = strjoina(arg_dest, "/", where, ".wants/", service);
-
-        mkdir_parents_label(to, 0755);
-
-        r = symlink(from, to);
-        if (r < 0) {
-                if (errno == EEXIST)
-                        return 0;
-
-                return -errno;
-        }
-
-        return 1;
+        hashmap_free_with_destructor(*h, free_sysvstub);
 }
 
 static int add_alias(const char *service, const char *alias) {
@@ -159,6 +120,7 @@ static int add_alias(const char *service, const char *alias) {
 }
 
 static int generate_unit_file(SysvStub *s) {
+        _cleanup_free_ char *path_escaped = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         const char *unit;
         char **p;
@@ -169,6 +131,10 @@ static int generate_unit_file(SysvStub *s) {
         if (!s->loaded)
                 return 0;
 
+        path_escaped = specifier_escape(s->path);
+        if (!path_escaped)
+                return log_oom();
+
         unit = strjoina(arg_dest, "/", s->name);
 
         /* We might already have a symlink with the same name from a Provides:,
@@ -188,10 +154,17 @@ static int generate_unit_file(SysvStub *s) {
                 "[Unit]\n"
                 "Documentation=man:systemd-sysv-generator(8)\n"
                 "SourcePath=%s\n",
-                s->path);
+                path_escaped);
 
-        if (s->description)
-                fprintf(f, "Description=%s\n", s->description);
+        if (s->description) {
+                _cleanup_free_ char *t;
+
+                t = specifier_escape(s->description);
+                if (!t)
+                        return log_oom();
+
+                fprintf(f, "Description=%s\n", t);
+        }
 
         STRV_FOREACH(p, s->before)
                 fprintf(f, "Before=%s\n", *p);
@@ -199,8 +172,6 @@ static int generate_unit_file(SysvStub *s) {
                 fprintf(f, "After=%s\n", *p);
         STRV_FOREACH(p, s->wants)
                 fprintf(f, "Wants=%s\n", *p);
-        STRV_FOREACH(p, s->conflicts)
-                fprintf(f, "Conflicts=%s\n", *p);
 
         fprintf(f,
                 "\n[Service]\n"
@@ -213,26 +184,37 @@ static int generate_unit_file(SysvStub *s) {
                 "RemainAfterExit=%s\n",
                 yes_no(!s->pid_file));
 
-        if (s->pid_file)
-                fprintf(f, "PIDFile=%s\n", s->pid_file);
+        if (s->pid_file) {
+                _cleanup_free_ char *t;
+
+                t = specifier_escape(s->pid_file);
+                if (!t)
+                        return log_oom();
+
+                fprintf(f, "PIDFile=%s\n", t);
+        }
+
+        /* Consider two special LSB exit codes a clean exit */
+        if (s->has_lsb)
+                fprintf(f,
+                        "SuccessExitStatus=%i %i\n",
+                        EXIT_NOTINSTALLED,
+                        EXIT_NOTCONFIGURED);
 
         fprintf(f,
                 "ExecStart=%s start\n"
                 "ExecStop=%s stop\n",
-                s->path, s->path);
+                path_escaped, path_escaped);
 
         if (s->reload)
-                fprintf(f, "ExecReload=%s reload\n", s->path);
+                fprintf(f, "ExecReload=%s reload\n", path_escaped);
 
         r = fflush_and_check(f);
         if (r < 0)
                 return log_error_errno(r, "Failed to write unit %s: %m", unit);
 
-        STRV_FOREACH(p, s->wanted_by) {
-                r = add_symlink(s->name, *p);
-                if (r < 0)
-                        log_warning_errno(r, "Failed to create 'Wants' symlink to %s, ignoring: %m", *p);
-        }
+        STRV_FOREACH(p, s->wanted_by)
+                (void) generator_add_symlink(arg_dest, *p, "wants", s->name);
 
         return 1;
 }
@@ -258,13 +240,13 @@ static char *sysv_translate_name(const char *name) {
         if (res)
                 *res = 0;
 
-        if (unit_name_mangle(c, UNIT_NAME_NOGLOB, &res) < 0)
+        if (unit_name_mangle(c, 0, &res) < 0)
                 return NULL;
 
         return res;
 }
 
-static int sysv_translate_facility(const char *name, const char *filename, char **ret) {
+static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, char **ret) {
 
         /* We silently ignore the $ prefix here. According to the LSB
          * spec it simply indicates whether something is a
@@ -283,23 +265,28 @@ static int sysv_translate_facility(const char *name, const char *filename, char
                 "time",                 SPECIAL_TIME_SYNC_TARGET,
         };
 
+        const char *filename;
         char *filename_no_sh, *e, *m;
         const char *n;
         unsigned i;
         int r;
 
         assert(name);
-        assert(filename);
+        assert(s);
         assert(ret);
 
+        filename = basename(s->path);
+
         n = *name == '$' ? name + 1 : name;
 
         for (i = 0; i < ELEMENTSOF(table); i += 2) {
                 if (!streq(table[i], n))
                         continue;
 
-                if (!table[i+1])
+                if (!table[i+1]) {
+                        *ret = NULL;
                         return 0;
+                }
 
                 m = strdup(table[i+1]);
                 if (!m)
@@ -316,9 +303,9 @@ static int sysv_translate_facility(const char *name, const char *filename, char
         if (*name == '$')  {
                 r = unit_name_build(n, NULL, ".target", ret);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to build name: %m");
+                        return log_error_errno(r, "[%s:%u] Could not build name for facility %s: %m", s->path, line, name);
 
-                return r;
+                return 1;
         }
 
         /* Strip ".sh" suffix from file name for comparison */
@@ -330,8 +317,10 @@ static int sysv_translate_facility(const char *name, const char *filename, char
         }
 
         /* Names equaling the file name of the services are redundant */
-        if (streq_ptr(n, filename))
+        if (streq_ptr(n, filename)) {
+                *ret = NULL;
                 return 0;
+        }
 
         /* Everything else we assume to be normal service names */
         m = sysv_translate_name(n);
@@ -354,11 +343,11 @@ static int handle_provides(SysvStub *s, unsigned line, const char *full_text, co
 
                 r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to parse word from provides string: %m");
+                        return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
                 if (r == 0)
                         break;
 
-                r = sysv_translate_facility(word, basename(s->path), &m);
+                r = sysv_translate_facility(s, line, word, &m);
                 if (r <= 0) /* continue on error */
                         continue;
 
@@ -391,6 +380,9 @@ static int handle_provides(SysvStub *s, unsigned line, const char *full_text, co
                                 r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
                                 if (r < 0)
                                         return log_oom();
+                                r = strv_extend(&s->wants, SPECIAL_NETWORK_TARGET);
+                                if (r < 0)
+                                        return log_oom();
                         }
 
                         break;
@@ -420,11 +412,11 @@ static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text
 
                 r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to parse word from provides string: %m");
+                        return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
                 if (r == 0)
                         break;
 
-                r = sysv_translate_facility(word, basename(s->path), &m);
+                r = sysv_translate_facility(s, line, word, &m);
                 if (r <= 0) /* continue on error */
                         continue;
 
@@ -504,7 +496,7 @@ static int load_sysv(SysvStub *s) {
                         continue;
                 }
 
-                if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) {
+                if (IN_SET(state, LSB_DESCRIPTION, LSB) && streq(t, "### END INIT INFO")) {
                         state = NORMAL;
                         continue;
                 }
@@ -527,9 +519,7 @@ static int load_sysv(SysvStub *s) {
                                         t[k-1] = 0;
                                 }
 
-                                j = strstrip(t+12);
-                                if (isempty(j))
-                                        j = NULL;
+                                j = empty_to_null(strstrip(t+12));
 
                                 r = free_and_strdup(&chkconfig_description, j);
                                 if (r < 0)
@@ -570,7 +560,7 @@ static int load_sysv(SysvStub *s) {
                                 char *d = NULL;
 
                                 if (chkconfig_description)
-                                        d = strjoin(chkconfig_description, " ", j, NULL);
+                                        d = strjoin(chkconfig_description, " ", j);
                                 else
                                         d = strdup(j);
                                 if (!d)
@@ -580,7 +570,7 @@ static int load_sysv(SysvStub *s) {
                                 chkconfig_description = d;
                         }
 
-                } else if (state == LSB || state == LSB_DESCRIPTION) {
+                } else if (IN_SET(state, LSB, LSB_DESCRIPTION)) {
 
                         if (startswith_no_case(t, "Provides:")) {
                                 state = LSB;
@@ -605,9 +595,7 @@ static int load_sysv(SysvStub *s) {
 
                                 state = LSB_DESCRIPTION;
 
-                                j = strstrip(t+12);
-                                if (isempty(j))
-                                        j = NULL;
+                                j = empty_to_null(strstrip(t+12));
 
                                 r = free_and_strdup(&long_description, j);
                                 if (r < 0)
@@ -618,9 +606,7 @@ static int load_sysv(SysvStub *s) {
 
                                 state = LSB;
 
-                                j = strstrip(t+18);
-                                if (isempty(j))
-                                        j = NULL;
+                                j = empty_to_null(strstrip(t+18));
 
                                 r = free_and_strdup(&short_description, j);
                                 if (r < 0)
@@ -636,7 +622,7 @@ static int load_sysv(SysvStub *s) {
                                                 char *d = NULL;
 
                                                 if (long_description)
-                                                        d = strjoin(long_description, " ", t, NULL);
+                                                        d = strjoin(long_description, " ", t);
                                                 else
                                                         d = strdup(j);
                                                 if (!d)
@@ -755,8 +741,7 @@ static int acquire_search_path(const char *def, const char *envvar, char ***ret)
         if (!path_strv_resolve_uniq(l, NULL))
                 return log_oom();
 
-        *ret = l;
-        l = NULL;
+        *ret = TAKE_PTR(l);
 
         return 0;
 }
@@ -807,7 +792,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
                                 continue;
 
                         r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name);
-                        if (r < 0 && !IN_SET(r, -ELOOP, -ESHUTDOWN, -EADDRNOTAVAIL)) {
+                        if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) {
                                 log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
                                 continue;
                         } else if (r != 0) {
@@ -815,7 +800,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
                                 continue;
                         }
 
-                        fpath = strjoin(*path, "/", de->d_name, NULL);
+                        fpath = strjoin(*path, "/", de->d_name);
                         if (!fpath)
                                 return log_oom();
 
@@ -824,9 +809,8 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
                                 return log_oom();
 
                         service->sysv_start_priority = -1;
-                        service->name = name;
-                        service->path = fpath;
-                        name = fpath = NULL;
+                        service->name = TAKE_PTR(name);
+                        service->path = TAKE_PTR(fpath);
 
                         r = hashmap_put(all_services, service->name, service);
                         if (r < 0)
@@ -841,7 +825,6 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
 
 static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
         Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
-        _cleanup_set_free_ Set *shutdown_services = NULL;
         _cleanup_strv_free_ char **sysvrcnd_path = NULL;
         SysvStub *service;
         unsigned i;
@@ -862,7 +845,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                         _cleanup_free_ char *path = NULL;
                         struct dirent *de;
 
-                        path = strjoin(*p, "/", rcnd_table[i].path, NULL);
+                        path = strjoin(*p, "/", rcnd_table[i].path);
                         if (!path) {
                                 r = log_oom();
                                 goto finish;
@@ -880,7 +863,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                                 _cleanup_free_ char *name = NULL, *fpath = NULL;
                                 int a, b;
 
-                                if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
+                                if (de->d_name[0] != 'S')
                                         continue;
 
                                 if (strlen(de->d_name) < 4)
@@ -892,7 +875,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                                 if (a < 0 || b < 0)
                                         continue;
 
-                                fpath = strjoin(*p, "/", de->d_name, NULL);
+                                fpath = strjoin(*p, "/", de->d_name);
                                 if (!fpath) {
                                         r = log_oom();
                                         goto finish;
@@ -910,43 +893,23 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                                         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], NULL);
-                                        if (r < 0) {
-                                                log_oom();
-                                                goto finish;
-                                        }
-
-                                        r = set_put(runlevel_services[i], service);
-                                        if (r < 0) {
-                                                log_oom();
-                                                goto finish;
-                                        }
-
-                                } else if (de->d_name[0] == 'K' &&
-                                           (rcnd_table[i].type == RUNLEVEL_DOWN)) {
+                                service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
 
-                                        r = set_ensure_allocated(&shutdown_services, NULL);
-                                        if (r < 0) {
-                                                log_oom();
-                                                goto finish;
-                                        }
+                                r = set_ensure_allocated(&runlevel_services[i], NULL);
+                                if (r < 0) {
+                                        log_oom();
+                                        goto finish;
+                                }
 
-                                        r = set_put(shutdown_services, service);
-                                        if (r < 0) {
-                                                log_oom();
-                                                goto finish;
-                                        }
+                                r = set_put(runlevel_services[i], service);
+                                if (r < 0) {
+                                        log_oom();
+                                        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);
@@ -961,19 +924,6 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                         }
                 }
 
-        SET_FOREACH(service, shutdown_services, j) {
-                r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
-                if (r < 0) {
-                        log_oom();
-                        goto finish;
-                }
-                r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
-                if (r < 0) {
-                        log_oom();
-                        goto finish;
-                }
-        }
-
         r = 0;
 
 finish:
@@ -998,7 +948,8 @@ int main(int argc, char *argv[]) {
         if (argc > 1)
                 arg_dest = argv[3];
 
-        log_set_target(LOG_TARGET_SAFE);
+        log_set_prohibit_ipc(true);
+        log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
         log_open();