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