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