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