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