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