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