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