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