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