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