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