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