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