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