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"
39 typedef enum RunlevelType {
47 const RunlevelType type;
49 /* Standard SysV runlevels for start-up */
50 { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
51 { "rc2.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
52 { "rc3.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
53 { "rc4.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
54 { "rc5.d", SPECIAL_GRAPHICAL_TARGET, RUNLEVEL_UP },
56 /* Standard SysV runlevels for shutdown */
57 { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
58 { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
60 /* Note that the order here matters, as we read the
61 directories in this order, and we want to make sure that
62 sysv_start_priority is known when we first load the
63 unit. And that value we only know from S links. Hence
64 UP must be read before DOWN */
67 typedef struct SysvStub {
71 int sysv_start_priority;
82 const char *arg_dest = "/tmp";
84 static int add_symlink(const char *service, const char *where) {
85 _cleanup_free_ char *from = NULL, *to = NULL;
91 from = strjoin(arg_dest, "/", service, NULL);
95 to = strjoin(arg_dest, "/", where, ".wants/", service, NULL);
99 mkdir_parents_label(to, 0755);
101 r = symlink(from, to);
111 static int add_alias(const char *service, const char *alias) {
112 _cleanup_free_ char *link = NULL;
118 link = strjoin(arg_dest, "/", alias, NULL);
122 r = symlink(service, link);
132 static int generate_unit_file(SysvStub *s) {
134 _cleanup_fclose_ FILE *f = NULL;
135 _cleanup_free_ char *unit = NULL;
136 _cleanup_free_ char *before = NULL;
137 _cleanup_free_ char *after = NULL;
138 _cleanup_free_ char *wants = NULL;
139 _cleanup_free_ char *conflicts = NULL;
142 before = strv_join(s->before, " ");
146 after = strv_join(s->after, " ");
150 wants = strv_join(s->wants, " ");
154 conflicts = strv_join(s->conflicts, " ");
158 unit = strjoin(arg_dest, "/", s->name, NULL);
162 /* We might already have a symlink with the same name from a Provides:,
163 * or from backup files like /etc/init.d/foo.bak. Real scripts always win,
164 * so remove an existing link */
165 if (is_symlink(unit) > 0) {
166 log_warning("Overwriting existing symlink %s with real service", unit);
170 f = fopen(unit, "wxe");
172 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
175 "# Automatically generated by systemd-sysv-generator\n\n"
177 "Documentation=man:systemd-sysv-generator(8)\n"
180 s->path, s->description);
182 if (!isempty(before))
183 fprintf(f, "Before=%s\n", before);
185 fprintf(f, "After=%s\n", after);
187 fprintf(f, "Wants=%s\n", wants);
188 if (!isempty(conflicts))
189 fprintf(f, "Conflicts=%s\n", conflicts);
199 "RemainAfterExit=%s\n",
200 yes_no(!s->pid_file));
203 fprintf(f, "PIDFile=%s\n", s->pid_file);
206 "ExecStart=%s start\n"
207 "ExecStop=%s stop\n",
211 fprintf(f, "ExecReload=%s reload\n", s->path);
213 STRV_FOREACH(p, s->wanted_by) {
214 r = add_symlink(s->name, *p);
216 log_unit_error_errno(s->name, r, "Failed to create 'Wants' symlink to %s: %m", *p);
222 static bool usage_contains_reload(const char *line) {
223 return (strcasestr(line, "{reload|") ||
224 strcasestr(line, "{reload}") ||
225 strcasestr(line, "{reload\"") ||
226 strcasestr(line, "|reload|") ||
227 strcasestr(line, "|reload}") ||
228 strcasestr(line, "|reload\""));
231 static char *sysv_translate_name(const char *name) {
234 r = new(char, strlen(name) + strlen(".service") + 1);
238 if (endswith(name, ".sh"))
239 /* Drop .sh suffix */
240 strcpy(stpcpy(r, name) - 3, ".service");
242 /* Normal init script name */
243 strcpy(stpcpy(r, name), ".service");
248 static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
250 /* We silently ignore the $ prefix here. According to the LSB
251 * spec it simply indicates whether something is a
252 * standardized name or a distribution-specific one. Since we
253 * just follow what already exists and do not introduce new
254 * uses or names we don't care who introduced a new name. */
256 static const char * const table[] = {
257 /* LSB defined facilities */
259 "network", SPECIAL_NETWORK_ONLINE_TARGET,
260 "named", SPECIAL_NSS_LOOKUP_TARGET,
261 "portmap", SPECIAL_RPCBIND_TARGET,
262 "remote_fs", SPECIAL_REMOTE_FS_TARGET,
264 "time", SPECIAL_TIME_SYNC_TARGET,
267 char *filename_no_sh, *e, *r;
274 n = *name == '$' ? name + 1 : name;
276 for (i = 0; i < ELEMENTSOF(table); i += 2) {
278 if (!streq(table[i], n))
284 r = strdup(table[i+1]);
291 /* strip ".sh" suffix from file name for comparison */
292 filename_no_sh = strdupa(filename);
293 e = endswith(filename_no_sh, ".sh");
296 filename = filename_no_sh;
299 /* If we don't know this name, fallback heuristics to figure
300 * out whether something is a target or a service alias. */
303 if (!unit_prefix_is_valid(n))
306 /* Facilities starting with $ are most likely targets */
307 r = unit_name_build(n, NULL, ".target");
308 } else if (streq_ptr(n, filename))
309 /* Names equaling the file name of the services are redundant */
312 /* Everything else we assume to be normal service names */
313 r = sysv_translate_name(n);
323 static int load_sysv(SysvStub *s) {
324 _cleanup_fclose_ FILE *f;
334 _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
336 bool supports_reload = false;
340 f = fopen(s->path, "re");
342 return errno == ENOENT ? 0 : -errno;
344 log_debug("Loading SysV script %s", s->path);
347 char l[LINE_MAX], *t;
349 if (!fgets(l, sizeof(l), f)) {
353 log_unit_error(s->name,
354 "Failed to read configuration file '%s': %m",
363 /* Try to figure out whether this init script supports
364 * the reload operation. This heuristic looks for
365 * "Usage" lines which include the reload option. */
366 if ( state == USAGE_CONTINUATION ||
367 (state == NORMAL && strcasestr(t, "usage"))) {
368 if (usage_contains_reload(t)) {
369 supports_reload = true;
371 } else if (t[strlen(t)-1] == '\\')
372 state = USAGE_CONTINUATION;
380 if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
386 if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) {
392 t += strspn(t, WHITESPACE);
394 if (state == NORMAL) {
396 /* Try to parse Red Hat style description */
398 if (startswith_no_case(t, "description:")) {
400 size_t k = strlen(t);
404 if (t[k-1] == '\\') {
417 free(chkconfig_description);
418 chkconfig_description = d;
420 } else if (startswith_no_case(t, "pidfile:")) {
427 if (!path_is_absolute(fn)) {
428 log_unit_error(s->name,
429 "[%s:%u] PID file not absolute. Ignoring.",
442 } else if (state == DESCRIPTION) {
444 /* Try to parse Red Hat style description
447 size_t k = strlen(t);
459 if (chkconfig_description)
460 d = strjoin(chkconfig_description, " ", j, NULL);
467 free(chkconfig_description);
468 chkconfig_description = d;
471 } else if (state == LSB || state == LSB_DESCRIPTION) {
473 if (startswith_no_case(t, "Provides:")) {
474 const char *word, *state_;
479 FOREACH_WORD_QUOTED(word, z, t+9, state_) {
480 _cleanup_free_ char *n = NULL, *m = NULL;
482 n = strndup(word, z);
486 r = sysv_translate_facility(n, basename(s->path), &m);
492 if (unit_name_to_type(m) == UNIT_SERVICE) {
493 log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
494 r = add_alias(s->name, m);
501 * indication that the
503 * now available. This
506 * targets do NOT pull
509 r = strv_extend(&s->before, m);
512 r = strv_extend(&s->wants, m);
515 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
516 r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
523 log_unit_error(s->name,
524 "[%s:%u] Failed to add LSB Provides name %s, ignoring: %s",
525 s->path, line, m, strerror(-r));
527 if (!isempty(state_))
528 log_unit_error(s->name,
529 "[%s:%u] Trailing garbage in Provides, ignoring.",
532 } else if (startswith_no_case(t, "Required-Start:") ||
533 startswith_no_case(t, "Should-Start:") ||
534 startswith_no_case(t, "X-Start-Before:") ||
535 startswith_no_case(t, "X-Start-After:")) {
536 const char *word, *state_;
541 FOREACH_WORD_QUOTED(word, z, strchr(t, ':')+1, state_) {
542 _cleanup_free_ char *n = NULL, *m = NULL;
545 n = strndup(word, z);
549 r = sysv_translate_facility(n, basename(s->path), &m);
551 log_unit_error(s->name,
552 "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
553 s->path, line, n, strerror(-r));
560 is_before = startswith_no_case(t, "X-Start-Before:");
562 if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
563 /* the network-online target is special, as it needs to be actively pulled in */
564 r = strv_extend(&s->after, m);
567 r = strv_extend(&s->wants, m);
573 r = strv_extend(&s->before, m);
578 r = strv_extend(&s->after, m);
585 log_unit_error(s->name,
586 "[%s:%u] Failed to add dependency on %s, ignoring: %s",
587 s->path, line, m, strerror(-r));
589 if (!isempty(state_))
590 log_unit_error(s->name,
591 "[%s:%u] Trailing garbage in %*s, ignoring.",
593 (int)(strchr(t, ':') - t), t);
595 } else if (startswith_no_case(t, "Description:")) {
598 state = LSB_DESCRIPTION;
608 free(long_description);
609 long_description = d;
611 } else if (startswith_no_case(t, "Short-Description:")) {
624 free(short_description);
625 short_description = d;
627 } else if (state == LSB_DESCRIPTION) {
629 if (startswith(l, "#\t") || startswith(l, "# ")) {
636 if (long_description)
637 d = strjoin(long_description, " ", t, NULL);
644 free(long_description);
645 long_description = d;
654 s->reload = supports_reload;
656 /* We use the long description only if
657 * no short description is set. */
659 if (short_description)
660 description = short_description;
661 else if (chkconfig_description)
662 description = chkconfig_description;
663 else if (long_description)
664 description = long_description;
671 d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
681 static int fix_order(SysvStub *s, Hashmap *all_services) {
688 if (s->sysv_start_priority < 0)
691 HASHMAP_FOREACH(other, all_services, j) {
695 if (other->sysv_start_priority < 0)
698 /* If both units have modern headers we don't care
699 * about the priorities */
700 if (s->has_lsb && other->has_lsb)
703 if (other->sysv_start_priority < s->sysv_start_priority) {
704 r = strv_extend(&s->after, other->name);
708 else if (other->sysv_start_priority > s->sysv_start_priority) {
709 r = strv_extend(&s->before, other->name);
716 /* FIXME: Maybe we should compare the name here lexicographically? */
722 static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
725 STRV_FOREACH(path, lp.sysvinit_path) {
726 _cleanup_closedir_ DIR *d = NULL;
732 log_warning_errno(errno, "opendir(%s) failed: %m", *path);
736 while ((de = readdir(d))) {
737 _cleanup_free_ char *fpath = NULL, *name = NULL;
738 _cleanup_free_ SysvStub *service = NULL;
742 if (hidden_file(de->d_name))
745 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
746 log_warning_errno(errno, "stat() failed on %s/%s: %m", *path, de->d_name);
750 if (!(st.st_mode & S_IXUSR))
753 if (!S_ISREG(st.st_mode))
756 name = sysv_translate_name(de->d_name);
760 if (hashmap_contains(all_services, name))
763 fpath = strjoin(*path, "/", de->d_name, NULL);
767 if (unit_file_get_state(UNIT_FILE_SYSTEM, NULL, name) >= 0) {
768 log_debug("Native unit for %s already exists, skipping", name);
772 service = new0(SysvStub, 1);
776 service->sysv_start_priority = -1;
777 service->name = name;
778 service->path = fpath;
780 r = hashmap_put(all_services, service->name, service);
792 static int set_dependencies_from_rcnd(LookupPaths lp, Hashmap *all_services) {
795 _cleanup_closedir_ DIR *d = NULL;
796 _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
799 Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
800 _cleanup_set_free_ Set *shutdown_services = NULL;
803 STRV_FOREACH(p, lp.sysvrcnd_path)
804 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
808 path = strjoin(*p, "/", rcnd_table[i].path, NULL);
818 log_warning_errno(errno, "opendir(%s) failed: %m", path);
823 while ((de = readdir(d))) {
826 if (hidden_file(de->d_name))
829 if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
832 if (strlen(de->d_name) < 4)
835 a = undecchar(de->d_name[1]);
836 b = undecchar(de->d_name[2]);
842 fpath = strjoin(*p, "/", de->d_name, NULL);
848 name = sysv_translate_name(de->d_name + 3);
854 service = hashmap_get(all_services, name);
856 log_debug("Ignoring %s symlink in %s, not generating %s.",
857 de->d_name, rcnd_table[i].path, name);
861 if (de->d_name[0] == 'S') {
863 if (rcnd_table[i].type == RUNLEVEL_UP) {
864 service->sysv_start_priority =
865 MAX(a*10 + b, service->sysv_start_priority);
868 r = set_ensure_allocated(&runlevel_services[i], NULL);
872 r = set_put(runlevel_services[i], service);
876 } else if (de->d_name[0] == 'K' &&
877 (rcnd_table[i].type == RUNLEVEL_DOWN)) {
879 r = set_ensure_allocated(&shutdown_services, NULL);
883 r = set_put(shutdown_services, service);
891 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
892 SET_FOREACH(service, runlevel_services[i], j) {
893 r = strv_extend(&service->before, rcnd_table[i].target);
896 r = strv_extend(&service->wanted_by, rcnd_table[i].target);
901 SET_FOREACH(service, shutdown_services, j) {
902 r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
905 r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
914 for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
915 set_free(runlevel_services[i]);
920 int main(int argc, char *argv[]) {
923 Hashmap *all_services;
927 if (argc > 1 && argc != 4) {
928 log_error("This program takes three or no arguments.");
935 log_set_target(LOG_TARGET_SAFE);
936 log_parse_environment();
941 r = lookup_paths_init(&lp, SYSTEMD_SYSTEM, true, NULL, NULL, NULL, NULL);
943 log_error("Failed to find lookup paths.");
947 all_services = hashmap_new(&string_hash_ops);
953 r = enumerate_sysv(lp, all_services);
955 log_error("Failed to generate units for all init scripts.");
959 r = set_dependencies_from_rcnd(lp, all_services);
961 log_error("Failed to read runlevels from rcnd links.");
965 HASHMAP_FOREACH(service, all_services, j) {
966 q = load_sysv(service);
971 HASHMAP_FOREACH(service, all_services, j) {
972 q = fix_order(service, all_services);
976 q = generate_unit_file(service);