]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysv-generator/sysv-generator.c
sysv-generator: initialize LookupPaths just once
[thirdparty/systemd.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 "hashmap.h"
38
39 typedef enum RunlevelType {
40 RUNLEVEL_UP,
41 RUNLEVEL_DOWN
42 } RunlevelType;
43
44 static const struct {
45 const char *path;
46 const char *target;
47 const RunlevelType type;
48 } rcnd_table[] = {
49 /* Standard SysV runlevels for start-up */
50 { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
51 { "rc2.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
52 { "rc3.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
53 { "rc4.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
54 { "rc5.d", SPECIAL_GRAPHICAL_TARGET, RUNLEVEL_UP },
55
56 /* Standard SysV runlevels for shutdown */
57 { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
58 { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
59
60 /* Note that the order here matters, as we read the
61 directories in this order, and we want to make sure that
62 sysv_start_priority is known when we first load the
63 unit. And that value we only know from S links. Hence
64 UP must be read before DOWN */
65 };
66
67 typedef struct SysvStub {
68 char *name;
69 char *path;
70 char *description;
71 int sysv_start_priority;
72 char *pid_file;
73 char **before;
74 char **after;
75 char **wants;
76 char **wanted_by;
77 char **conflicts;
78 bool has_lsb;
79 bool reload;
80 } SysvStub;
81
82 const char *arg_dest = "/tmp";
83
84 static int add_symlink(const char *service, const char *where) {
85 _cleanup_free_ char *from = NULL, *to = NULL;
86 int r;
87
88 assert(service);
89 assert(where);
90
91 from = strjoin(arg_dest, "/", service, NULL);
92 if (!from)
93 return log_oom();
94
95 to = strjoin(arg_dest, "/", where, ".wants/", service, NULL);
96 if (!to)
97 return log_oom();
98
99 mkdir_parents_label(to, 0755);
100
101 r = symlink(from, to);
102 if (r < 0) {
103 if (errno == EEXIST)
104 return 0;
105 return -errno;
106 }
107
108 return 1;
109 }
110
111 static int add_alias(const char *service, const char *alias) {
112 _cleanup_free_ char *link = NULL;
113 int r;
114
115 assert(service);
116 assert(alias);
117
118 link = strjoin(arg_dest, "/", alias, NULL);
119 if (!link)
120 return log_oom();
121
122 r = symlink(service, link);
123 if (r < 0) {
124 if (errno == EEXIST)
125 return 0;
126 return -errno;
127 }
128
129 return 1;
130 }
131
132 static int generate_unit_file(SysvStub *s) {
133 char **p;
134 _cleanup_fclose_ FILE *f = NULL;
135 _cleanup_free_ char *unit = NULL;
136 _cleanup_free_ char *before = NULL;
137 _cleanup_free_ char *after = NULL;
138 _cleanup_free_ char *wants = NULL;
139 _cleanup_free_ char *conflicts = NULL;
140 int r;
141
142 before = strv_join(s->before, " ");
143 if (!before)
144 return log_oom();
145
146 after = strv_join(s->after, " ");
147 if (!after)
148 return log_oom();
149
150 wants = strv_join(s->wants, " ");
151 if (!wants)
152 return log_oom();
153
154 conflicts = strv_join(s->conflicts, " ");
155 if (!conflicts)
156 return log_oom();
157
158 unit = strjoin(arg_dest, "/", s->name, NULL);
159 if (!unit)
160 return log_oom();
161
162 /* We might already have a symlink with the same name from a Provides:,
163 * or from backup files like /etc/init.d/foo.bak. Real scripts always win,
164 * so remove an existing link */
165 if (is_symlink(unit) > 0) {
166 log_warning("Overwriting existing symlink %s with real service", unit);
167 (void) unlink(unit);
168 }
169
170 f = fopen(unit, "wxe");
171 if (!f)
172 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
173
174 fprintf(f,
175 "# Automatically generated by systemd-sysv-generator\n\n"
176 "[Unit]\n"
177 "Documentation=man:systemd-sysv-generator(8)\n"
178 "SourcePath=%s\n"
179 "Description=%s\n",
180 s->path, s->description);
181
182 if (!isempty(before))
183 fprintf(f, "Before=%s\n", before);
184 if (!isempty(after))
185 fprintf(f, "After=%s\n", after);
186 if (!isempty(wants))
187 fprintf(f, "Wants=%s\n", wants);
188 if (!isempty(conflicts))
189 fprintf(f, "Conflicts=%s\n", conflicts);
190
191 fprintf(f,
192 "\n[Service]\n"
193 "Type=forking\n"
194 "Restart=no\n"
195 "TimeoutSec=5min\n"
196 "IgnoreSIGPIPE=no\n"
197 "KillMode=process\n"
198 "GuessMainPID=no\n"
199 "RemainAfterExit=%s\n",
200 yes_no(!s->pid_file));
201
202 if (s->pid_file)
203 fprintf(f, "PIDFile=%s\n", s->pid_file);
204
205 fprintf(f,
206 "ExecStart=%s start\n"
207 "ExecStop=%s stop\n",
208 s->path, s->path);
209
210 if (s->reload)
211 fprintf(f, "ExecReload=%s reload\n", s->path);
212
213 STRV_FOREACH(p, s->wanted_by) {
214 r = add_symlink(s->name, *p);
215 if (r < 0)
216 log_unit_error_errno(s->name, r, "Failed to create 'Wants' symlink to %s: %m", *p);
217 }
218
219 return 0;
220 }
221
222 static bool usage_contains_reload(const char *line) {
223 return (strcasestr(line, "{reload|") ||
224 strcasestr(line, "{reload}") ||
225 strcasestr(line, "{reload\"") ||
226 strcasestr(line, "|reload|") ||
227 strcasestr(line, "|reload}") ||
228 strcasestr(line, "|reload\""));
229 }
230
231 static char *sysv_translate_name(const char *name) {
232 char *r;
233
234 r = new(char, strlen(name) + strlen(".service") + 1);
235 if (!r)
236 return NULL;
237
238 if (endswith(name, ".sh"))
239 /* Drop .sh suffix */
240 strcpy(stpcpy(r, name) - 3, ".service");
241 else
242 /* Normal init script name */
243 strcpy(stpcpy(r, name), ".service");
244
245 return r;
246 }
247
248 static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
249
250 /* We silently ignore the $ prefix here. According to the LSB
251 * spec it simply indicates whether something is a
252 * standardized name or a distribution-specific one. Since we
253 * just follow what already exists and do not introduce new
254 * uses or names we don't care who introduced a new name. */
255
256 static const char * const table[] = {
257 /* LSB defined facilities */
258 "local_fs", NULL,
259 "network", SPECIAL_NETWORK_ONLINE_TARGET,
260 "named", SPECIAL_NSS_LOOKUP_TARGET,
261 "portmap", SPECIAL_RPCBIND_TARGET,
262 "remote_fs", SPECIAL_REMOTE_FS_TARGET,
263 "syslog", NULL,
264 "time", SPECIAL_TIME_SYNC_TARGET,
265 };
266
267 char *filename_no_sh, *e, *r;
268 const char *n;
269 unsigned i;
270
271 assert(name);
272 assert(_r);
273
274 n = *name == '$' ? name + 1 : name;
275
276 for (i = 0; i < ELEMENTSOF(table); i += 2) {
277
278 if (!streq(table[i], n))
279 continue;
280
281 if (!table[i+1])
282 return 0;
283
284 r = strdup(table[i+1]);
285 if (!r)
286 return log_oom();
287
288 goto finish;
289 }
290
291 /* strip ".sh" suffix from file name for comparison */
292 filename_no_sh = strdupa(filename);
293 e = endswith(filename_no_sh, ".sh");
294 if (e) {
295 *e = '\0';
296 filename = filename_no_sh;
297 }
298
299 /* If we don't know this name, fallback heuristics to figure
300 * out whether something is a target or a service alias. */
301
302 if (*name == '$') {
303 if (!unit_prefix_is_valid(n))
304 return -EINVAL;
305
306 /* Facilities starting with $ are most likely targets */
307 r = unit_name_build(n, NULL, ".target");
308 } else if (streq_ptr(n, filename))
309 /* Names equaling the file name of the services are redundant */
310 return 0;
311 else
312 /* Everything else we assume to be normal service names */
313 r = sysv_translate_name(n);
314 if (!r)
315 return -ENOMEM;
316
317 finish:
318 *_r = r;
319
320 return 1;
321 }
322
323 static int load_sysv(SysvStub *s) {
324 _cleanup_fclose_ FILE *f;
325 unsigned line = 0;
326 int r;
327 enum {
328 NORMAL,
329 DESCRIPTION,
330 LSB,
331 LSB_DESCRIPTION,
332 USAGE_CONTINUATION
333 } state = NORMAL;
334 _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
335 char *description;
336 bool supports_reload = false;
337
338 assert(s);
339
340 f = fopen(s->path, "re");
341 if (!f)
342 return errno == ENOENT ? 0 : -errno;
343
344 log_debug("Loading SysV script %s", s->path);
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 if (r < 0)
488 return r;
489 if (r == 0)
490 continue;
491
492 if (unit_name_to_type(m) == UNIT_SERVICE) {
493 log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
494 r = add_alias(s->name, m);
495 } else {
496 /* NB: SysV targets
497 * which are provided
498 * by a service are
499 * pulled in by the
500 * services, as an
501 * indication that the
502 * generic service is
503 * now available. This
504 * is strictly
505 * one-way. The
506 * targets do NOT pull
507 * in the SysV
508 * services! */
509 r = strv_extend(&s->before, m);
510 if (r < 0)
511 return log_oom();
512 r = strv_extend(&s->wants, m);
513 if (r < 0)
514 return log_oom();
515 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
516 r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
517 if (r < 0)
518 return log_oom();
519 }
520 }
521
522 if (r < 0)
523 log_unit_error(s->name,
524 "[%s:%u] Failed to add LSB Provides name %s, ignoring: %s",
525 s->path, line, m, strerror(-r));
526 }
527 if (!isempty(state_))
528 log_unit_error(s->name,
529 "[%s:%u] Trailing garbage in Provides, ignoring.",
530 s->path, line);
531
532 } else if (startswith_no_case(t, "Required-Start:") ||
533 startswith_no_case(t, "Should-Start:") ||
534 startswith_no_case(t, "X-Start-Before:") ||
535 startswith_no_case(t, "X-Start-After:")) {
536 const char *word, *state_;
537 size_t z;
538
539 state = LSB;
540
541 FOREACH_WORD_QUOTED(word, z, strchr(t, ':')+1, state_) {
542 _cleanup_free_ char *n = NULL, *m = NULL;
543 bool is_before;
544
545 n = strndup(word, z);
546 if (!n)
547 return -ENOMEM;
548
549 r = sysv_translate_facility(n, basename(s->path), &m);
550 if (r < 0) {
551 log_unit_error(s->name,
552 "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
553 s->path, line, n, strerror(-r));
554 continue;
555 }
556
557 if (r == 0)
558 continue;
559
560 is_before = startswith_no_case(t, "X-Start-Before:");
561
562 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
563 /* the network-online target is special, as it needs to be actively pulled in */
564 r = strv_extend(&s->after, m);
565 if (r < 0)
566 return log_oom();
567 r = strv_extend(&s->wants, m);
568 if (r < 0)
569 return log_oom();
570 }
571 else {
572 if (is_before) {
573 r = strv_extend(&s->before, m);
574 if (r < 0)
575 return log_oom();
576 }
577 else {
578 r = strv_extend(&s->after, m);
579 if (r < 0)
580 return log_oom();
581 }
582 }
583
584 if (r < 0)
585 log_unit_error(s->name,
586 "[%s:%u] Failed to add dependency on %s, ignoring: %s",
587 s->path, line, m, strerror(-r));
588 }
589 if (!isempty(state_))
590 log_unit_error(s->name,
591 "[%s:%u] Trailing garbage in %*s, ignoring.",
592 s->path, line,
593 (int)(strchr(t, ':') - t), t);
594
595 } else if (startswith_no_case(t, "Description:")) {
596 char *d, *j;
597
598 state = LSB_DESCRIPTION;
599
600 j = strstrip(t+12);
601 if (j && *j) {
602 d = strdup(j);
603 if (!d)
604 return -ENOMEM;
605 } else
606 d = NULL;
607
608 free(long_description);
609 long_description = d;
610
611 } else if (startswith_no_case(t, "Short-Description:")) {
612 char *d, *j;
613
614 state = LSB;
615
616 j = strstrip(t+18);
617 if (j && *j) {
618 d = strdup(j);
619 if (!d)
620 return -ENOMEM;
621 } else
622 d = NULL;
623
624 free(short_description);
625 short_description = d;
626
627 } else if (state == LSB_DESCRIPTION) {
628
629 if (startswith(l, "#\t") || startswith(l, "# ")) {
630 char *j;
631
632 j = strstrip(t);
633 if (j && *j) {
634 char *d = NULL;
635
636 if (long_description)
637 d = strjoin(long_description, " ", t, NULL);
638 else
639 d = strdup(j);
640
641 if (!d)
642 return -ENOMEM;
643
644 free(long_description);
645 long_description = d;
646 }
647
648 } else
649 state = LSB;
650 }
651 }
652 }
653
654 s->reload = supports_reload;
655
656 /* We use the long description only if
657 * no short description is set. */
658
659 if (short_description)
660 description = short_description;
661 else if (chkconfig_description)
662 description = chkconfig_description;
663 else if (long_description)
664 description = long_description;
665 else
666 description = NULL;
667
668 if (description) {
669 char *d;
670
671 d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
672 if (!d)
673 return -ENOMEM;
674
675 s->description = d;
676 }
677
678 return 0;
679 }
680
681 static int fix_order(SysvStub *s, Hashmap *all_services) {
682 SysvStub *other;
683 Iterator j;
684 int r;
685
686 assert(s);
687
688 if (s->sysv_start_priority < 0)
689 return 0;
690
691 HASHMAP_FOREACH(other, all_services, j) {
692 if (s == other)
693 continue;
694
695 if (other->sysv_start_priority < 0)
696 continue;
697
698 /* If both units have modern headers we don't care
699 * about the priorities */
700 if (s->has_lsb && other->has_lsb)
701 continue;
702
703 if (other->sysv_start_priority < s->sysv_start_priority) {
704 r = strv_extend(&s->after, other->name);
705 if (r < 0)
706 return log_oom();
707 }
708 else if (other->sysv_start_priority > s->sysv_start_priority) {
709 r = strv_extend(&s->before, other->name);
710 if (r < 0)
711 return log_oom();
712 }
713 else
714 continue;
715
716 /* FIXME: Maybe we should compare the name here lexicographically? */
717 }
718
719 return 0;
720 }
721
722 static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
723 char **path;
724
725 STRV_FOREACH(path, lp->sysvinit_path) {
726 _cleanup_closedir_ DIR *d = NULL;
727 struct dirent *de;
728
729 d = opendir(*path);
730 if (!d) {
731 if (errno != ENOENT)
732 log_warning_errno(errno, "opendir(%s) failed: %m", *path);
733 continue;
734 }
735
736 while ((de = readdir(d))) {
737 _cleanup_free_ char *fpath = NULL, *name = NULL;
738 _cleanup_free_ SysvStub *service = NULL;
739 struct stat st;
740 int r;
741
742 if (hidden_file(de->d_name))
743 continue;
744
745 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
746 log_warning_errno(errno, "stat() failed on %s/%s: %m", *path, de->d_name);
747 continue;
748 }
749
750 if (!(st.st_mode & S_IXUSR))
751 continue;
752
753 if (!S_ISREG(st.st_mode))
754 continue;
755
756 name = sysv_translate_name(de->d_name);
757 if (!name)
758 return log_oom();
759
760 if (hashmap_contains(all_services, name))
761 continue;
762
763 fpath = strjoin(*path, "/", de->d_name, NULL);
764 if (!fpath)
765 return log_oom();
766
767 if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) {
768 log_debug("Native unit for %s already exists, skipping", name);
769 continue;
770 }
771
772 service = new0(SysvStub, 1);
773 if (!service)
774 return log_oom();
775
776 service->sysv_start_priority = -1;
777 service->name = name;
778 service->path = fpath;
779
780 r = hashmap_put(all_services, service->name, service);
781 if (r < 0)
782 return log_oom();
783
784 name = fpath = NULL;
785 service = NULL;
786 }
787 }
788
789 return 0;
790 }
791
792 static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
793 char **p;
794 unsigned i;
795 _cleanup_closedir_ DIR *d = NULL;
796 _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
797 SysvStub *service;
798 Iterator j;
799 Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
800 _cleanup_set_free_ Set *shutdown_services = NULL;
801 int r = 0;
802
803 STRV_FOREACH(p, lp->sysvrcnd_path)
804 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
805 struct dirent *de;
806
807 free(path);
808 path = strjoin(*p, "/", rcnd_table[i].path, NULL);
809 if (!path)
810 return -ENOMEM;
811
812 if (d)
813 closedir(d);
814
815 d = opendir(path);
816 if (!d) {
817 if (errno != ENOENT)
818 log_warning_errno(errno, "opendir(%s) failed: %m", path);
819
820 continue;
821 }
822
823 while ((de = readdir(d))) {
824 int a, b;
825
826 if (hidden_file(de->d_name))
827 continue;
828
829 if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
830 continue;
831
832 if (strlen(de->d_name) < 4)
833 continue;
834
835 a = undecchar(de->d_name[1]);
836 b = undecchar(de->d_name[2]);
837
838 if (a < 0 || b < 0)
839 continue;
840
841 free(fpath);
842 fpath = strjoin(*p, "/", de->d_name, NULL);
843 if (!fpath) {
844 r = -ENOMEM;
845 goto finish;
846 }
847
848 name = sysv_translate_name(de->d_name + 3);
849 if (!name) {
850 r = log_oom();
851 goto finish;
852 }
853
854 service = hashmap_get(all_services, name);
855 if (!service){
856 log_debug("Ignoring %s symlink in %s, not generating %s.",
857 de->d_name, rcnd_table[i].path, name);
858 continue;
859 }
860
861 if (de->d_name[0] == 'S') {
862
863 if (rcnd_table[i].type == RUNLEVEL_UP) {
864 service->sysv_start_priority =
865 MAX(a*10 + b, service->sysv_start_priority);
866 }
867
868 r = set_ensure_allocated(&runlevel_services[i], NULL);
869 if (r < 0)
870 goto finish;
871
872 r = set_put(runlevel_services[i], service);
873 if (r < 0)
874 goto finish;
875
876 } else if (de->d_name[0] == 'K' &&
877 (rcnd_table[i].type == RUNLEVEL_DOWN)) {
878
879 r = set_ensure_allocated(&shutdown_services, NULL);
880 if (r < 0)
881 goto finish;
882
883 r = set_put(shutdown_services, service);
884 if (r < 0)
885 goto finish;
886 }
887 }
888 }
889
890
891 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
892 SET_FOREACH(service, runlevel_services[i], j) {
893 r = strv_extend(&service->before, rcnd_table[i].target);
894 if (r < 0)
895 return log_oom();
896 r = strv_extend(&service->wanted_by, rcnd_table[i].target);
897 if (r < 0)
898 return log_oom();
899 }
900
901 SET_FOREACH(service, shutdown_services, j) {
902 r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
903 if (r < 0)
904 return log_oom();
905 r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
906 if (r < 0)
907 return log_oom();
908 }
909
910 r = 0;
911
912 finish:
913
914 for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
915 set_free(runlevel_services[i]);
916
917 return r;
918 }
919
920 int main(int argc, char *argv[]) {
921 int r, q;
922 LookupPaths lp;
923 Hashmap *all_services;
924 SysvStub *service;
925 Iterator j;
926
927 if (argc > 1 && argc != 4) {
928 log_error("This program takes three or no arguments.");
929 return EXIT_FAILURE;
930 }
931
932 if (argc > 1)
933 arg_dest = argv[3];
934
935 log_set_target(LOG_TARGET_SAFE);
936 log_parse_environment();
937 log_open();
938
939 umask(0022);
940
941 r = lookup_paths_init(&lp, SYSTEMD_SYSTEM, true, NULL, NULL, NULL, NULL);
942 if (r < 0) {
943 log_error("Failed to find lookup paths.");
944 return EXIT_FAILURE;
945 }
946
947 all_services = hashmap_new(&string_hash_ops);
948 if (!all_services) {
949 log_oom();
950 return EXIT_FAILURE;
951 }
952
953 r = enumerate_sysv(&lp, all_services);
954 if (r < 0) {
955 log_error("Failed to generate units for all init scripts.");
956 return EXIT_FAILURE;
957 }
958
959 r = set_dependencies_from_rcnd(&lp, all_services);
960 if (r < 0) {
961 log_error("Failed to read runlevels from rcnd links.");
962 return EXIT_FAILURE;
963 }
964
965 HASHMAP_FOREACH(service, all_services, j) {
966 q = load_sysv(service);
967 if (q < 0)
968 continue;
969 }
970
971 HASHMAP_FOREACH(service, all_services, j) {
972 q = fix_order(service, all_services);
973 if (q < 0)
974 continue;
975
976 q = generate_unit_file(service);
977 if (q < 0)
978 continue;
979 }
980
981 return EXIT_SUCCESS;
982 }