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