LCOV - code coverage report
Current view: top level - sysv-generator - sysv-generator.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 344 503 68.4 %
Date: 2019-08-22 15:41:25 Functions: 16 17 94.1 %

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

Generated by: LCOV version 1.14