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