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