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