1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Thomas H.P. Andersen
7 Copyright 2010 Lennart Poettering
8 Copyright 2011 Michal Schmidt
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.
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.
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/>.
31 #include "path-util.h"
32 #include "path-lookup.h"
35 #include "unit-name.h"
37 #include "exit-status.h"
43 typedef enum RunlevelType {
51 const RunlevelType type;
53 /* Standard SysV runlevels for start-up */
54 { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
55 { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP },
56 { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP },
57 { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
58 { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
60 /* Standard SysV runlevels for shutdown */
61 { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
62 { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
64 /* Note that the order here matters, as we read the
65 directories in this order, and we want to make sure that
66 sysv_start_priority is known when we first load the
67 unit. And that value we only know from S links. Hence
68 UP must be read before DOWN */
71 typedef struct SysvStub {
75 int sysv_start_priority;
86 const char *arg_dest = "/tmp";
88 static int add_symlink(const char *service, const char *where) {
89 _cleanup_free_ char *from = NULL, *to = NULL;
95 from = strjoin(arg_dest, "/", service, NULL);
99 to = strjoin(arg_dest, "/", where, ".wants/", service, NULL);
103 mkdir_parents_label(to, 0755);
105 r = symlink(from, to);
115 static int add_alias(const char *service, const char *alias) {
116 _cleanup_free_ char *link = NULL;
122 link = strjoin(arg_dest, "/", alias, NULL);
126 r = symlink(service, link);
136 static int generate_unit_file(SysvStub *s) {
138 _cleanup_fclose_ FILE *f = NULL;
139 _cleanup_free_ char *unit = NULL;
140 _cleanup_free_ char *before = NULL;
141 _cleanup_free_ char *after = NULL;
142 _cleanup_free_ char *wants = NULL;
143 _cleanup_free_ char *conflicts = NULL;
146 before = strv_join(s->before, " ");
150 after = strv_join(s->after, " ");
154 wants = strv_join(s->wants, " ");
158 conflicts = strv_join(s->conflicts, " ");
162 unit = strjoin(arg_dest, "/", s->name, NULL);
166 /* We might already have a symlink with the same name from a Provides:,
167 * or from backup files like /etc/init.d/foo.bak. Real scripts always win,
168 * so remove an existing link */
169 if (is_symlink(unit)) {
170 log_warning("Overwriting existing symlink %s with real service", unit);
174 f = fopen(unit, "wxe");
176 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
179 "# Automatically generated by systemd-sysv-generator\n\n"
181 "Documentation=man:systemd-sysv-generator(8)\n"
184 s->path, s->description);
186 if (!isempty(before))
187 fprintf(f, "Before=%s\n", before);
189 fprintf(f, "After=%s\n", after);
191 fprintf(f, "Wants=%s\n", wants);
192 if (!isempty(conflicts))
193 fprintf(f, "Conflicts=%s\n", conflicts);
203 "RemainAfterExit=%s\n",
204 yes_no(!s->pid_file));
207 fprintf(f, "PIDFile=%s\n", s->pid_file);
210 "ExecStart=%s start\n"
211 "ExecStop=%s stop\n",
215 fprintf(f, "ExecReload=%s reload\n", s->path);
217 STRV_FOREACH(p, s->wanted_by) {
218 r = add_symlink(s->name, *p);
220 log_unit_error_errno(s->name, r, "Failed to create 'Wants' symlink to %s: %m", *p);
226 static bool usage_contains_reload(const char *line) {
227 return (strcasestr(line, "{reload|") ||
228 strcasestr(line, "{reload}") ||
229 strcasestr(line, "{reload\"") ||
230 strcasestr(line, "|reload|") ||
231 strcasestr(line, "|reload}") ||
232 strcasestr(line, "|reload\""));
235 static char *sysv_translate_name(const char *name) {
238 r = new(char, strlen(name) + strlen(".service") + 1);
242 if (endswith(name, ".sh"))
243 /* Drop .sh suffix */
244 strcpy(stpcpy(r, name) - 3, ".service");
246 /* Normal init script name */
247 strcpy(stpcpy(r, name), ".service");
252 static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
254 /* We silently ignore the $ prefix here. According to the LSB
255 * spec it simply indicates whether something is a
256 * standardized name or a distribution-specific one. Since we
257 * just follow what already exists and do not introduce new
258 * uses or names we don't care who introduced a new name. */
260 static const char * const table[] = {
261 /* LSB defined facilities */
263 "network", SPECIAL_NETWORK_ONLINE_TARGET,
264 "named", SPECIAL_NSS_LOOKUP_TARGET,
265 "portmap", SPECIAL_RPCBIND_TARGET,
266 "remote_fs", SPECIAL_REMOTE_FS_TARGET,
268 "time", SPECIAL_TIME_SYNC_TARGET,
271 char *filename_no_sh, *e, *r;
278 n = *name == '$' ? name + 1 : name;
280 for (i = 0; i < ELEMENTSOF(table); i += 2) {
282 if (!streq(table[i], n))
288 r = strdup(table[i+1]);
295 /* strip ".sh" suffix from file name for comparison */
296 filename_no_sh = strdupa(filename);
297 e = endswith(filename_no_sh, ".sh");
300 filename = filename_no_sh;
303 /* If we don't know this name, fallback heuristics to figure
304 * out whether something is a target or a service alias. */
307 if (!unit_prefix_is_valid(n))
310 /* Facilities starting with $ are most likely targets */
311 r = unit_name_build(n, NULL, ".target");
312 } else if (streq_ptr(n, filename))
313 /* Names equaling the file name of the services are redundant */
316 /* Everything else we assume to be normal service names */
317 r = sysv_translate_name(n);
327 static int load_sysv(SysvStub *s) {
328 _cleanup_fclose_ FILE *f;
338 _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
340 bool supports_reload = false;
344 f = fopen(s->path, "re");
346 return errno == ENOENT ? 0 : -errno;
348 log_debug("Loading SysV script %s", s->path);
351 char l[LINE_MAX], *t;
353 if (!fgets(l, sizeof(l), f)) {
357 log_unit_error(s->name,
358 "Failed to read configuration file '%s': %m",
367 /* Try to figure out whether this init script supports
368 * the reload operation. This heuristic looks for
369 * "Usage" lines which include the reload option. */
370 if ( state == USAGE_CONTINUATION ||
371 (state == NORMAL && strcasestr(t, "usage"))) {
372 if (usage_contains_reload(t)) {
373 supports_reload = true;
375 } else if (t[strlen(t)-1] == '\\')
376 state = USAGE_CONTINUATION;
384 if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
390 if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) {
396 t += strspn(t, WHITESPACE);
398 if (state == NORMAL) {
400 /* Try to parse Red Hat style description */
402 if (startswith_no_case(t, "description:")) {
404 size_t k = strlen(t);
408 if (t[k-1] == '\\') {
421 free(chkconfig_description);
422 chkconfig_description = d;
424 } else if (startswith_no_case(t, "pidfile:")) {
431 if (!path_is_absolute(fn)) {
432 log_unit_error(s->name,
433 "[%s:%u] PID file not absolute. Ignoring.",
446 } else if (state == DESCRIPTION) {
448 /* Try to parse Red Hat style description
451 size_t k = strlen(t);
463 if (chkconfig_description)
464 d = strjoin(chkconfig_description, " ", j, NULL);
471 free(chkconfig_description);
472 chkconfig_description = d;
475 } else if (state == LSB || state == LSB_DESCRIPTION) {
477 if (startswith_no_case(t, "Provides:")) {
478 const char *word, *state_;
483 FOREACH_WORD_QUOTED(word, z, t+9, state_) {
484 _cleanup_free_ char *n = NULL, *m = NULL;
486 n = strndup(word, z);
490 r = sysv_translate_facility(n, basename(s->path), &m);
496 if (unit_name_to_type(m) == UNIT_SERVICE) {
497 log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
498 r = add_alias(s->name, m);
505 * indication that the
507 * now available. This
510 * targets do NOT pull
513 r = strv_extend(&s->before, m);
516 r = strv_extend(&s->wants, m);
519 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
520 r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
527 log_unit_error(s->name,
528 "[%s:%u] Failed to add LSB Provides name %s, ignoring: %s",
529 s->path, line, m, strerror(-r));
531 if (!isempty(state_))
532 log_unit_error(s->name,
533 "[%s:%u] Trailing garbage in Provides, ignoring.",
536 } else if (startswith_no_case(t, "Required-Start:") ||
537 startswith_no_case(t, "Should-Start:") ||
538 startswith_no_case(t, "X-Start-Before:") ||
539 startswith_no_case(t, "X-Start-After:")) {
540 const char *word, *state_;
545 FOREACH_WORD_QUOTED(word, z, strchr(t, ':')+1, state_) {
546 _cleanup_free_ char *n = NULL, *m = NULL;
549 n = strndup(word, z);
553 r = sysv_translate_facility(n, basename(s->path), &m);
555 log_unit_error(s->name,
556 "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
557 s->path, line, n, strerror(-r));
564 is_before = startswith_no_case(t, "X-Start-Before:");
566 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
567 /* the network-online target is special, as it needs to be actively pulled in */
568 r = strv_extend(&s->after, m);
571 r = strv_extend(&s->wants, m);
577 r = strv_extend(&s->before, m);
582 r = strv_extend(&s->after, m);
589 log_unit_error(s->name,
590 "[%s:%u] Failed to add dependency on %s, ignoring: %s",
591 s->path, line, m, strerror(-r));
593 if (!isempty(state_))
594 log_unit_error(s->name,
595 "[%s:%u] Trailing garbage in %*s, ignoring.",
597 (int)(strchr(t, ':') - t), t);
599 } else if (startswith_no_case(t, "Description:")) {
602 state = LSB_DESCRIPTION;
612 free(long_description);
613 long_description = d;
615 } else if (startswith_no_case(t, "Short-Description:")) {
628 free(short_description);
629 short_description = d;
631 } else if (state == LSB_DESCRIPTION) {
633 if (startswith(l, "#\t") || startswith(l, "# ")) {
640 if (long_description)
641 d = strjoin(long_description, " ", t, NULL);
648 free(long_description);
649 long_description = d;
658 s->reload = supports_reload;
660 /* We use the long description only if
661 * no short description is set. */
663 if (short_description)
664 description = short_description;
665 else if (chkconfig_description)
666 description = chkconfig_description;
667 else if (long_description)
668 description = long_description;
675 d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
685 static int fix_order(SysvStub *s, Hashmap *all_services) {
692 if (s->sysv_start_priority < 0)
695 HASHMAP_FOREACH(other, all_services, j) {
699 if (other->sysv_start_priority < 0)
702 /* If both units have modern headers we don't care
703 * about the priorities */
704 if (s->has_lsb && other->has_lsb)
707 if (other->sysv_start_priority < s->sysv_start_priority) {
708 r = strv_extend(&s->after, other->name);
712 else if (other->sysv_start_priority > s->sysv_start_priority) {
713 r = strv_extend(&s->before, other->name);
720 /* FIXME: Maybe we should compare the name here lexicographically? */
726 static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
729 STRV_FOREACH(path, lp.sysvinit_path) {
730 _cleanup_closedir_ DIR *d = NULL;
736 log_warning_errno(errno, "opendir(%s) failed: %m", *path);
740 while ((de = readdir(d))) {
741 _cleanup_free_ char *fpath = NULL, *name = NULL;
742 _cleanup_free_ SysvStub *service = NULL;
746 if (hidden_file(de->d_name))
749 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
750 log_warning_errno(errno, "stat() failed on %s/%s: %m", *path, de->d_name);
754 if (!(st.st_mode & S_IXUSR))
757 if (!S_ISREG(st.st_mode))
760 name = sysv_translate_name(de->d_name);
764 if (hashmap_contains(all_services, name))
767 fpath = strjoin(*path, "/", de->d_name, NULL);
771 service = new0(SysvStub, 1);
775 service->sysv_start_priority = -1;
776 service->name = name;
777 service->path = fpath;
779 r = hashmap_put(all_services, service->name, service);
791 static int set_dependencies_from_rcnd(LookupPaths lp, Hashmap *all_services) {
794 _cleanup_closedir_ DIR *d = NULL;
795 _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
798 Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
799 _cleanup_set_free_ Set *shutdown_services = NULL;
802 STRV_FOREACH(p, lp.sysvrcnd_path)
803 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
807 path = strjoin(*p, "/", rcnd_table[i].path, NULL);
817 log_warning_errno(errno, "opendir(%s) failed: %m", path);
822 while ((de = readdir(d))) {
825 if (hidden_file(de->d_name))
828 if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
831 if (strlen(de->d_name) < 4)
834 a = undecchar(de->d_name[1]);
835 b = undecchar(de->d_name[2]);
841 fpath = strjoin(*p, "/", de->d_name, NULL);
847 name = sysv_translate_name(de->d_name + 3);
853 service = hashmap_get(all_services, name);
855 log_warning("Could not find init script for %s", name);
859 if (de->d_name[0] == 'S') {
861 if (rcnd_table[i].type == RUNLEVEL_UP) {
862 service->sysv_start_priority =
863 MAX(a*10 + b, service->sysv_start_priority);
866 r = set_ensure_allocated(&runlevel_services[i], NULL);
870 r = set_put(runlevel_services[i], service);
874 } else if (de->d_name[0] == 'K' &&
875 (rcnd_table[i].type == RUNLEVEL_DOWN)) {
877 r = set_ensure_allocated(&shutdown_services, NULL);
881 r = set_put(shutdown_services, service);
889 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
890 SET_FOREACH(service, runlevel_services[i], j) {
891 r = strv_extend(&service->before, rcnd_table[i].target);
894 r = strv_extend(&service->wanted_by, rcnd_table[i].target);
899 SET_FOREACH(service, shutdown_services, j) {
900 r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
903 r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
912 for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
913 set_free(runlevel_services[i]);
918 int main(int argc, char *argv[]) {
921 Hashmap *all_services;
925 if (argc > 1 && argc != 4) {
926 log_error("This program takes three or no arguments.");
933 log_set_target(LOG_TARGET_SAFE);
934 log_parse_environment();
939 r = lookup_paths_init(&lp, SYSTEMD_SYSTEM, true, NULL, NULL, NULL, NULL);
941 log_error("Failed to find lookup paths.");
945 all_services = hashmap_new(&string_hash_ops);
951 r = enumerate_sysv(lp, all_services);
953 log_error("Failed to generate units for all init scripts.");
957 r = set_dependencies_from_rcnd(lp, all_services);
959 log_error("Failed to read runlevels from rcnd links.");
963 HASHMAP_FOREACH(service, all_services, j) {
964 q = load_sysv(service);
969 HASHMAP_FOREACH(service, all_services, j) {
970 q = fix_order(service, all_services);
974 q = generate_unit_file(service);