chiark / gitweb /
9a869badcade5f65fa967e48860c07962d75acc3
[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 *unit;
118         char **p;
119         _cleanup_fclose_ FILE *f = 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                                 char *i, *w;
446                                 size_t z;
447
448                                 state = LSB;
449
450                                 FOREACH_WORD_QUOTED(w, z, t+9, i) {
451                                         _cleanup_free_ char *n = NULL, *m = NULL;
452
453                                         n = strndup(w, 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
498                         } else if (startswith_no_case(t, "Required-Start:") ||
499                                    startswith_no_case(t, "Should-Start:") ||
500                                    startswith_no_case(t, "X-Start-Before:") ||
501                                    startswith_no_case(t, "X-Start-After:")) {
502                                 char *i, *w;
503                                 size_t z;
504
505                                 state = LSB;
506
507                                 FOREACH_WORD_QUOTED(w, z, strchr(t, ':')+1, i) {
508                                         _cleanup_free_ char *n = NULL, *m = NULL;
509                                         bool is_before;
510
511                                         n = strndup(w, z);
512                                         if (!n)
513                                                 return -ENOMEM;
514
515                                         r = sysv_translate_facility(n, basename(s->path), &m);
516                                         if (r < 0) {
517                                                 log_error_unit(s->name,
518                                                                "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
519                                                                s->path, line, n, strerror(-r));
520                                                 continue;
521                                         }
522
523                                         if (r == 0)
524                                                 continue;
525
526                                         is_before = startswith_no_case(t, "X-Start-Before:");
527
528                                         if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
529                                                 /* the network-online target is special, as it needs to be actively pulled in */
530                                                 r = strv_extend(&s->after, m);
531                                                 if (r < 0)
532                                                         return log_oom();
533                                                 r = strv_extend(&s->wants, m);
534                                                 if (r < 0)
535                                                         return log_oom();
536                                         }
537                                         else {
538                                                 if (is_before) {
539                                                         r = strv_extend(&s->before, m);
540                                                         if (r < 0)
541                                                                 return log_oom();
542                                                 }
543                                                 else {
544                                                         r = strv_extend(&s->after, m);
545                                                         if (r < 0)
546                                                                 return log_oom();
547                                                 }
548                                         }
549
550                                         if (r < 0)
551                                                 log_error_unit(s->name,
552                                                                "[%s:%u] Failed to add dependency on %s, ignoring: %s",
553                                                                s->path, line, m, strerror(-r));
554                                 }
555
556                         } else if (startswith_no_case(t, "Description:")) {
557                                 char *d, *j;
558
559                                 state = LSB_DESCRIPTION;
560
561                                 j = strstrip(t+12);
562                                 if (j && *j) {
563                                         d = strdup(j);
564                                         if (!d)
565                                                 return -ENOMEM;
566                                 } else
567                                         d = NULL;
568
569                                 free(long_description);
570                                 long_description = d;
571
572                         } else if (startswith_no_case(t, "Short-Description:")) {
573                                 char *d, *j;
574
575                                 state = LSB;
576
577                                 j = strstrip(t+18);
578                                 if (j && *j) {
579                                         d = strdup(j);
580                                         if (!d)
581                                                 return -ENOMEM;
582                                 } else
583                                         d = NULL;
584
585                                 free(short_description);
586                                 short_description = d;
587
588                         } else if (state == LSB_DESCRIPTION) {
589
590                                 if (startswith(l, "#\t") || startswith(l, "#  ")) {
591                                         char *j;
592
593                                         j = strstrip(t);
594                                         if (j && *j) {
595                                                 char *d = NULL;
596
597                                                 if (long_description)
598                                                         d = strjoin(long_description, " ", t, NULL);
599                                                 else
600                                                         d = strdup(j);
601
602                                                 if (!d)
603                                                         return -ENOMEM;
604
605                                                 free(long_description);
606                                                 long_description = d;
607                                         }
608
609                                 } else
610                                         state = LSB;
611                         }
612                 }
613         }
614
615         s->reload = supports_reload;
616
617         /* We use the long description only if
618          * no short description is set. */
619
620         if (short_description)
621                 description = short_description;
622         else if (chkconfig_description)
623                 description = chkconfig_description;
624         else if (long_description)
625                 description = long_description;
626         else
627                 description = NULL;
628
629         if (description) {
630                 char *d;
631
632                 d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
633                 if (!d)
634                         return -ENOMEM;
635
636                 s->description = d;
637         }
638
639         return 0;
640 }
641
642 static int fix_order(SysvStub *s, Hashmap *all_services) {
643         SysvStub *other;
644         Iterator j;
645         int r;
646
647         assert(s);
648
649         if (s->sysv_start_priority < 0)
650                 return 0;
651
652         HASHMAP_FOREACH(other, all_services, j) {
653                 if (s == other)
654                         continue;
655
656                 if (other->sysv_start_priority < 0)
657                         continue;
658
659                 /* If both units have modern headers we don't care
660                  * about the priorities */
661                 if (s->has_lsb && other->has_lsb)
662                         continue;
663
664                 if (other->sysv_start_priority < s->sysv_start_priority) {
665                         r = strv_extend(&s->after, other->name);
666                         if (r < 0)
667                                 return log_oom();
668                 }
669                 else if (other->sysv_start_priority > s->sysv_start_priority) {
670                         r = strv_extend(&s->before, other->name);
671                         if (r < 0)
672                                 return log_oom();
673                 }
674                 else
675                         continue;
676
677                 /* FIXME: Maybe we should compare the name here lexicographically? */
678         }
679
680         return 0;
681 }
682
683 static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
684         char **path;
685
686         STRV_FOREACH(path, lp.sysvinit_path) {
687                 _cleanup_closedir_ DIR *d = NULL;
688                 struct dirent *de;
689
690                 d = opendir(*path);
691                 if (!d) {
692                         if (errno != ENOENT)
693                                 log_warning("opendir(%s) failed: %m", *path);
694                         continue;
695                 }
696
697                 while ((de = readdir(d))) {
698                         SysvStub *service;
699                         struct stat st;
700                         _cleanup_free_ char *fpath = NULL, *name = NULL;
701                         int r;
702
703                         if (ignore_file(de->d_name))
704                                 continue;
705
706                         fpath = strjoin(*path, "/", de->d_name, NULL);
707                         if (!fpath)
708                                 return log_oom();
709
710                         if (stat(fpath, &st) < 0)
711                                 continue;
712
713                         if (!(st.st_mode & S_IXUSR))
714                                 continue;
715
716                         name = sysv_translate_name(de->d_name);
717                         if (!name)
718                                 return log_oom();
719
720                         if (hashmap_contains(all_services, name))
721                                 continue;
722
723                         service = new0(SysvStub, 1);
724                         if (!service)
725                                 return log_oom();
726
727                         service->sysv_start_priority = -1;
728                         service->name = name;
729                         service->path = fpath;
730
731                         r = hashmap_put(all_services, service->name, service);
732                         if (r < 0)
733                                 return log_oom();
734
735                         name = fpath = NULL;
736                 }
737         }
738
739         return 0;
740 }
741
742 static int set_dependencies_from_rcnd(LookupPaths lp, Hashmap *all_services) {
743         char **p;
744         unsigned i;
745         _cleanup_closedir_ DIR *d = NULL;
746         _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
747         SysvStub *service;
748         Iterator j;
749         Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
750         _cleanup_set_free_ Set *shutdown_services = NULL;
751         int r = 0;
752
753         STRV_FOREACH(p, lp.sysvrcnd_path)
754                 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
755                         struct dirent *de;
756
757                         free(path);
758                         path = strjoin(*p, "/", rcnd_table[i].path, NULL);
759                         if (!path)
760                                 return -ENOMEM;
761
762                         if (d)
763                                 closedir(d);
764
765                         d = opendir(path);
766                         if (!d) {
767                                 if (errno != ENOENT)
768                                         log_warning("opendir(%s) failed: %m", path);
769
770                                 continue;
771                         }
772
773                         while ((de = readdir(d))) {
774                                 int a, b;
775
776                                 if (ignore_file(de->d_name))
777                                         continue;
778
779                                 if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
780                                         continue;
781
782                                 if (strlen(de->d_name) < 4)
783                                         continue;
784
785                                 a = undecchar(de->d_name[1]);
786                                 b = undecchar(de->d_name[2]);
787
788                                 if (a < 0 || b < 0)
789                                         continue;
790
791                                 free(fpath);
792                                 fpath = strjoin(*p, "/", de->d_name, NULL);
793                                 if (!fpath) {
794                                         r = -ENOMEM;
795                                         goto finish;
796                                 }
797
798                                 name = sysv_translate_name(de->d_name + 3);
799                                 if (!name) {
800                                         r = log_oom();
801                                         goto finish;
802                                 }
803
804                                 if (hashmap_contains(all_services, name))
805                                         service = hashmap_get(all_services, name);
806                                 else {
807                                         log_warning("Could not find init script for %s", name);
808                                         continue;
809                                 }
810
811                                 if (de->d_name[0] == 'S')  {
812
813                                         if (rcnd_table[i].type == RUNLEVEL_UP) {
814                                                 service->sysv_start_priority =
815                                                         MAX(a*10 + b, service->sysv_start_priority);
816                                         }
817
818                                         r = set_ensure_allocated(&runlevel_services[i],
819                                                                  trivial_hash_func, trivial_compare_func);
820                                         if (r < 0)
821                                                 goto finish;
822
823                                         r = set_put(runlevel_services[i], service);
824                                         if (r < 0)
825                                                 goto finish;
826
827                                 } else if (de->d_name[0] == 'K' &&
828                                            (rcnd_table[i].type == RUNLEVEL_DOWN)) {
829
830                                         r = set_ensure_allocated(&shutdown_services,
831                                                                  trivial_hash_func, trivial_compare_func);
832                                         if (r < 0)
833                                                 goto finish;
834
835                                         r = set_put(shutdown_services, service);
836                                         if (r < 0)
837                                                 goto finish;
838                                 }
839                         }
840                 }
841
842
843         for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
844                 SET_FOREACH(service, runlevel_services[i], j) {
845                         r = strv_extend(&service->before, rcnd_table[i].target);
846                         if (r < 0)
847                                 return log_oom();
848                         r = strv_extend(&service->wanted_by, rcnd_table[i].target);
849                         if (r < 0)
850                                 return log_oom();
851                 }
852
853         SET_FOREACH(service, shutdown_services, j) {
854                 r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
855                 if (r < 0)
856                         return log_oom();
857                 r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
858                 if (r < 0)
859                         return log_oom();
860         }
861
862         r = 0;
863
864 finish:
865
866         for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
867                 set_free(runlevel_services[i]);
868
869         return r;
870 }
871
872 int main(int argc, char *argv[]) {
873         int r, q;
874         LookupPaths lp;
875         Hashmap *all_services;
876         SysvStub *service;
877         Iterator j;
878
879         if (argc > 1 && argc != 4) {
880                 log_error("This program takes three or no arguments.");
881                 return EXIT_FAILURE;
882         }
883
884         if (argc > 1)
885                 arg_dest = argv[3];
886
887         log_set_target(LOG_TARGET_SAFE);
888         log_parse_environment();
889         log_open();
890
891         umask(0022);
892
893         r = lookup_paths_init(&lp, SYSTEMD_SYSTEM, true, NULL, NULL, NULL, NULL);
894         if (r < 0) {
895                 log_error("Failed to find lookup paths.");
896                 return EXIT_FAILURE;
897         }
898
899         all_services = hashmap_new(string_hash_func, string_compare_func);
900         if (!all_services) {
901                 log_oom();
902                 return EXIT_FAILURE;
903         }
904
905         r = enumerate_sysv(lp, all_services);
906         if (r < 0) {
907                 log_error("Failed to generate units for all init scripts.");
908                 return EXIT_FAILURE;
909         }
910
911         r = set_dependencies_from_rcnd(lp, all_services);
912         if (r < 0) {
913                 log_error("Failed to read runlevels from rcnd links.");
914                 return EXIT_FAILURE;
915         }
916
917         HASHMAP_FOREACH(service, all_services, j) {
918                 q = load_sysv(service);
919                 if (q < 0)
920                         continue;
921
922                 q = fix_order(service, all_services);
923                 if (q < 0)
924                         continue;
925
926                 q = generate_unit_file(service);
927                 if (q < 0)
928                         continue;
929         }
930
931         return EXIT_SUCCESS;
932 }