LCOV - code coverage report
Current view: top level - sysv-generator - sysv-generator.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 358 515 69.5 %
Date: 2015-07-29 18:47:03 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2014 Thomas H.P. Andersen
       7             :   Copyright 2010 Lennart Poettering
       8             :   Copyright 2011 Michal Schmidt
       9             : 
      10             :   systemd is free software; you can redistribute it and/or modify it
      11             :   under the terms of the GNU Lesser General Public License as published by
      12             :   the Free Software Foundation; either version 2.1 of the License, or
      13             :   (at your option) any later version.
      14             : 
      15             :   systemd is distributed in the hope that it will be useful, but
      16             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      18             :   Lesser General Public License for more details.
      19             : 
      20             :   You should have received a copy of the GNU Lesser General Public License
      21             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      22             : ***/
      23             : 
      24             : #include <errno.h>
      25             : #include <stdio.h>
      26             : #include <unistd.h>
      27             : 
      28             : #include "util.h"
      29             : #include "mkdir.h"
      30             : #include "strv.h"
      31             : #include "path-util.h"
      32             : #include "path-lookup.h"
      33             : #include "log.h"
      34             : #include "unit-name.h"
      35             : #include "special.h"
      36             : #include "hashmap.h"
      37             : #include "set.h"
      38             : #include "install.h"
      39             : 
      40             : typedef enum RunlevelType {
      41             :         RUNLEVEL_UP,
      42             :         RUNLEVEL_DOWN
      43             : } RunlevelType;
      44             : 
      45             : static const struct {
      46             :         const char *path;
      47             :         const char *target;
      48             :         const RunlevelType type;
      49             : } rcnd_table[] = {
      50             :         /* Standard SysV runlevels for start-up */
      51             :         { "rc1.d",  SPECIAL_RESCUE_TARGET,     RUNLEVEL_UP },
      52             :         { "rc2.d",  SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
      53             :         { "rc3.d",  SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
      54             :         { "rc4.d",  SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
      55             :         { "rc5.d",  SPECIAL_GRAPHICAL_TARGET,  RUNLEVEL_UP },
      56             : 
      57             :         /* Standard SysV runlevels for shutdown */
      58             :         { "rc0.d",  SPECIAL_POWEROFF_TARGET,  RUNLEVEL_DOWN },
      59             :         { "rc6.d",  SPECIAL_REBOOT_TARGET,    RUNLEVEL_DOWN }
      60             : 
      61             :         /* Note that the order here matters, as we read the
      62             :            directories in this order, and we want to make sure that
      63             :            sysv_start_priority is known when we first load the
      64             :            unit. And that value we only know from S links. Hence
      65             :            UP must be read before DOWN */
      66             : };
      67             : 
      68             : const char *arg_dest = "/tmp";
      69             : 
      70             : typedef struct SysvStub {
      71             :         char *name;
      72             :         char *path;
      73             :         char *description;
      74             :         int sysv_start_priority;
      75             :         char *pid_file;
      76             :         char **before;
      77             :         char **after;
      78             :         char **wants;
      79             :         char **wanted_by;
      80             :         char **conflicts;
      81             :         bool has_lsb;
      82             :         bool reload;
      83             : } SysvStub;
      84             : 
      85          25 : static void free_sysvstub(SysvStub *s) {
      86          25 :         free(s->name);
      87          25 :         free(s->path);
      88          25 :         free(s->description);
      89          25 :         free(s->pid_file);
      90          25 :         strv_free(s->before);
      91          25 :         strv_free(s->after);
      92          25 :         strv_free(s->wants);
      93          25 :         strv_free(s->wanted_by);
      94          25 :         strv_free(s->conflicts);
      95          25 :         free(s);
      96          25 : }
      97             : 
      98          69 : DEFINE_TRIVIAL_CLEANUP_FUNC(SysvStub*, free_sysvstub);
      99             : 
     100          19 : static void free_sysvstub_hashmapp(Hashmap **h) {
     101             :         SysvStub *stub;
     102             : 
     103          63 :         while ((stub = hashmap_steal_first(*h)))
     104          25 :                 free_sysvstub(stub);
     105             : 
     106          19 :         hashmap_free(*h);
     107          19 : }
     108             : 
     109          52 : static int add_symlink(const char *service, const char *where) {
     110         104 :         _cleanup_free_ char *from = NULL, *to = NULL;
     111             :         int r;
     112             : 
     113          52 :         assert(service);
     114          52 :         assert(where);
     115             : 
     116          52 :         from = strjoin(arg_dest, "/", service, NULL);
     117          52 :         if (!from)
     118           0 :                 return log_oom();
     119             : 
     120          52 :         to = strjoin(arg_dest, "/", where, ".wants/", service, NULL);
     121          52 :         if (!to)
     122           0 :                 return log_oom();
     123             : 
     124          52 :         mkdir_parents_label(to, 0755);
     125             : 
     126          52 :         r = symlink(from, to);
     127          52 :         if (r < 0) {
     128          25 :                 if (errno == EEXIST)
     129          25 :                         return 0;
     130           0 :                 return -errno;
     131             :         }
     132             : 
     133          27 :         return 1;
     134             : }
     135             : 
     136           9 : static int add_alias(const char *service, const char *alias) {
     137          18 :         _cleanup_free_ char *link = NULL;
     138             :         int r;
     139             : 
     140           9 :         assert(service);
     141           9 :         assert(alias);
     142             : 
     143           9 :         link = strjoin(arg_dest, "/", alias, NULL);
     144           9 :         if (!link)
     145           0 :                 return log_oom();
     146             : 
     147           9 :         r = symlink(service, link);
     148           9 :         if (r < 0) {
     149           2 :                 if (errno == EEXIST)
     150           2 :                         return 0;
     151           0 :                 return -errno;
     152             :         }
     153             : 
     154           7 :         return 1;
     155             : }
     156             : 
     157          25 : static int generate_unit_file(SysvStub *s) {
     158             :         char **p;
     159          50 :         _cleanup_fclose_ FILE *f = NULL;
     160          50 :         _cleanup_free_ char *unit = NULL,
     161          50 :                 *before = NULL, *after = NULL,
     162          50 :                 *wants = NULL, *conflicts = NULL;
     163             :         int r;
     164             : 
     165          25 :         before = strv_join(s->before, " ");
     166          25 :         after = strv_join(s->after, " ");
     167          25 :         wants = strv_join(s->wants, " ");
     168          25 :         conflicts = strv_join(s->conflicts, " ");
     169          25 :         unit = strjoin(arg_dest, "/", s->name, NULL);
     170          25 :         if (!before || !after || !wants || !conflicts || !unit)
     171           0 :                 return log_oom();
     172             : 
     173             :         /* We might already have a symlink with the same name from a Provides:,
     174             :          * or from backup files like /etc/init.d/foo.bak. Real scripts always win,
     175             :          * so remove an existing link */
     176          25 :         if (is_symlink(unit) > 0) {
     177           2 :                 log_warning("Overwriting existing symlink %s with real service", unit);
     178           2 :                 (void) unlink(unit);
     179             :         }
     180             : 
     181          25 :         f = fopen(unit, "wxe");
     182          25 :         if (!f)
     183           0 :                 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
     184             : 
     185          25 :         fprintf(f,
     186             :                 "# Automatically generated by systemd-sysv-generator\n\n"
     187             :                 "[Unit]\n"
     188             :                 "Documentation=man:systemd-sysv-generator(8)\n"
     189             :                 "SourcePath=%s\n"
     190             :                 "Description=%s\n",
     191             :                 s->path, s->description);
     192             : 
     193          25 :         if (!isempty(before))
     194          15 :                 fprintf(f, "Before=%s\n", before);
     195          25 :         if (!isempty(after))
     196           4 :                 fprintf(f, "After=%s\n", after);
     197          25 :         if (!isempty(wants))
     198           1 :                 fprintf(f, "Wants=%s\n", wants);
     199          25 :         if (!isempty(conflicts))
     200          13 :                 fprintf(f, "Conflicts=%s\n", conflicts);
     201             : 
     202          25 :         fprintf(f,
     203             :                 "\n[Service]\n"
     204             :                 "Type=forking\n"
     205             :                 "Restart=no\n"
     206             :                 "TimeoutSec=5min\n"
     207             :                 "IgnoreSIGPIPE=no\n"
     208             :                 "KillMode=process\n"
     209             :                 "GuessMainPID=no\n"
     210             :                 "RemainAfterExit=%s\n",
     211          25 :                 yes_no(!s->pid_file));
     212             : 
     213          25 :         if (s->pid_file)
     214           0 :                 fprintf(f, "PIDFile=%s\n", s->pid_file);
     215             : 
     216          25 :         fprintf(f,
     217             :                 "ExecStart=%s start\n"
     218             :                 "ExecStop=%s stop\n",
     219             :                 s->path, s->path);
     220             : 
     221          25 :         if (s->reload)
     222           0 :                 fprintf(f, "ExecReload=%s reload\n", s->path);
     223             : 
     224          77 :         STRV_FOREACH(p, s->wanted_by) {
     225          52 :                 r = add_symlink(s->name, *p);
     226          52 :                 if (r < 0)
     227           0 :                         log_error_errno(r, "Failed to create 'Wants' symlink to %s: %m", *p);
     228             :         }
     229             : 
     230          25 :         return 0;
     231             : }
     232             : 
     233           0 : static bool usage_contains_reload(const char *line) {
     234           0 :         return (strcasestr(line, "{reload|") ||
     235           0 :                 strcasestr(line, "{reload}") ||
     236           0 :                 strcasestr(line, "{reload\"") ||
     237           0 :                 strcasestr(line, "|reload|") ||
     238           0 :                 strcasestr(line, "|reload}") ||
     239           0 :                 strcasestr(line, "|reload\""));
     240             : }
     241             : 
     242         137 : static char *sysv_translate_name(const char *name) {
     243         274 :         _cleanup_free_ char *c = NULL;
     244             :         char *res;
     245             : 
     246         137 :         c = strdup(name);
     247         137 :         if (!c)
     248           0 :                 return NULL;
     249             : 
     250         137 :         res = endswith(c, ".sh");
     251         137 :         if (res)
     252          17 :                 *res = 0;
     253             : 
     254         137 :         if (unit_name_mangle(c, UNIT_NAME_NOGLOB, &res) < 0)
     255           0 :                 return NULL;
     256             : 
     257         137 :         return res;
     258             : }
     259             : 
     260          57 : static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
     261             : 
     262             :         /* We silently ignore the $ prefix here. According to the LSB
     263             :          * spec it simply indicates whether something is a
     264             :          * standardized name or a distribution-specific one. Since we
     265             :          * just follow what already exists and do not introduce new
     266             :          * uses or names we don't care who introduced a new name. */
     267             : 
     268             :         static const char * const table[] = {
     269             :                 /* LSB defined facilities */
     270             :                 "local_fs",             NULL,
     271             :                 "network",              SPECIAL_NETWORK_ONLINE_TARGET,
     272             :                 "named",                SPECIAL_NSS_LOOKUP_TARGET,
     273             :                 "portmap",              SPECIAL_RPCBIND_TARGET,
     274             :                 "remote_fs",            SPECIAL_REMOTE_FS_TARGET,
     275             :                 "syslog",               NULL,
     276             :                 "time",                 SPECIAL_TIME_SYNC_TARGET,
     277             :         };
     278             : 
     279             :         char *filename_no_sh, *e, *r;
     280             :         const char *n;
     281             :         unsigned i;
     282             : 
     283          57 :         assert(name);
     284          57 :         assert(_r);
     285             : 
     286          57 :         n = *name == '$' ? name + 1 : name;
     287             : 
     288         602 :         for (i = 0; i < ELEMENTSOF(table); i += 2) {
     289             : 
     290         267 :                 if (!streq(table[i], n))
     291         244 :                         continue;
     292             : 
     293          23 :                 if (!table[i+1])
     294          20 :                         return 0;
     295             : 
     296           3 :                 r = strdup(table[i+1]);
     297           3 :                 if (!r)
     298           0 :                         return log_oom();
     299             : 
     300           3 :                 goto finish;
     301             :         }
     302             : 
     303             :         /* strip ".sh" suffix from file name for comparison */
     304          34 :         filename_no_sh = strdupa(filename);
     305          34 :         e = endswith(filename_no_sh, ".sh");
     306          34 :         if (e) {
     307           3 :                 *e = '\0';
     308           3 :                 filename = filename_no_sh;
     309             :         }
     310             : 
     311             :         /* If we don't know this name, fallback heuristics to figure
     312             :          * out whether something is a target or a service alias. */
     313             : 
     314          34 :         if (*name == '$') {
     315             :                 int k;
     316             : 
     317             :                 /* Facilities starting with $ are most likely targets */
     318           0 :                 k = unit_name_build(n, NULL, ".target", &r);
     319           0 :                 if (k < 0)
     320           0 :                         return k;
     321             : 
     322          34 :         } else if (streq_ptr(n, filename))
     323             :                 /* Names equaling the file name of the services are redundant */
     324          21 :                 return 0;
     325             :         else
     326             :                 /* Everything else we assume to be normal service names */
     327          13 :                 r = sysv_translate_name(n);
     328          13 :         if (!r)
     329           0 :                 return -ENOMEM;
     330             : 
     331             : finish:
     332          16 :         *_r = r;
     333             : 
     334          16 :         return 1;
     335             : }
     336             : 
     337          23 : static int handle_provides(SysvStub *s, unsigned line, const char *full_text, const char *text) {
     338             :         const char *word, *state_;
     339             :         size_t z;
     340             :         int r;
     341             : 
     342          53 :         FOREACH_WORD_QUOTED(word, z, text, state_) {
     343          60 :                 _cleanup_free_ char *n = NULL, *m = NULL;
     344             :                 UnitType t;
     345             : 
     346          30 :                 n = strndup(word, z);
     347          30 :                 if (!n)
     348           0 :                         return log_oom();
     349             : 
     350          30 :                 r = sysv_translate_facility(n, basename(s->path), &m);
     351          30 :                 if (r < 0)
     352           0 :                         return r;
     353          30 :                 if (r == 0)
     354          21 :                         continue;
     355             : 
     356           9 :                 t = unit_name_to_type(m);
     357           9 :                 if (t == UNIT_SERVICE) {
     358           9 :                         log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
     359           9 :                         r = add_alias(s->name, m);
     360           9 :                         if (r < 0)
     361           0 :                                 log_warning_errno(r, "[%s:%u] Failed to add LSB Provides name %s, ignoring: %m", s->path, line, m);
     362           0 :                 } else if (t == UNIT_TARGET) {
     363             :                         /* NB: SysV targets which are provided by a
     364             :                          * service are pulled in by the services, as
     365             :                          * an indication that the generic service is
     366             :                          * now available. This is strictly one-way.
     367             :                          * The targets do NOT pull in SysV services! */
     368           0 :                         r = strv_extend(&s->before, m);
     369           0 :                         if (r < 0)
     370           0 :                                 return log_oom();
     371           0 :                         r = strv_extend(&s->wants, m);
     372           0 :                         if (r < 0)
     373           0 :                                 return log_oom();
     374           0 :                         if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
     375           0 :                                 r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
     376           0 :                                 if (r < 0)
     377           0 :                                         return log_oom();
     378             :                         }
     379           0 :                 } else if (t == _UNIT_TYPE_INVALID)
     380           0 :                         log_warning("Unit name '%s' is invalid", m);
     381             :                 else
     382           0 :                         log_warning("Unknown unit type for unit '%s'", m);
     383             :         }
     384          23 :         if (!isempty(state_))
     385           0 :                 log_error("[%s:%u] Trailing garbage in Provides, ignoring.", s->path, line);
     386          23 :         return 0;
     387             : }
     388             : 
     389          24 : static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) {
     390             :         const char *word, *state_;
     391             :         size_t z;
     392             :         int r;
     393             : 
     394          51 :         FOREACH_WORD_QUOTED(word, z, text, state_) {
     395          54 :                 _cleanup_free_ char *n = NULL, *m = NULL;
     396             :                 bool is_before;
     397             : 
     398          27 :                 n = strndup(word, z);
     399          27 :                 if (!n)
     400           0 :                         return log_oom();
     401             : 
     402          27 :                 r = sysv_translate_facility(n, basename(s->path), &m);
     403          27 :                 if (r < 0) {
     404           0 :                         log_warning_errno(r, "[%s:%u] Failed to translate LSB dependency %s, ignoring: %m", s->path, line, n);
     405           0 :                         continue;
     406             :                 }
     407          27 :                 if (r == 0)
     408          20 :                         continue;
     409             : 
     410           7 :                 is_before = startswith_no_case(full_text, "X-Start-Before:");
     411             : 
     412           7 :                 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
     413             :                         /* the network-online target is special, as it needs to be actively pulled in */
     414           1 :                         r = strv_extend(&s->after, m);
     415           1 :                         if (r < 0)
     416           0 :                                 return log_oom();
     417           1 :                         r = strv_extend(&s->wants, m);
     418             :                 } else
     419           6 :                         r = strv_extend(is_before ? &s->before : &s->after, m);
     420             : 
     421           7 :                 if (r < 0)
     422           0 :                         return log_oom();
     423             :         }
     424          24 :         if (!isempty(state_))
     425           0 :                 log_warning("[%s:%u] Trailing garbage in %*s, ignoring.", s->path, line, (int)(strchr(full_text, ':') - full_text), full_text);
     426          24 :         return 0;
     427             : }
     428             : 
     429          25 : static int load_sysv(SysvStub *s) {
     430          25 :         _cleanup_fclose_ FILE *f;
     431          25 :         unsigned line = 0;
     432             :         int r;
     433             :         enum {
     434             :                 NORMAL,
     435             :                 DESCRIPTION,
     436             :                 LSB,
     437             :                 LSB_DESCRIPTION,
     438             :                 USAGE_CONTINUATION
     439          25 :         } state = NORMAL;
     440          50 :         _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
     441             :         char *description;
     442          25 :         bool supports_reload = false;
     443             : 
     444          25 :         assert(s);
     445             : 
     446          25 :         f = fopen(s->path, "re");
     447          25 :         if (!f)
     448           0 :                 return errno == ENOENT ? 0 : -errno;
     449             : 
     450          25 :         log_debug("Loading SysV script %s", s->path);
     451             : 
     452         308 :         while (!feof(f)) {
     453             :                 char l[LINE_MAX], *t;
     454             : 
     455         283 :                 if (!fgets(l, sizeof(l), f)) {
     456          25 :                         if (feof(f))
     457          25 :                                 break;
     458             : 
     459           0 :                         return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path);
     460             :                 }
     461             : 
     462         258 :                 line++;
     463             : 
     464         258 :                 t = strstrip(l);
     465         258 :                 if (*t != '#') {
     466             :                         /* Try to figure out whether this init script supports
     467             :                          * the reload operation. This heuristic looks for
     468             :                          * "Usage" lines which include the reload option. */
     469          25 :                         if ( state == USAGE_CONTINUATION ||
     470          25 :                             (state == NORMAL && strcasestr(t, "usage"))) {
     471           0 :                                 if (usage_contains_reload(t)) {
     472           0 :                                         supports_reload = true;
     473           0 :                                         state = NORMAL;
     474           0 :                                 } else if (t[strlen(t)-1] == '\\')
     475           0 :                                         state = USAGE_CONTINUATION;
     476             :                                 else
     477           0 :                                         state = NORMAL;
     478             :                         }
     479             : 
     480          96 :                         continue;
     481             :                 }
     482             : 
     483         233 :                 if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
     484          23 :                         state = LSB;
     485          23 :                         s->has_lsb = true;
     486          23 :                         continue;
     487             :                 }
     488             : 
     489         210 :                 if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) {
     490          23 :                         state = NORMAL;
     491          23 :                         continue;
     492             :                 }
     493             : 
     494         187 :                 t++;
     495         187 :                 t += strspn(t, WHITESPACE);
     496             : 
     497         187 :                 if (state == NORMAL) {
     498             : 
     499             :                         /* Try to parse Red Hat style description */
     500             : 
     501          25 :                         if (startswith_no_case(t, "description:")) {
     502             : 
     503           0 :                                 size_t k = strlen(t);
     504             :                                 char *d;
     505             :                                 const char *j;
     506             : 
     507           0 :                                 if (t[k-1] == '\\') {
     508           0 :                                         state = DESCRIPTION;
     509           0 :                                         t[k-1] = 0;
     510             :                                 }
     511             : 
     512           0 :                                 j = strstrip(t+12);
     513           0 :                                 if (j && *j) {
     514           0 :                                         d = strdup(j);
     515           0 :                                         if (!d)
     516           0 :                                                 return -ENOMEM;
     517             :                                 } else
     518           0 :                                         d = NULL;
     519             : 
     520           0 :                                 free(chkconfig_description);
     521           0 :                                 chkconfig_description = d;
     522             : 
     523          25 :                         } else if (startswith_no_case(t, "pidfile:")) {
     524             : 
     525             :                                 char *fn;
     526             : 
     527           0 :                                 state = NORMAL;
     528             : 
     529           0 :                                 fn = strstrip(t+8);
     530           0 :                                 if (!path_is_absolute(fn)) {
     531           0 :                                         log_error("[%s:%u] PID file not absolute. Ignoring.", s->path, line);
     532           0 :                                         continue;
     533             :                                 }
     534             : 
     535           0 :                                 fn = strdup(fn);
     536           0 :                                 if (!fn)
     537           0 :                                         return -ENOMEM;
     538             : 
     539           0 :                                 free(s->pid_file);
     540           0 :                                 s->pid_file = fn;
     541             :                         }
     542             : 
     543         162 :                 } else if (state == DESCRIPTION) {
     544             : 
     545             :                         /* Try to parse Red Hat style description
     546             :                          * continuation */
     547             : 
     548           0 :                         size_t k = strlen(t);
     549             :                         char *j;
     550             : 
     551           0 :                         if (t[k-1] == '\\')
     552           0 :                                 t[k-1] = 0;
     553             :                         else
     554           0 :                                 state = NORMAL;
     555             : 
     556           0 :                         j = strstrip(t);
     557           0 :                         if (j && *j) {
     558           0 :                                 char *d = NULL;
     559             : 
     560           0 :                                 if (chkconfig_description)
     561           0 :                                         d = strjoin(chkconfig_description, " ", j, NULL);
     562             :                                 else
     563           0 :                                         d = strdup(j);
     564             : 
     565           0 :                                 if (!d)
     566           0 :                                         return -ENOMEM;
     567             : 
     568           0 :                                 free(chkconfig_description);
     569           0 :                                 chkconfig_description = d;
     570             :                         }
     571             : 
     572         162 :                 } else if (state == LSB || state == LSB_DESCRIPTION) {
     573             : 
     574         162 :                         if (startswith_no_case(t, "Provides:")) {
     575          23 :                                 state = LSB;
     576             : 
     577          23 :                                 r = handle_provides(s, line, t, t + 9);
     578          23 :                                 if (r < 0)
     579           0 :                                         return r;
     580         255 :                         } else if (startswith_no_case(t, "Required-Start:") ||
     581         231 :                                    startswith_no_case(t, "Should-Start:") ||
     582         230 :                                    startswith_no_case(t, "X-Start-Before:") ||
     583         115 :                                    startswith_no_case(t, "X-Start-After:")) {
     584             : 
     585          24 :                                 state = LSB;
     586             : 
     587          24 :                                 r = handle_dependencies(s, line, t, strchr(t, ':') + 1);
     588          48 :                                 if (r < 0)
     589           0 :                                         return r;
     590             : 
     591             : 
     592         115 :                         } else if (startswith_no_case(t, "Description:")) {
     593             :                                 char *d, *j;
     594             : 
     595          23 :                                 state = LSB_DESCRIPTION;
     596             : 
     597          23 :                                 j = strstrip(t+12);
     598          23 :                                 if (j && *j) {
     599          23 :                                         d = strdup(j);
     600          46 :                                         if (!d)
     601           0 :                                                 return -ENOMEM;
     602             :                                 } else
     603           0 :                                         d = NULL;
     604             : 
     605          23 :                                 free(long_description);
     606          23 :                                 long_description = d;
     607             : 
     608          92 :                         } else if (startswith_no_case(t, "Short-Description:")) {
     609             :                                 char *d, *j;
     610             : 
     611          23 :                                 state = LSB;
     612             : 
     613          23 :                                 j = strstrip(t+18);
     614          23 :                                 if (j && *j) {
     615          23 :                                         d = strdup(j);
     616          46 :                                         if (!d)
     617           0 :                                                 return -ENOMEM;
     618             :                                 } else
     619           0 :                                         d = NULL;
     620             : 
     621          23 :                                 free(short_description);
     622          23 :                                 short_description = d;
     623             : 
     624          69 :                         } else if (state == LSB_DESCRIPTION) {
     625             : 
     626           0 :                                 if (startswith(l, "#\t") || startswith(l, "#  ")) {
     627             :                                         char *j;
     628             : 
     629           0 :                                         j = strstrip(t);
     630           0 :                                         if (j && *j) {
     631           0 :                                                 char *d = NULL;
     632             : 
     633           0 :                                                 if (long_description)
     634           0 :                                                         d = strjoin(long_description, " ", t, NULL);
     635             :                                                 else
     636           0 :                                                         d = strdup(j);
     637             : 
     638           0 :                                                 if (!d)
     639           0 :                                                         return -ENOMEM;
     640             : 
     641           0 :                                                 free(long_description);
     642           0 :                                                 long_description = d;
     643             :                                         }
     644             : 
     645             :                                 } else
     646           0 :                                         state = LSB;
     647             :                         }
     648             :                 }
     649             :         }
     650             : 
     651          25 :         s->reload = supports_reload;
     652             : 
     653             :         /* We use the long description only if
     654             :          * no short description is set. */
     655             : 
     656          25 :         if (short_description)
     657          23 :                 description = short_description;
     658           2 :         else if (chkconfig_description)
     659           0 :                 description = chkconfig_description;
     660           2 :         else if (long_description)
     661           0 :                 description = long_description;
     662             :         else
     663           2 :                 description = NULL;
     664             : 
     665          25 :         if (description) {
     666             :                 char *d;
     667             : 
     668          23 :                 d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
     669          23 :                 if (!d)
     670           0 :                         return -ENOMEM;
     671             : 
     672          23 :                 s->description = d;
     673             :         }
     674             : 
     675          25 :         return 0;
     676             : }
     677             : 
     678          25 : static int fix_order(SysvStub *s, Hashmap *all_services) {
     679             :         SysvStub *other;
     680             :         Iterator j;
     681             :         int r;
     682             : 
     683          25 :         assert(s);
     684             : 
     685          25 :         if (s->sysv_start_priority < 0)
     686          10 :                 return 0;
     687             : 
     688          65 :         HASHMAP_FOREACH(other, all_services, j) {
     689          35 :                 if (s == other)
     690          15 :                         continue;
     691             : 
     692          20 :                 if (other->sysv_start_priority < 0)
     693           2 :                         continue;
     694             : 
     695             :                 /* If both units have modern headers we don't care
     696             :                  * about the priorities */
     697          18 :                 if (s->has_lsb && other->has_lsb)
     698          16 :                         continue;
     699             : 
     700           2 :                 if (other->sysv_start_priority < s->sysv_start_priority) {
     701           1 :                         r = strv_extend(&s->after, other->name);
     702           1 :                         if (r < 0)
     703           0 :                                 return log_oom();
     704             :                 }
     705           1 :                 else if (other->sysv_start_priority > s->sysv_start_priority) {
     706           1 :                         r = strv_extend(&s->before, other->name);
     707           1 :                         if (r < 0)
     708           0 :                                 return log_oom();
     709             :                 }
     710             :                 else
     711           0 :                         continue;
     712             : 
     713             :                 /* FIXME: Maybe we should compare the name here lexicographically? */
     714             :         }
     715             : 
     716          15 :         return 0;
     717             : }
     718             : 
     719          19 : static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
     720             :         char **path;
     721             : 
     722          38 :         STRV_FOREACH(path, lp->sysvinit_path) {
     723          38 :                 _cleanup_closedir_ DIR *d = NULL;
     724             :                 struct dirent *de;
     725             : 
     726          19 :                 d = opendir(*path);
     727          19 :                 if (!d) {
     728           0 :                         if (errno != ENOENT)
     729           0 :                                 log_warning_errno(errno, "opendir(%s) failed: %m", *path);
     730           0 :                         continue;
     731             :                 }
     732             : 
     733         107 :                 while ((de = readdir(d))) {
     734         138 :                         _cleanup_free_ char *fpath = NULL, *name = NULL;
     735         138 :                         _cleanup_(free_sysvstubp) SysvStub *service = NULL;
     736             :                         struct stat st;
     737             :                         int r;
     738             : 
     739          69 :                         if (hidden_file(de->d_name))
     740          42 :                                 continue;
     741             : 
     742          27 :                         if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
     743           0 :                                 log_warning_errno(errno, "stat() failed on %s/%s: %m", *path, de->d_name);
     744           0 :                                 continue;
     745             :                         }
     746             : 
     747          27 :                         if (!(st.st_mode & S_IXUSR))
     748           1 :                                 continue;
     749             : 
     750          26 :                         if (!S_ISREG(st.st_mode))
     751           0 :                                 continue;
     752             : 
     753          26 :                         name = sysv_translate_name(de->d_name);
     754          26 :                         if (!name)
     755           0 :                                 return log_oom();
     756             : 
     757          26 :                         if (hashmap_contains(all_services, name))
     758           0 :                                 continue;
     759             : 
     760          26 :                         fpath = strjoin(*path, "/", de->d_name, NULL);
     761          26 :                         if (!fpath)
     762           0 :                                 return log_oom();
     763             : 
     764          26 :                         if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) {
     765           1 :                                 log_debug("Native unit for %s already exists, skipping", name);
     766           1 :                                 continue;
     767             :                         }
     768             : 
     769          25 :                         service = new0(SysvStub, 1);
     770          25 :                         if (!service)
     771           0 :                                 return log_oom();
     772             : 
     773          25 :                         service->sysv_start_priority = -1;
     774          25 :                         service->name = name;
     775          25 :                         service->path = fpath;
     776             : 
     777          25 :                         r = hashmap_put(all_services, service->name, service);
     778          25 :                         if (r < 0)
     779           0 :                                 return log_oom();
     780             : 
     781          25 :                         name = fpath = NULL;
     782          25 :                         service = NULL;
     783             :                 }
     784             :         }
     785             : 
     786          19 :         return 0;
     787             : }
     788             : 
     789          19 : static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
     790             :         char **p;
     791             :         unsigned i;
     792          38 :         _cleanup_closedir_ DIR *d = NULL;
     793          38 :         _cleanup_free_ char *path = NULL, *fpath = NULL;
     794             :         SysvStub *service;
     795             :         Iterator j;
     796          19 :         Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
     797          38 :         _cleanup_set_free_ Set *shutdown_services = NULL;
     798          19 :         int r = 0;
     799             : 
     800          38 :         STRV_FOREACH(p, lp->sysvrcnd_path)
     801         152 :                 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
     802             :                         struct dirent *de;
     803             : 
     804         133 :                         free(path);
     805         133 :                         path = strjoin(*p, "/", rcnd_table[i].path, NULL);
     806         133 :                         if (!path)
     807           0 :                                 return -ENOMEM;
     808             : 
     809         133 :                         if (d)
     810          53 :                                 closedir(d);
     811             : 
     812         133 :                         d = opendir(path);
     813         133 :                         if (!d) {
     814          71 :                                 if (errno != ENOENT)
     815           0 :                                         log_warning_errno(errno, "opendir(%s) failed: %m", path);
     816             : 
     817          71 :                                 continue;
     818             :                         }
     819             : 
     820         346 :                         while ((de = readdir(d))) {
     821         444 :                                 _cleanup_free_ char *name = NULL;
     822             : 
     823             :                                 int a, b;
     824             : 
     825         222 :                                 if (hidden_file(de->d_name))
     826         124 :                                         continue;
     827             : 
     828          98 :                                 if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
     829           0 :                                         continue;
     830             : 
     831          98 :                                 if (strlen(de->d_name) < 4)
     832           0 :                                         continue;
     833             : 
     834          98 :                                 a = undecchar(de->d_name[1]);
     835          98 :                                 b = undecchar(de->d_name[2]);
     836             : 
     837          98 :                                 if (a < 0 || b < 0)
     838           0 :                                         continue;
     839             : 
     840          98 :                                 free(fpath);
     841          98 :                                 fpath = strjoin(*p, "/", de->d_name, NULL);
     842          98 :                                 if (!fpath) {
     843           0 :                                         r = -ENOMEM;
     844           0 :                                         goto finish;
     845             :                                 }
     846             : 
     847          98 :                                 name = sysv_translate_name(de->d_name + 3);
     848          98 :                                 if (!name) {
     849           0 :                                         r = log_oom();
     850           0 :                                         goto finish;
     851             :                                 }
     852             : 
     853          98 :                                 service = hashmap_get(all_services, name);
     854          98 :                                 if (!service){
     855           7 :                                         log_debug("Ignoring %s symlink in %s, not generating %s.",
     856             :                                                   de->d_name, rcnd_table[i].path, name);
     857           7 :                                         continue;
     858             :                                 }
     859             : 
     860          91 :                                 if (de->d_name[0] == 'S')  {
     861             : 
     862          52 :                                         if (rcnd_table[i].type == RUNLEVEL_UP) {
     863         104 :                                                 service->sysv_start_priority =
     864          52 :                                                         MAX(a*10 + b, service->sysv_start_priority);
     865             :                                         }
     866             : 
     867          52 :                                         r = set_ensure_allocated(&runlevel_services[i], NULL);
     868          52 :                                         if (r < 0)
     869           0 :                                                 goto finish;
     870             : 
     871          52 :                                         r = set_put(runlevel_services[i], service);
     872          52 :                                         if (r < 0)
     873           0 :                                                 goto finish;
     874             : 
     875          78 :                                 } else if (de->d_name[0] == 'K' &&
     876          39 :                                            (rcnd_table[i].type == RUNLEVEL_DOWN)) {
     877             : 
     878          26 :                                         r = set_ensure_allocated(&shutdown_services, NULL);
     879          26 :                                         if (r < 0)
     880           0 :                                                 goto finish;
     881             : 
     882          26 :                                         r = set_put(shutdown_services, service);
     883          26 :                                         if (r < 0)
     884           0 :                                                 goto finish;
     885             :                                 }
     886             :                         }
     887             :                 }
     888             : 
     889             : 
     890         152 :         for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
     891         318 :                 SET_FOREACH(service, runlevel_services[i], j) {
     892          52 :                         r = strv_extend(&service->before, rcnd_table[i].target);
     893          52 :                         if (r < 0)
     894           0 :                                 return log_oom();
     895          52 :                         r = strv_extend(&service->wanted_by, rcnd_table[i].target);
     896          52 :                         if (r < 0)
     897           0 :                                 return log_oom();
     898             :                 }
     899             : 
     900          51 :         SET_FOREACH(service, shutdown_services, j) {
     901          13 :                 r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
     902          13 :                 if (r < 0)
     903           0 :                         return log_oom();
     904          13 :                 r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
     905          13 :                 if (r < 0)
     906           0 :                         return log_oom();
     907             :         }
     908             : 
     909          19 :         r = 0;
     910             : 
     911             : finish:
     912             : 
     913         152 :         for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
     914         133 :                 set_free(runlevel_services[i]);
     915             : 
     916          19 :         return r;
     917             : }
     918             : 
     919          19 : int main(int argc, char *argv[]) {
     920             :         int r, q;
     921          38 :         _cleanup_lookup_paths_free_ LookupPaths lp = {};
     922          38 :         _cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
     923             :         SysvStub *service;
     924             :         Iterator j;
     925             : 
     926          19 :         if (argc > 1 && argc != 4) {
     927           0 :                 log_error("This program takes three or no arguments.");
     928           0 :                 return EXIT_FAILURE;
     929             :         }
     930             : 
     931          19 :         if (argc > 1)
     932          19 :                 arg_dest = argv[3];
     933             : 
     934          19 :         log_set_target(LOG_TARGET_SAFE);
     935          19 :         log_parse_environment();
     936          19 :         log_open();
     937             : 
     938          19 :         umask(0022);
     939             : 
     940          19 :         r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL);
     941          19 :         if (r < 0) {
     942           0 :                 log_error("Failed to find lookup paths.");
     943           0 :                 return EXIT_FAILURE;
     944             :         }
     945             : 
     946          19 :         all_services = hashmap_new(&string_hash_ops);
     947          19 :         if (!all_services) {
     948           0 :                 log_oom();
     949           0 :                 return EXIT_FAILURE;
     950             :         }
     951             : 
     952          19 :         r = enumerate_sysv(&lp, all_services);
     953          19 :         if (r < 0) {
     954           0 :                 log_error("Failed to generate units for all init scripts.");
     955           0 :                 return EXIT_FAILURE;
     956             :         }
     957             : 
     958          19 :         r = set_dependencies_from_rcnd(&lp, all_services);
     959          19 :         if (r < 0) {
     960           0 :                 log_error("Failed to read runlevels from rcnd links.");
     961           0 :                 return EXIT_FAILURE;
     962             :         }
     963             : 
     964          63 :         HASHMAP_FOREACH(service, all_services, j) {
     965          25 :                 q = load_sysv(service);
     966          25 :                 if (q < 0)
     967           0 :                         continue;
     968             :         }
     969             : 
     970          63 :         HASHMAP_FOREACH(service, all_services, j) {
     971          25 :                 q = fix_order(service, all_services);
     972          25 :                 if (q < 0)
     973           0 :                         continue;
     974             : 
     975          25 :                 q = generate_unit_file(service);
     976          25 :                 if (q < 0)
     977           0 :                         continue;
     978             :         }
     979             : 
     980          19 :         return EXIT_SUCCESS;
     981             : }

Generated by: LCOV version 1.11