]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysv-generator/sysv-generator.c
sysv-generator: fix coding-style
[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, AT_SYMLINK_NOFOLLOW) < 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 if (d)
810 closedir(d);
811
812 d = opendir(path);
813 if (!d) {
814 if (errno != ENOENT)
815 log_warning_errno(errno, "opendir(%s) failed: %m", path);
816
817 continue;
818 }
819
820 while ((de = readdir(d))) {
821 _cleanup_free_ char *name = NULL;
822
823 int a, b;
824
825 if (hidden_file(de->d_name))
826 continue;
827
828 if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
829 continue;
830
831 if (strlen(de->d_name) < 4)
832 continue;
833
834 a = undecchar(de->d_name[1]);
835 b = undecchar(de->d_name[2]);
836
837 if (a < 0 || b < 0)
838 continue;
839
840 free(fpath);
841 fpath = strjoin(*p, "/", de->d_name, NULL);
842 if (!fpath) {
843 r = -ENOMEM;
844 goto finish;
845 }
846
847 name = sysv_translate_name(de->d_name + 3);
848 if (!name) {
849 r = log_oom();
850 goto finish;
851 }
852
853 service = hashmap_get(all_services, name);
854 if (!service){
855 log_debug("Ignoring %s symlink in %s, not generating %s.",
856 de->d_name, rcnd_table[i].path, name);
857 continue;
858 }
859
860 if (de->d_name[0] == 'S') {
861
862 if (rcnd_table[i].type == RUNLEVEL_UP) {
863 service->sysv_start_priority =
864 MAX(a*10 + b, service->sysv_start_priority);
865 }
866
867 r = set_ensure_allocated(&runlevel_services[i], NULL);
868 if (r < 0)
869 goto finish;
870
871 r = set_put(runlevel_services[i], service);
872 if (r < 0)
873 goto finish;
874
875 } else if (de->d_name[0] == 'K' &&
876 (rcnd_table[i].type == RUNLEVEL_DOWN)) {
877
878 r = set_ensure_allocated(&shutdown_services, NULL);
879 if (r < 0)
880 goto finish;
881
882 r = set_put(shutdown_services, service);
883 if (r < 0)
884 goto finish;
885 }
886 }
887 }
888
889
890 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
891 SET_FOREACH(service, runlevel_services[i], j) {
892 r = strv_extend(&service->before, rcnd_table[i].target);
893 if (r < 0)
894 return log_oom();
895 r = strv_extend(&service->wanted_by, rcnd_table[i].target);
896 if (r < 0)
897 return log_oom();
898 }
899
900 SET_FOREACH(service, shutdown_services, j) {
901 r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
902 if (r < 0)
903 return log_oom();
904 r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
905 if (r < 0)
906 return log_oom();
907 }
908
909 r = 0;
910
911 finish:
912
913 for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
914 set_free(runlevel_services[i]);
915
916 return r;
917 }
918
919 int main(int argc, char *argv[]) {
920 int r, q;
921 _cleanup_lookup_paths_free_ LookupPaths lp = {};
922 _cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
923 SysvStub *service;
924 Iterator j;
925
926 if (argc > 1 && argc != 4) {
927 log_error("This program takes three or no arguments.");
928 return EXIT_FAILURE;
929 }
930
931 if (argc > 1)
932 arg_dest = argv[3];
933
934 log_set_target(LOG_TARGET_SAFE);
935 log_parse_environment();
936 log_open();
937
938 umask(0022);
939
940 r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL);
941 if (r < 0) {
942 log_error("Failed to find lookup paths.");
943 return EXIT_FAILURE;
944 }
945
946 all_services = hashmap_new(&string_hash_ops);
947 if (!all_services) {
948 log_oom();
949 return EXIT_FAILURE;
950 }
951
952 r = enumerate_sysv(&lp, all_services);
953 if (r < 0) {
954 log_error("Failed to generate units for all init scripts.");
955 return EXIT_FAILURE;
956 }
957
958 r = set_dependencies_from_rcnd(&lp, all_services);
959 if (r < 0) {
960 log_error("Failed to read runlevels from rcnd links.");
961 return EXIT_FAILURE;
962 }
963
964 HASHMAP_FOREACH(service, all_services, j) {
965 q = load_sysv(service);
966 if (q < 0)
967 continue;
968 }
969
970 HASHMAP_FOREACH(service, all_services, j) {
971 q = fix_order(service, all_services);
972 if (q < 0)
973 continue;
974
975 q = generate_unit_file(service);
976 if (q < 0)
977 continue;
978 }
979
980 return EXIT_SUCCESS;
981 }