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