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