]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysv-generator/sysv-generator.c
sysv-generator: Provides: $network should also pull network.target to transaction...
[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 r = strv_extend(&s->wants, SPECIAL_NETWORK_TARGET);
393 if (r < 0)
394 return log_oom();
395 }
396
397 break;
398
399 case _UNIT_TYPE_INVALID:
400 log_warning("Unit name '%s' is invalid", m);
401 break;
402
403 default:
404 log_warning("Unknown unit type for unit '%s'", m);
405 }
406 }
407
408 return 0;
409 }
410
411 static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) {
412 int r;
413
414 assert(s);
415 assert(full_text);
416 assert(text);
417
418 for (;;) {
419 _cleanup_free_ char *word = NULL, *m = NULL;
420 bool is_before;
421
422 r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
423 if (r < 0)
424 return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
425 if (r == 0)
426 break;
427
428 r = sysv_translate_facility(s, line, word, &m);
429 if (r <= 0) /* continue on error */
430 continue;
431
432 is_before = startswith_no_case(full_text, "X-Start-Before:");
433
434 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
435 /* the network-online target is special, as it needs to be actively pulled in */
436 r = strv_extend(&s->after, m);
437 if (r < 0)
438 return log_oom();
439
440 r = strv_extend(&s->wants, m);
441 } else
442 r = strv_extend(is_before ? &s->before : &s->after, m);
443 if (r < 0)
444 return log_oom();
445 }
446
447 return 0;
448 }
449
450 static int load_sysv(SysvStub *s) {
451 _cleanup_fclose_ FILE *f;
452 unsigned line = 0;
453 int r;
454 enum {
455 NORMAL,
456 DESCRIPTION,
457 LSB,
458 LSB_DESCRIPTION,
459 USAGE_CONTINUATION
460 } state = NORMAL;
461 _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
462 char *description;
463 bool supports_reload = false;
464 char l[LINE_MAX];
465
466 assert(s);
467
468 f = fopen(s->path, "re");
469 if (!f) {
470 if (errno == ENOENT)
471 return 0;
472
473 return log_error_errno(errno, "Failed to open %s: %m", s->path);
474 }
475
476 log_debug("Loading SysV script %s", s->path);
477
478 FOREACH_LINE(l, f, goto fail) {
479 char *t;
480
481 line++;
482
483 t = strstrip(l);
484 if (*t != '#') {
485 /* Try to figure out whether this init script supports
486 * the reload operation. This heuristic looks for
487 * "Usage" lines which include the reload option. */
488 if ( state == USAGE_CONTINUATION ||
489 (state == NORMAL && strcasestr(t, "usage"))) {
490 if (usage_contains_reload(t)) {
491 supports_reload = true;
492 state = NORMAL;
493 } else if (t[strlen(t)-1] == '\\')
494 state = USAGE_CONTINUATION;
495 else
496 state = NORMAL;
497 }
498
499 continue;
500 }
501
502 if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
503 state = LSB;
504 s->has_lsb = true;
505 continue;
506 }
507
508 if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) {
509 state = NORMAL;
510 continue;
511 }
512
513 t++;
514 t += strspn(t, WHITESPACE);
515
516 if (state == NORMAL) {
517
518 /* Try to parse Red Hat style description */
519
520 if (startswith_no_case(t, "description:")) {
521
522 size_t k;
523 const char *j;
524
525 k = strlen(t);
526 if (k > 0 && t[k-1] == '\\') {
527 state = DESCRIPTION;
528 t[k-1] = 0;
529 }
530
531 j = empty_to_null(strstrip(t+12));
532
533 r = free_and_strdup(&chkconfig_description, j);
534 if (r < 0)
535 return log_oom();
536
537 } else if (startswith_no_case(t, "pidfile:")) {
538 const char *fn;
539
540 state = NORMAL;
541
542 fn = strstrip(t+8);
543 if (!path_is_absolute(fn)) {
544 log_error("[%s:%u] PID file not absolute. Ignoring.", s->path, line);
545 continue;
546 }
547
548 r = free_and_strdup(&s->pid_file, fn);
549 if (r < 0)
550 return log_oom();
551 }
552
553 } else if (state == DESCRIPTION) {
554
555 /* Try to parse Red Hat style description
556 * continuation */
557
558 size_t k;
559 char *j;
560
561 k = strlen(t);
562 if (k > 0 && t[k-1] == '\\')
563 t[k-1] = 0;
564 else
565 state = NORMAL;
566
567 j = strstrip(t);
568 if (!isempty(j)) {
569 char *d = NULL;
570
571 if (chkconfig_description)
572 d = strjoin(chkconfig_description, " ", j);
573 else
574 d = strdup(j);
575 if (!d)
576 return log_oom();
577
578 free(chkconfig_description);
579 chkconfig_description = d;
580 }
581
582 } else if (state == LSB || state == LSB_DESCRIPTION) {
583
584 if (startswith_no_case(t, "Provides:")) {
585 state = LSB;
586
587 r = handle_provides(s, line, t, t + 9);
588 if (r < 0)
589 return r;
590
591 } else if (startswith_no_case(t, "Required-Start:") ||
592 startswith_no_case(t, "Should-Start:") ||
593 startswith_no_case(t, "X-Start-Before:") ||
594 startswith_no_case(t, "X-Start-After:")) {
595
596 state = LSB;
597
598 r = handle_dependencies(s, line, t, strchr(t, ':') + 1);
599 if (r < 0)
600 return r;
601
602 } else if (startswith_no_case(t, "Description:")) {
603 const char *j;
604
605 state = LSB_DESCRIPTION;
606
607 j = empty_to_null(strstrip(t+12));
608
609 r = free_and_strdup(&long_description, j);
610 if (r < 0)
611 return log_oom();
612
613 } else if (startswith_no_case(t, "Short-Description:")) {
614 const char *j;
615
616 state = LSB;
617
618 j = empty_to_null(strstrip(t+18));
619
620 r = free_and_strdup(&short_description, j);
621 if (r < 0)
622 return log_oom();
623
624 } else if (state == LSB_DESCRIPTION) {
625
626 if (startswith(l, "#\t") || startswith(l, "# ")) {
627 const char *j;
628
629 j = strstrip(t);
630 if (!isempty(j)) {
631 char *d = NULL;
632
633 if (long_description)
634 d = strjoin(long_description, " ", t);
635 else
636 d = strdup(j);
637 if (!d)
638 return log_oom();
639
640 free(long_description);
641 long_description = d;
642 }
643
644 } else
645 state = LSB;
646 }
647 }
648 }
649
650 s->reload = supports_reload;
651
652 /* We use the long description only if
653 * no short description is set. */
654
655 if (short_description)
656 description = short_description;
657 else if (chkconfig_description)
658 description = chkconfig_description;
659 else if (long_description)
660 description = long_description;
661 else
662 description = NULL;
663
664 if (description) {
665 char *d;
666
667 d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
668 if (!d)
669 return log_oom();
670
671 s->description = d;
672 }
673
674 s->loaded = true;
675 return 0;
676
677 fail:
678 return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path);
679 }
680
681 static int fix_order(SysvStub *s, Hashmap *all_services) {
682 SysvStub *other;
683 Iterator j;
684 int r;
685
686 assert(s);
687
688 if (!s->loaded)
689 return 0;
690
691 if (s->sysv_start_priority < 0)
692 return 0;
693
694 HASHMAP_FOREACH(other, all_services, j) {
695 if (s == other)
696 continue;
697
698 if (!other->loaded)
699 continue;
700
701 if (other->sysv_start_priority < 0)
702 continue;
703
704 /* If both units have modern headers we don't care
705 * about the priorities */
706 if (s->has_lsb && other->has_lsb)
707 continue;
708
709 if (other->sysv_start_priority < s->sysv_start_priority) {
710 r = strv_extend(&s->after, other->name);
711 if (r < 0)
712 return log_oom();
713
714 } else if (other->sysv_start_priority > s->sysv_start_priority) {
715 r = strv_extend(&s->before, other->name);
716 if (r < 0)
717 return log_oom();
718 } else
719 continue;
720
721 /* FIXME: Maybe we should compare the name here lexicographically? */
722 }
723
724 return 0;
725 }
726
727 static int acquire_search_path(const char *def, const char *envvar, char ***ret) {
728 _cleanup_strv_free_ char **l = NULL;
729 const char *e;
730 int r;
731
732 assert(def);
733 assert(envvar);
734
735 e = getenv(envvar);
736 if (e) {
737 r = path_split_and_make_absolute(e, &l);
738 if (r < 0)
739 return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar);
740 }
741
742 if (strv_isempty(l)) {
743 strv_free(l);
744
745 l = strv_new(def, NULL);
746 if (!l)
747 return log_oom();
748 }
749
750 if (!path_strv_resolve_uniq(l, NULL))
751 return log_oom();
752
753 *ret = l;
754 l = NULL;
755
756 return 0;
757 }
758
759 static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
760 _cleanup_strv_free_ char **sysvinit_path = NULL;
761 char **path;
762 int r;
763
764 assert(lp);
765
766 r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path);
767 if (r < 0)
768 return r;
769
770 STRV_FOREACH(path, sysvinit_path) {
771 _cleanup_closedir_ DIR *d = NULL;
772 struct dirent *de;
773
774 d = opendir(*path);
775 if (!d) {
776 if (errno != ENOENT)
777 log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path);
778 continue;
779 }
780
781 FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
782 _cleanup_free_ char *fpath = NULL, *name = NULL;
783 _cleanup_(free_sysvstubp) SysvStub *service = NULL;
784 struct stat st;
785
786 if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
787 log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
788 continue;
789 }
790
791 if (!(st.st_mode & S_IXUSR))
792 continue;
793
794 if (!S_ISREG(st.st_mode))
795 continue;
796
797 name = sysv_translate_name(de->d_name);
798 if (!name)
799 return log_oom();
800
801 if (hashmap_contains(all_services, name))
802 continue;
803
804 r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name);
805 if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) {
806 log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
807 continue;
808 } else if (r != 0) {
809 log_debug("Native unit for %s already exists, skipping.", name);
810 continue;
811 }
812
813 fpath = strjoin(*path, "/", de->d_name);
814 if (!fpath)
815 return log_oom();
816
817 service = new0(SysvStub, 1);
818 if (!service)
819 return log_oom();
820
821 service->sysv_start_priority = -1;
822 service->name = name;
823 service->path = fpath;
824 name = fpath = NULL;
825
826 r = hashmap_put(all_services, service->name, service);
827 if (r < 0)
828 return log_oom();
829
830 service = NULL;
831 }
832 }
833
834 return 0;
835 }
836
837 static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
838 Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
839 _cleanup_strv_free_ char **sysvrcnd_path = NULL;
840 SysvStub *service;
841 unsigned i;
842 Iterator j;
843 char **p;
844 int r;
845
846 assert(lp);
847
848 r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path);
849 if (r < 0)
850 return r;
851
852 STRV_FOREACH(p, sysvrcnd_path) {
853 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
854
855 _cleanup_closedir_ DIR *d = NULL;
856 _cleanup_free_ char *path = NULL;
857 struct dirent *de;
858
859 path = strjoin(*p, "/", rcnd_table[i].path);
860 if (!path) {
861 r = log_oom();
862 goto finish;
863 }
864
865 d = opendir(path);
866 if (!d) {
867 if (errno != ENOENT)
868 log_warning_errno(errno, "Opening %s failed, ignoring: %m", path);
869
870 continue;
871 }
872
873 FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
874 _cleanup_free_ char *name = NULL, *fpath = NULL;
875 int a, b;
876
877 if (de->d_name[0] != 'S')
878 continue;
879
880 if (strlen(de->d_name) < 4)
881 continue;
882
883 a = undecchar(de->d_name[1]);
884 b = undecchar(de->d_name[2]);
885
886 if (a < 0 || b < 0)
887 continue;
888
889 fpath = strjoin(*p, "/", de->d_name);
890 if (!fpath) {
891 r = log_oom();
892 goto finish;
893 }
894
895 name = sysv_translate_name(de->d_name + 3);
896 if (!name) {
897 r = log_oom();
898 goto finish;
899 }
900
901 service = hashmap_get(all_services, name);
902 if (!service) {
903 log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name);
904 continue;
905 }
906
907 service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
908
909 r = set_ensure_allocated(&runlevel_services[i], NULL);
910 if (r < 0) {
911 log_oom();
912 goto finish;
913 }
914
915 r = set_put(runlevel_services[i], service);
916 if (r < 0) {
917 log_oom();
918 goto finish;
919 }
920 }
921 }
922 }
923
924 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
925 SET_FOREACH(service, runlevel_services[i], j) {
926 r = strv_extend(&service->before, rcnd_table[i].target);
927 if (r < 0) {
928 log_oom();
929 goto finish;
930 }
931 r = strv_extend(&service->wanted_by, rcnd_table[i].target);
932 if (r < 0) {
933 log_oom();
934 goto finish;
935 }
936 }
937
938 r = 0;
939
940 finish:
941 for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
942 set_free(runlevel_services[i]);
943
944 return r;
945 }
946
947 int main(int argc, char *argv[]) {
948 _cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
949 _cleanup_lookup_paths_free_ LookupPaths lp = {};
950 SysvStub *service;
951 Iterator j;
952 int r;
953
954 if (argc > 1 && argc != 4) {
955 log_error("This program takes three or no arguments.");
956 return EXIT_FAILURE;
957 }
958
959 if (argc > 1)
960 arg_dest = argv[3];
961
962 log_set_target(LOG_TARGET_SAFE);
963 log_parse_environment();
964 log_open();
965
966 umask(0022);
967
968 r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
969 if (r < 0) {
970 log_error_errno(r, "Failed to find lookup paths: %m");
971 goto finish;
972 }
973
974 all_services = hashmap_new(&string_hash_ops);
975 if (!all_services) {
976 r = log_oom();
977 goto finish;
978 }
979
980 r = enumerate_sysv(&lp, all_services);
981 if (r < 0)
982 goto finish;
983
984 r = set_dependencies_from_rcnd(&lp, all_services);
985 if (r < 0)
986 goto finish;
987
988 HASHMAP_FOREACH(service, all_services, j)
989 (void) load_sysv(service);
990
991 HASHMAP_FOREACH(service, all_services, j) {
992 (void) fix_order(service, all_services);
993 (void) generate_unit_file(service);
994 }
995
996 r = 0;
997
998 finish:
999 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1000 }