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