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