]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysv-generator/sysv-generator.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[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, 0, &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 = TAKE_PTR(l);
745
746 return 0;
747 }
748
749 static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
750 _cleanup_strv_free_ char **sysvinit_path = NULL;
751 char **path;
752 int r;
753
754 assert(lp);
755
756 r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path);
757 if (r < 0)
758 return r;
759
760 STRV_FOREACH(path, sysvinit_path) {
761 _cleanup_closedir_ DIR *d = NULL;
762 struct dirent *de;
763
764 d = opendir(*path);
765 if (!d) {
766 if (errno != ENOENT)
767 log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path);
768 continue;
769 }
770
771 FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
772 _cleanup_free_ char *fpath = NULL, *name = NULL;
773 _cleanup_(free_sysvstubp) SysvStub *service = NULL;
774 struct stat st;
775
776 if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
777 log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
778 continue;
779 }
780
781 if (!(st.st_mode & S_IXUSR))
782 continue;
783
784 if (!S_ISREG(st.st_mode))
785 continue;
786
787 name = sysv_translate_name(de->d_name);
788 if (!name)
789 return log_oom();
790
791 if (hashmap_contains(all_services, name))
792 continue;
793
794 r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name);
795 if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) {
796 log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
797 continue;
798 } else if (r != 0) {
799 log_debug("Native unit for %s already exists, skipping.", name);
800 continue;
801 }
802
803 fpath = strjoin(*path, "/", de->d_name);
804 if (!fpath)
805 return log_oom();
806
807 service = new0(SysvStub, 1);
808 if (!service)
809 return log_oom();
810
811 service->sysv_start_priority = -1;
812 service->name = TAKE_PTR(name);
813 service->path = TAKE_PTR(fpath);
814
815 r = hashmap_put(all_services, service->name, service);
816 if (r < 0)
817 return log_oom();
818
819 service = NULL;
820 }
821 }
822
823 return 0;
824 }
825
826 static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
827 Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
828 _cleanup_strv_free_ char **sysvrcnd_path = NULL;
829 SysvStub *service;
830 unsigned i;
831 Iterator j;
832 char **p;
833 int r;
834
835 assert(lp);
836
837 r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path);
838 if (r < 0)
839 return r;
840
841 STRV_FOREACH(p, sysvrcnd_path) {
842 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
843
844 _cleanup_closedir_ DIR *d = NULL;
845 _cleanup_free_ char *path = NULL;
846 struct dirent *de;
847
848 path = strjoin(*p, "/", rcnd_table[i].path);
849 if (!path) {
850 r = log_oom();
851 goto finish;
852 }
853
854 d = opendir(path);
855 if (!d) {
856 if (errno != ENOENT)
857 log_warning_errno(errno, "Opening %s failed, ignoring: %m", path);
858
859 continue;
860 }
861
862 FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
863 _cleanup_free_ char *name = NULL, *fpath = NULL;
864 int a, b;
865
866 if (de->d_name[0] != 'S')
867 continue;
868
869 if (strlen(de->d_name) < 4)
870 continue;
871
872 a = undecchar(de->d_name[1]);
873 b = undecchar(de->d_name[2]);
874
875 if (a < 0 || b < 0)
876 continue;
877
878 fpath = strjoin(*p, "/", de->d_name);
879 if (!fpath) {
880 r = log_oom();
881 goto finish;
882 }
883
884 name = sysv_translate_name(de->d_name + 3);
885 if (!name) {
886 r = log_oom();
887 goto finish;
888 }
889
890 service = hashmap_get(all_services, name);
891 if (!service) {
892 log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name);
893 continue;
894 }
895
896 service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
897
898 r = set_ensure_allocated(&runlevel_services[i], NULL);
899 if (r < 0) {
900 log_oom();
901 goto finish;
902 }
903
904 r = set_put(runlevel_services[i], service);
905 if (r < 0) {
906 log_oom();
907 goto finish;
908 }
909 }
910 }
911 }
912
913 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
914 SET_FOREACH(service, runlevel_services[i], j) {
915 r = strv_extend(&service->before, rcnd_table[i].target);
916 if (r < 0) {
917 log_oom();
918 goto finish;
919 }
920 r = strv_extend(&service->wanted_by, rcnd_table[i].target);
921 if (r < 0) {
922 log_oom();
923 goto finish;
924 }
925 }
926
927 r = 0;
928
929 finish:
930 for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
931 set_free(runlevel_services[i]);
932
933 return r;
934 }
935
936 int main(int argc, char *argv[]) {
937 _cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
938 _cleanup_lookup_paths_free_ LookupPaths lp = {};
939 SysvStub *service;
940 Iterator j;
941 int r;
942
943 if (argc > 1 && argc != 4) {
944 log_error("This program takes three or no arguments.");
945 return EXIT_FAILURE;
946 }
947
948 if (argc > 1)
949 arg_dest = argv[3];
950
951 log_set_prohibit_ipc(true);
952 log_set_target(LOG_TARGET_AUTO);
953 log_parse_environment();
954 log_open();
955
956 umask(0022);
957
958 r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
959 if (r < 0) {
960 log_error_errno(r, "Failed to find lookup paths: %m");
961 goto finish;
962 }
963
964 all_services = hashmap_new(&string_hash_ops);
965 if (!all_services) {
966 r = log_oom();
967 goto finish;
968 }
969
970 r = enumerate_sysv(&lp, all_services);
971 if (r < 0)
972 goto finish;
973
974 r = set_dependencies_from_rcnd(&lp, all_services);
975 if (r < 0)
976 goto finish;
977
978 HASHMAP_FOREACH(service, all_services, j)
979 (void) load_sysv(service);
980
981 HASHMAP_FOREACH(service, all_services, j) {
982 (void) fix_order(service, all_services);
983 (void) generate_unit_file(service);
984 }
985
986 r = 0;
987
988 finish:
989 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
990 }