]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysv-generator/sysv-generator.c
tree-wide: make more global variables static
[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 enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
733 char **path;
734 int r;
735
736 assert(lp);
737 assert(all_services);
738
739 STRV_FOREACH(path, lp->sysvinit_path) {
740 _cleanup_closedir_ DIR *d = NULL;
741 struct dirent *de;
742
743 d = opendir(*path);
744 if (!d) {
745 if (errno != ENOENT)
746 log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path);
747 continue;
748 }
749
750 FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
751 _cleanup_free_ char *fpath = NULL, *name = NULL;
752 _cleanup_(free_sysvstubp) SysvStub *service = NULL;
753 struct stat st;
754
755 if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
756 log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
757 continue;
758 }
759
760 if (!(st.st_mode & S_IXUSR))
761 continue;
762
763 if (!S_ISREG(st.st_mode))
764 continue;
765
766 name = sysv_translate_name(de->d_name);
767 if (!name)
768 return log_oom();
769
770 if (hashmap_contains(all_services, name))
771 continue;
772
773 r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL);
774 if (r < 0 && r != -ENOENT) {
775 log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
776 continue;
777 } else if (r >= 0) {
778 log_debug("Native unit for %s already exists, skipping.", name);
779 continue;
780 }
781
782 fpath = strjoin(*path, "/", de->d_name, NULL);
783 if (!fpath)
784 return log_oom();
785
786 service = new0(SysvStub, 1);
787 if (!service)
788 return log_oom();
789
790 service->sysv_start_priority = -1;
791 service->name = name;
792 service->path = fpath;
793 name = fpath = NULL;
794
795 r = hashmap_put(all_services, service->name, service);
796 if (r < 0)
797 return log_oom();
798
799 service = NULL;
800 }
801 }
802
803 return 0;
804 }
805
806 static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
807 Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
808 _cleanup_set_free_ Set *shutdown_services = NULL;
809 SysvStub *service;
810 unsigned i;
811 Iterator j;
812 char **p;
813 int r;
814
815 assert(lp);
816
817 STRV_FOREACH(p, lp->sysvrcnd_path) {
818 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
819
820 _cleanup_closedir_ DIR *d = NULL;
821 _cleanup_free_ char *path = NULL;
822 struct dirent *de;
823
824 path = strjoin(*p, "/", rcnd_table[i].path, NULL);
825 if (!path) {
826 r = log_oom();
827 goto finish;
828 }
829
830 d = opendir(path);
831 if (!d) {
832 if (errno != ENOENT)
833 log_warning_errno(errno, "Opening %s failed, ignoring: %m", path);
834
835 continue;
836 }
837
838 FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
839 _cleanup_free_ char *name = NULL, *fpath = NULL;
840 int a, b;
841
842 if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
843 continue;
844
845 if (strlen(de->d_name) < 4)
846 continue;
847
848 a = undecchar(de->d_name[1]);
849 b = undecchar(de->d_name[2]);
850
851 if (a < 0 || b < 0)
852 continue;
853
854 fpath = strjoin(*p, "/", de->d_name, NULL);
855 if (!fpath) {
856 r = log_oom();
857 goto finish;
858 }
859
860 name = sysv_translate_name(de->d_name + 3);
861 if (!name) {
862 r = log_oom();
863 goto finish;
864 }
865
866 service = hashmap_get(all_services, name);
867 if (!service){
868 log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name);
869 continue;
870 }
871
872 if (de->d_name[0] == 'S') {
873
874 if (rcnd_table[i].type == RUNLEVEL_UP)
875 service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
876
877 r = set_ensure_allocated(&runlevel_services[i], NULL);
878 if (r < 0) {
879 log_oom();
880 goto finish;
881 }
882
883 r = set_put(runlevel_services[i], service);
884 if (r < 0) {
885 log_oom();
886 goto finish;
887 }
888
889 } else if (de->d_name[0] == 'K' &&
890 (rcnd_table[i].type == RUNLEVEL_DOWN)) {
891
892 r = set_ensure_allocated(&shutdown_services, NULL);
893 if (r < 0) {
894 log_oom();
895 goto finish;
896 }
897
898 r = set_put(shutdown_services, service);
899 if (r < 0) {
900 log_oom();
901 goto finish;
902 }
903 }
904 }
905 }
906 }
907
908
909 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
910 SET_FOREACH(service, runlevel_services[i], j) {
911 r = strv_extend(&service->before, rcnd_table[i].target);
912 if (r < 0) {
913 log_oom();
914 goto finish;
915 }
916 r = strv_extend(&service->wanted_by, rcnd_table[i].target);
917 if (r < 0) {
918 log_oom();
919 goto finish;
920 }
921 }
922
923 SET_FOREACH(service, shutdown_services, j) {
924 r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
925 if (r < 0) {
926 log_oom();
927 goto finish;
928 }
929 r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
930 if (r < 0) {
931 log_oom();
932 goto finish;
933 }
934 }
935
936 r = 0;
937
938 finish:
939 for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
940 set_free(runlevel_services[i]);
941
942 return r;
943 }
944
945 int main(int argc, char *argv[]) {
946 _cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
947 _cleanup_lookup_paths_free_ LookupPaths lp = {};
948 SysvStub *service;
949 Iterator j;
950 int r;
951
952 if (argc > 1 && argc != 4) {
953 log_error("This program takes three or no arguments.");
954 return EXIT_FAILURE;
955 }
956
957 if (argc > 1)
958 arg_dest = argv[3];
959
960 log_set_target(LOG_TARGET_SAFE);
961 log_parse_environment();
962 log_open();
963
964 umask(0022);
965
966 r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL);
967 if (r < 0) {
968 log_error_errno(r, "Failed to find lookup paths: %m");
969 goto finish;
970 }
971
972 all_services = hashmap_new(&string_hash_ops);
973 if (!all_services) {
974 r = log_oom();
975 goto finish;
976 }
977
978 r = enumerate_sysv(&lp, all_services);
979 if (r < 0)
980 goto finish;
981
982 r = set_dependencies_from_rcnd(&lp, all_services);
983 if (r < 0)
984 goto finish;
985
986 HASHMAP_FOREACH(service, all_services, j)
987 (void) load_sysv(service);
988
989 HASHMAP_FOREACH(service, all_services, j) {
990 (void) fix_order(service, all_services);
991 (void) generate_unit_file(service);
992 }
993
994 r = 0;
995
996 finish:
997 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
998 }