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