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