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