- 'systemd-sysctl'
- 'systemd-sysext'
- 'systemd-sysusers'
- - 'systemd-sysv-generator'
- 'systemd-timedate'
- 'systemd-timesync'
- 'systemd-tmpfiles'
- 'systemd-sysctl'
- 'systemd-sysext'
- 'systemd-sysusers'
- - 'systemd-sysv-generator'
- 'systemd-timedate'
- 'systemd-timesync'
- 'systemd-tmpfiles'
- src/basic/siphash24.h
* the following sources are licensed under the **MIT-0** license:
- all examples under man/
- - src/systemctl/systemd-sysv-install.SKELETON
- config files and examples under /network
* the following sources are under **Public Domain** (LicenseRef-murmurhash2-public-domain):
- src/basic/MurmurHash2.c
1. Find the right configure parameters for:
- * `-Dsysvinit-path=`
- * `-Dsysvrcnd-path=`
* `-Drc-local=`
* `-Dloadkeys-path=`
* `-Dsetfont-path=`
as a child process by another tool, such as package managers running it in a
postinstall script.
-`systemd-sysv-generator`:
-
-* `$SYSTEMD_SYSVINIT_PATH` — Controls where `systemd-sysv-generator` looks for
- SysV init scripts.
-
-* `$SYSTEMD_SYSVRCND_PATH` — Controls where `systemd-sysv-generator` looks for
- SysV init script runlevel link farms.
-
systemd tests:
* `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if
+++ /dev/null
-You are looking for the traditional init scripts in {{ SYSTEM_SYSVINIT_PATH }},
-and they are gone?
-
-Here's an explanation on what's going on:
-
-You are running a systemd-based OS where traditional init scripts have
-been replaced by native systemd services files. Service files provide
-very similar functionality to init scripts. To make use of service
-files simply invoke "systemctl", which will output a list of all
-currently running services (and other units). Use "systemctl
-list-unit-files" to get a listing of all known unit files, including
-stopped, disabled and masked ones. Use "systemctl start
-foobar.service" and "systemctl stop foobar.service" to start or stop a
-service, respectively. For further details, please refer to
-systemctl(1).
-
-Note that traditional init scripts continue to function on a systemd
-system. An init script {{ SYSTEM_SYSVINIT_PATH }}/foobar is implicitly mapped
-into a service unit foobar.service during system initialization.
-
-Thank you!
-
-Further reading:
- man:systemctl(1)
- man:systemd(1)
- https://0pointer.de/blog/projects/systemd-for-admins-3.html
- https://systemd.io/INCOMPATIBILITIES
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-custom_target(
- input : 'README.in',
- output : 'README',
- command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
- install : conf.get('HAVE_SYSV_COMPAT') == 1,
- install_dir : sysvinit_path)
['systemd-sysupdated'],
'ENABLE_SYSUPDATED'],
['systemd-sysusers', '8', ['systemd-sysusers.service'], ''],
- ['systemd-sysv-generator', '8', [], 'HAVE_SYSV_COMPAT'],
['systemd-time-wait-sync.service',
'8',
['systemd-time-wait-sync'],
+++ /dev/null
-<?xml version="1.0"?>
-<!--*-nxml-*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
- "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
-<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-sysv-generator" conditional="HAVE_SYSV_COMPAT">
-
- <refentryinfo>
- <title>systemd-sysv-generator</title>
- <productname>systemd</productname>
- </refentryinfo>
-
- <refmeta>
- <refentrytitle>systemd-sysv-generator</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
-
- <refnamediv>
- <refname>systemd-sysv-generator</refname>
- <refpurpose>Unit generator for SysV init scripts</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <para><filename>/usr/lib/systemd/system-generators/systemd-sysv-generator</filename></para>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para><emphasis>Note: this component is deprecated and scheduled for removal. Please replace remaining
- SysV init scripts with native unit files.</emphasis></para>
-
- <para><filename>systemd-sysv-generator</filename> is a generator that creates wrapper .service units for
- <ulink url="https://savannah.nongnu.org/projects/sysvinit">System V init</ulink> scripts in
- <filename>/etc/init.d/*</filename> at boot and when configuration of the system manager is reloaded. This
- allows <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
- support them similarly to native units.</para>
-
- <para><ulink
- url="http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html">LSB
- headers</ulink> in SysV init scripts are interpreted, and the ordering specified in the header is turned
- into dependencies between the generated unit and other units. The LSB facilities
- <literal>$remote_fs</literal>, <literal>$network</literal>, <literal>$named</literal>,
- <literal>$portmap</literal>, <literal>$time</literal> are supported and will be turned into dependencies
- on specific native systemd targets. See
- <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
- more details.</para>
-
- <para>Note that compatibility is quite comprehensive but not 100%, for more details see <ulink
- url="https://systemd.io/INCOMPATIBILITIES">Compatibility with SysV</ulink>.</para>
-
- <para><command>systemd</command> does not support SysV scripts as part of early boot, so all wrapper
- units are ordered after <filename>basic.target</filename>.</para>
-
- <para><filename>systemd-sysv-generator</filename> implements
- <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- <para><simplelist type="inline">
- <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
- <member><citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
- <member><citerefentry><refentrytitle>systemd.target</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
- </simplelist></para>
- </refsect1>
-
-</refentry>
<member><citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-rc-local-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
- <member><citerefentry><refentrytitle>systemd-sysv-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-xdg-autostart-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
which configure resource control settings for the processes of the
service.</para>
- <para>If SysV init compat is enabled, systemd automatically creates service units that wrap SysV init
- scripts (the service name is the same as the name of the script, with a <literal>.service</literal>
- suffix added); see
- <citerefentry><refentrytitle>systemd-sysv-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
- </para>
-
<para>The <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
command allows creating <filename>.service</filename> and <filename>.scope</filename> units dynamically
and transiently from the command line.</para>
have_standalone_binaries = get_option('standalone-binaries')
-sysvinit_path = get_option('sysvinit-path')
-sysvrcnd_path = get_option('sysvrcnd-path')
-conf.set10('HAVE_SYSV_COMPAT', sysvinit_path != '' and sysvrcnd_path != '',
- description : 'SysV init scripts and rcN.d links are supported')
sysvrclocal_path = get_option('rc-local')
conf.set10('HAVE_SYSV_RC_LOCAL', sysvrclocal_path != '')
conf.set10('CREATE_LOG_DIRS', get_option('create-log-dirs'))
conf.set_quoted('SYSTEM_PRESET_DIR', systempresetdir)
conf.set_quoted('SYSTEM_SHUTDOWN_PATH', systemshutdowndir)
conf.set_quoted('SYSTEM_SLEEP_PATH', systemsleepdir)
-conf.set_quoted('SYSTEM_SYSVINIT_PATH', sysvinit_path)
-conf.set_quoted('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
conf.set_quoted('SYSTEM_SYSVRCLOCAL_PATH', sysvrclocal_path)
conf.set_quoted('SYSUSERS_DIR', sysusersdir)
conf.set_quoted('TMPFILES_DIR', tmpfilesdir)
subdir('src/systemctl')
subdir('src/sysupdate')
subdir('src/sysusers')
-subdir('src/sysv-generator')
subdir('src/timedate')
subdir('src/timesync')
subdir('src/tmpfiles')
#####################################################################
-subdir('docs/sysvinit')
subdir('docs/var-log')
subdir('hwdb.d')
subdir('man')
'sysconf directory' : sysconfdir,
'include directory' : includedir,
'lib directory' : libdir,
- 'SysV init scripts' : sysvinit_path,
- 'SysV rc?.d directories' : sysvrcnd_path,
'SysV rc.local script' : sysvrclocal_path,
'PAM modules directory' : pamlibdir,
'PAM configuration directory' : pamconfdir,
option('standalone-binaries', type : 'boolean', value : false,
description : 'also build standalone versions of supported binaries')
-option('sysvinit-path', type : 'string', value : '/etc/init.d',
- description : 'the directory where the SysV init scripts are located')
-option('sysvrcnd-path', type : 'string', value : '/etc/rc.d',
- description : 'the base directory for SysV rcN.d directories')
+option('sysvinit-path', type : 'string', value : '/etc/init.d', deprecated : true,
+ description : 'This option is deprecated and will be removed in a future release')
+option('sysvrcnd-path', type : 'string', value : '/etc/rc.d', deprecated : true,
+ description : 'This option is deprecated and will be removed in a future release')
option('rc-local', type : 'string', value : '/etc/rc.local',
description : 'path to SysV rc.local script')
option('initrd', type : 'boolean',
TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
sed "pkg/$PKG_SUBDIR/PKGBUILD" \
+ --expression "/-Dsysvinit-path=/d" \
+ --expression "/-Dsysvrcnd-path=/d" \
--expression "s/^pkgver=.*/pkgver=$(cat meson.version)/" \
--expression "s/^pkgrel=.*/pkgrel=$(date "+%Y%m%d%H%M%S" --date "@$TS")/" >/tmp/PKGBUILD
mount --bind /tmp/PKGBUILD "pkg/$PKG_SUBDIR/PKGBUILD"
-e 's/\.gz$//; /systemd-cgroups-agent/d; s/import-pubring.gpg/import-pubring.pgp/' \
-e '/(initctl|runlevel|telinit)/ d' \
-e 's/systemd-quotacheck.service.8/systemd-quotacheck@.service.8/' \
+ -e '/systemd-sysv-generator/d' \
"$filelist" >"/tmp/$(basename "$filelist")"
mount --bind "/tmp/$(basename "$filelist")" "$filelist"
done < <(find "pkg/$PKG_SUBDIR${GIT_SUBDIR:+/$GIT_SUBDIR}" -name "files.*")
" -UTMP"
#endif
-#if HAVE_SYSV_COMPAT
- " +SYSVINIT"
-#else
- " -SYSVINIT"
-#endif
-
#if HAVE_LIBARCHIVE
" +LIBARCHIVE"
#else
'systemctl-daemon-reload.c',
'systemctl-logind.c',
'systemctl-start-unit.c',
- 'systemctl-sysv-compat.c',
'systemctl-util.c',
'systemctl.c',
)
#include "systemctl-daemon-reload.h"
#include "systemctl-enable.h"
#include "systemctl-start-unit.h"
-#include "systemctl-sysv-compat.h"
#include "systemctl-util.h"
#include "unit-name.h"
#include "verbs.h"
if (r < 0)
return r;
- r = enable_sysv_units(verb, names);
- if (r < 0)
- return r;
-
/* If the operation was fully executed by the SysV compat, let's finish early */
if (strv_isempty(names)) {
if (arg_no_reload || install_client_side() != INSTALL_CLIENT_SIDE_NO)
#include "strv.h"
#include "systemctl.h"
#include "systemctl-is-enabled.h"
-#include "systemctl-sysv-compat.h"
#include "systemctl-util.h"
static int show_installation_targets_client_side(const char *name) {
int verb_is_enabled(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **names = NULL;
- bool not_found, enabled;
+ bool not_found = true, enabled = false;
int r;
r = mangle_names("to check", strv_skip(argv, 1), &names);
if (r < 0)
return r;
- r = enable_sysv_units(argv[0], names);
- if (r < 0)
- return r;
-
- not_found = r == 0; /* Doesn't have SysV support or SYSV_UNIT_NOT_FOUND */
- enabled = r == SYSV_UNIT_ENABLED;
-
if (install_client_side() != INSTALL_CLIENT_SIDE_NO)
STRV_FOREACH(name, names) {
UnitFileState state;
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "env-util.h"
-#include "install.h"
-#include "log.h"
-#include "path-lookup.h"
-#include "path-util.h"
-#include "pidref.h"
-#include "process-util.h"
-#include "string-util.h"
-#include "strv.h"
-#include "systemctl.h"
-#include "systemctl-sysv-compat.h"
-
-int enable_sysv_units(const char *verb, char **args) {
- int r = 0;
-
-#if HAVE_SYSV_COMPAT
- _cleanup_(lookup_paths_done) LookupPaths paths = {};
- unsigned f = 0;
- SysVUnitEnableState enable_state = SYSV_UNIT_NOT_FOUND;
-
- /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */
-
- if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
- return 0;
-
- if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0)
- return 0;
-
- if (!STR_IN_SET(verb,
- "enable",
- "disable",
- "is-enabled"))
- return 0;
-
- r = lookup_paths_init_or_warn(&paths, arg_runtime_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root);
- if (r < 0)
- return r;
-
- r = 0;
- while (args[f]) {
-
- const char *argv[] = {
- LIBEXECDIR "/systemd-sysv-install",
- NULL, /* --root= */
- NULL, /* verb */
- NULL, /* service */
- NULL,
- };
-
- _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL, *v = NULL, *b = NULL;
- bool found_native = false, found_sysv;
- const char *name;
- unsigned c = 1;
- int j;
-
- name = args[f++];
-
- if (!endswith(name, ".service"))
- continue;
-
- if (path_is_absolute(name))
- continue;
-
- j = unit_file_exists(arg_runtime_scope, &paths, name);
- if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL))
- return log_error_errno(j, "Failed to look up unit file state: %m");
- found_native = j != 0;
-
- /* If we have both a native unit and a SysV script, enable/disable them both (below); for
- * is-enabled, prefer the native unit */
- if (found_native && streq(verb, "is-enabled"))
- continue;
-
- p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name);
- if (!p)
- return log_oom();
-
- p[strlen(p) - STRLEN(".service")] = 0;
- found_sysv = access(p, F_OK) >= 0;
- if (!found_sysv)
- continue;
-
- if (!arg_quiet) {
- if (found_native)
- log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]);
- else
- log_info("%s is not a native service, redirecting to systemd-sysv-install.", name);
- }
-
- if (!isempty(arg_root)) {
- q = strjoin("--root=", arg_root);
- if (!q)
- return log_oom();
-
- argv[c++] = q;
- }
-
- /* Let's copy the verb, since it's still pointing directly into the original argv[] array we
- * got passed, but safe_fork() is likely going to rewrite that for the new child */
- v = strdup(verb);
- if (!v)
- return log_oom();
-
- j = path_extract_filename(p, &b);
- if (j < 0)
- return log_error_errno(j, "Failed to extract file name from '%s': %m", p);
-
- argv[c++] = v;
- argv[c++] = b;
- argv[c] = NULL;
-
- l = strv_join((char**)argv, " ");
- if (!l)
- return log_oom();
-
- if (!arg_quiet)
- log_info("Executing: %s", l);
-
- _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
- j = pidref_safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pidref);
- if (j < 0)
- return j;
- if (j == 0) {
- /* Child */
- execv(argv[0], (char**) argv);
- log_error_errno(errno, "Failed to execute %s: %m", argv[0]);
- _exit(EXIT_FAILURE);
- }
-
- j = pidref_wait_for_terminate_and_check("sysv-install", &pidref, WAIT_LOG_ABNORMAL);
- if (j < 0)
- return j;
- if (streq(verb, "is-enabled")) {
- if (j == EXIT_SUCCESS) {
- if (!arg_quiet)
- puts("enabled");
- enable_state = SYSV_UNIT_ENABLED;
- } else {
- if (!arg_quiet)
- puts("disabled");
- if (enable_state != SYSV_UNIT_ENABLED)
- enable_state = SYSV_UNIT_DISABLED;
- }
-
- } else if (j != EXIT_SUCCESS)
- return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */
-
- if (found_native)
- continue;
-
- /* Remove this entry, so that we don't try enabling it as native unit */
- assert(f > 0);
- f--;
- assert(args[f] == name);
- strv_remove(args + f, name);
- }
-
- if (streq(verb, "is-enabled"))
- return enable_state;
-#endif
- return r;
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-typedef enum SysVUnitEnableState {
- SYSV_UNIT_NOT_FOUND = 0,
- SYSV_UNIT_DISABLED,
- SYSV_UNIT_ENABLED,
-} SysVUnitEnableState;
-
-int enable_sysv_units(const char *verb, char **args);
+++ /dev/null
-#!/bin/sh
-# SPDX-License-Identifier: MIT-0
-#
-# This script is called by "systemctl enable/disable" when the given unit is a
-# SysV init.d script. It needs to call the distribution's mechanism for
-# enabling/disabling those, such as chkconfig, update-rc.d, or similar. This
-# can optionally take a --root argument for enabling a SysV init script
-# in a chroot or similar.
-set -e
-
-usage() {
- echo "Usage: $0 [--root=path] enable|disable|is-enabled <sysv script name>" >&2
- exit 1
-}
-
-unset ROOT
-
-# parse options
-eval set -- "$(getopt -o r: --long root: -- "$@")"
-while true; do
- case "$1" in
- -r|--root)
- ROOT="$2"
- shift 2 ;;
- --) shift ; break ;;
- *) usage ;;
- esac
-done
-
-NAME="$2"
-[ -n "$NAME" ] || usage
-
-case "$1" in
- enable)
- # call the command to enable SysV init script $NAME here
- # (consider optional $ROOT)
- echo "IMPLEMENT ME: enabling SysV init.d script $NAME"
- ;;
- disable)
- # call the command to disable SysV init script $NAME here
- # (consider optional $ROOT)
- echo "IMPLEMENT ME: disabling SysV init.d script $NAME"
- ;;
- is-enabled)
- # exit with 0 if $NAME is enabled, non-zero if it is disabled
- # (consider optional $ROOT)
- echo "IMPLEMENT ME: checking SysV init.d script $NAME"
- ;;
- *)
- usage ;;
-esac
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-executables += [
- generator_template + {
- 'name' : 'systemd-sysv-generator',
- 'conditions' : ['HAVE_SYSV_COMPAT'],
- 'sources' : files('sysv-generator.c'),
- },
-]
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "sd-messages.h"
-
-#include "alloc-util.h"
-#include "dirent-util.h"
-#include "exit-status.h"
-#include "extract-word.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "generator.h"
-#include "glyph-util.h"
-#include "hashmap.h"
-#include "hexdecoct.h"
-#include "initrd-util.h"
-#include "install.h"
-#include "log.h"
-#include "path-lookup.h"
-#include "path-util.h"
-#include "set.h"
-#include "special.h"
-#include "specifier.h"
-#include "stat-util.h"
-#include "string-util.h"
-#include "strv.h"
-#include "unit-name.h"
-
-/* 🚨 Note: this generator is deprecated! Please do not add new features! Instead, please port remaining SysV
- * scripts over to native unit files! Thank you! 🚨 */
-
-static const struct {
- const char *path;
- const char *target;
-} rcnd_table[] = {
- /* Standard SysV runlevels for start-up */
- { "rc1.d", SPECIAL_RESCUE_TARGET },
- { "rc2.d", SPECIAL_MULTI_USER_TARGET },
- { "rc3.d", SPECIAL_MULTI_USER_TARGET },
- { "rc4.d", SPECIAL_MULTI_USER_TARGET },
- { "rc5.d", SPECIAL_GRAPHICAL_TARGET },
-
- /* We ignore the SysV runlevels for shutdown here, as SysV services get default dependencies anyway, and that
- * means they are shut down anyway at system power off if running. */
-};
-
-static const char *arg_dest = NULL;
-
-typedef struct SysvStub {
- char *name;
- char *path;
- char *description;
- int sysv_start_priority;
- char *pid_file;
- char **before;
- char **after;
- char **wants;
- char **wanted_by;
- bool has_lsb;
- bool reload;
- bool loaded;
-} SysvStub;
-
-static SysvStub* sysvstub_free(SysvStub *s) {
- if (!s)
- return NULL;
-
- free(s->name);
- free(s->path);
- free(s->description);
- free(s->pid_file);
- strv_free(s->before);
- strv_free(s->after);
- strv_free(s->wants);
- strv_free(s->wanted_by);
- return mfree(s);
-}
-DEFINE_TRIVIAL_CLEANUP_FUNC(SysvStub*, sysvstub_free);
-
-DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
- sysvstub_hash_ops,
- char, string_hash_func, string_compare_func,
- SysvStub, sysvstub_free);
-
-static int add_alias(const char *service, const char *alias) {
- _cleanup_free_ char *link = NULL;
-
- assert(service);
- assert(alias);
-
- link = path_join(arg_dest, alias);
- if (!link)
- return -ENOMEM;
-
- if (symlink(service, link) < 0) {
- if (errno == EEXIST)
- return 0;
-
- return -errno;
- }
-
- return 1;
-}
-
-static int generate_unit_file(SysvStub *s) {
- _cleanup_free_ char *path_escaped = NULL, *unit = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int r;
-
- assert(s);
-
- if (!s->loaded)
- return 0;
-
- path_escaped = specifier_escape(s->path);
- if (!path_escaped)
- return log_oom();
-
- unit = path_join(arg_dest, s->name);
- if (!unit)
- return log_oom();
-
- /* We might already have a symlink with the same name from a Provides:,
- * or from backup files like /etc/init.d/foo.bak. Real scripts always win,
- * so remove an existing link */
- if (is_symlink(unit) > 0) {
- log_warning("Overwriting existing symlink %s with real service.", unit);
- (void) unlink(unit);
- }
-
- f = fopen(unit, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
-
- fprintf(f,
- "# Automatically generated by systemd-sysv-generator\n\n"
- "[Unit]\n"
- "Documentation=man:systemd-sysv-generator(8)\n"
- "SourcePath=%s\n",
- path_escaped);
-
- if (s->description) {
- _cleanup_free_ char *t = NULL;
-
- t = specifier_escape(s->description);
- if (!t)
- return log_oom();
-
- fprintf(f, "Description=%s\n", t);
- }
-
- STRV_FOREACH(p, s->before)
- fprintf(f, "Before=%s\n", *p);
- STRV_FOREACH(p, s->after)
- fprintf(f, "After=%s\n", *p);
- STRV_FOREACH(p, s->wants)
- fprintf(f, "Wants=%s\n", *p);
-
- fprintf(f,
- "\n[Service]\n"
- "Type=forking\n"
- "Restart=no\n"
- "TimeoutSec=5min\n"
- "IgnoreSIGPIPE=no\n"
- "KillMode=process\n"
- "GuessMainPID=no\n"
- "RemainAfterExit=%s\n",
- yes_no(!s->pid_file));
-
- if (s->pid_file) {
- _cleanup_free_ char *t = NULL;
-
- t = specifier_escape(s->pid_file);
- if (!t)
- return log_oom();
-
- fprintf(f, "PIDFile=%s\n", t);
- }
-
- /* Consider two special LSB exit codes a clean exit */
- if (s->has_lsb)
- fprintf(f,
- "SuccessExitStatus=%i %i\n",
- EXIT_NOTINSTALLED,
- EXIT_NOTCONFIGURED);
-
- fprintf(f,
- "ExecStart=%s start\n"
- "ExecStop=%s stop\n",
- path_escaped, path_escaped);
-
- if (s->reload)
- fprintf(f, "ExecReload=%s reload\n", path_escaped);
-
- r = fflush_and_check(f);
- if (r < 0)
- return log_error_errno(r, "Failed to write unit %s: %m", unit);
-
- STRV_FOREACH(p, s->wanted_by)
- (void) generator_add_symlink(arg_dest, *p, "wants", s->name);
-
- return 1;
-}
-
-static bool usage_contains_reload(const char *line) {
- return (strcasestr(line, "{reload|") ||
- strcasestr(line, "{reload}") ||
- strcasestr(line, "{reload\"") ||
- strcasestr(line, "|reload|") ||
- strcasestr(line, "|reload}") ||
- strcasestr(line, "|reload\""));
-}
-
-static char *sysv_translate_name(const char *name) {
- _cleanup_free_ char *c = NULL;
- char *res;
-
- c = strdup(name);
- if (!c)
- return NULL;
-
- res = endswith(c, ".sh");
- if (res)
- *res = 0;
-
- if (unit_name_mangle(c, 0, &res) < 0)
- return NULL;
-
- return res;
-}
-
-static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, char **ret) {
-
- /* We silently ignore the $ prefix here. According to the LSB
- * spec it simply indicates whether something is a
- * standardized name or a distribution-specific one. Since we
- * just follow what already exists and do not introduce new
- * uses or names we don't care who introduced a new name. */
-
- static const char * const table[] = {
- /* LSB defined facilities */
- "local_fs", NULL,
- "network", SPECIAL_NETWORK_ONLINE_TARGET,
- "named", SPECIAL_NSS_LOOKUP_TARGET,
- "portmap", SPECIAL_RPCBIND_TARGET,
- "remote_fs", SPECIAL_REMOTE_FS_TARGET,
- "syslog", NULL,
- "time", SPECIAL_TIME_SYNC_TARGET,
- };
-
- _cleanup_free_ char *filename = NULL;
- const char *n;
- char *e, *m;
- int r;
-
- assert(name);
- assert(s);
- assert(ret);
-
- r = path_extract_filename(s->path, &filename);
- if (r < 0)
- return log_error_errno(r, "Failed to extract file name from path '%s': %m", s->path);
-
- n = *name == '$' ? name + 1 : name;
-
- for (size_t i = 0; i < ELEMENTSOF(table); i += 2) {
- if (!streq(table[i], n))
- continue;
-
- if (!table[i+1]) {
- *ret = NULL;
- return 0;
- }
-
- m = strdup(table[i+1]);
- if (!m)
- return log_oom();
-
- *ret = m;
- return 1;
- }
-
- /* If we don't know this name, fallback heuristics to figure
- * out whether something is a target or a service alias. */
-
- /* Facilities starting with $ are most likely targets */
- if (*name == '$') {
- r = unit_name_build(n, NULL, ".target", ret);
- if (r < 0)
- return log_error_errno(r, "[%s:%u] Could not build name for facility %s: %m", s->path, line, name);
-
- return 1;
- }
-
- /* Strip ".sh" suffix from file name for comparison */
- e = endswith(filename, ".sh");
- if (e)
- *e = '\0';
-
- /* Names equaling the file name of the services are redundant */
- if (streq_ptr(n, filename)) {
- *ret = NULL;
- return 0;
- }
-
- /* Everything else we assume to be normal service names */
- m = sysv_translate_name(n);
- if (!m)
- return log_oom();
-
- *ret = m;
- return 1;
-}
-
-static int handle_provides(SysvStub *s, unsigned line, const char *full_text, const char *text) {
- int r;
-
- assert(s);
- assert(full_text);
- assert(text);
-
- for (;;) {
- _cleanup_free_ char *word = NULL, *m = NULL;
-
- r = extract_first_word(&text, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
- if (r < 0)
- return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
- if (r == 0)
- break;
-
- r = sysv_translate_facility(s, line, word, &m);
- if (r <= 0) /* continue on error */
- continue;
-
- switch (unit_name_to_type(m)) {
-
- case UNIT_SERVICE:
- log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
- r = add_alias(s->name, m);
- if (r < 0)
- log_warning_errno(r, "[%s:%u] Failed to add LSB Provides name %s, ignoring: %m", s->path, line, m);
- break;
-
- case UNIT_TARGET:
-
- /* NB: SysV targets which are provided by a
- * service are pulled in by the services, as
- * an indication that the generic service is
- * now available. This is strictly one-way.
- * The targets do NOT pull in SysV services! */
-
- r = strv_extend(&s->before, m);
- if (r < 0)
- return log_oom();
-
- r = strv_extend(&s->wants, m);
- if (r < 0)
- return log_oom();
-
- if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
- r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
- if (r < 0)
- return log_oom();
- r = strv_extend(&s->wants, SPECIAL_NETWORK_TARGET);
- if (r < 0)
- return log_oom();
- }
-
- break;
-
- case _UNIT_TYPE_INVALID:
- log_warning("Unit name '%s' is invalid", m);
- break;
-
- default:
- log_warning("Unknown unit type for unit '%s'", m);
- }
- }
-
- return 0;
-}
-
-static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) {
- int r;
-
- assert(s);
- assert(full_text);
- assert(text);
-
- for (;;) {
- _cleanup_free_ char *word = NULL, *m = NULL;
- bool is_before;
-
- r = extract_first_word(&text, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
- if (r < 0)
- return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
- if (r == 0)
- break;
-
- r = sysv_translate_facility(s, line, word, &m);
- if (r <= 0) /* continue on error */
- continue;
-
- is_before = startswith_no_case(full_text, "X-Start-Before:");
-
- if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
- /* the network-online target is special, as it needs to be actively pulled in */
- r = strv_extend(&s->after, m);
- if (r < 0)
- return log_oom();
-
- r = strv_extend(&s->wants, m);
- } else
- r = strv_extend(is_before ? &s->before : &s->after, m);
- if (r < 0)
- return log_oom();
- }
-
- return 0;
-}
-
-static int load_sysv(SysvStub *s) {
- _cleanup_fclose_ FILE *f = NULL;
- unsigned line = 0;
- int r;
- enum {
- NORMAL,
- DESCRIPTION,
- LSB,
- LSB_DESCRIPTION,
- USAGE_CONTINUATION
- } state = NORMAL;
- _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
- char *description;
- bool supports_reload = false;
-
- assert(s);
-
- f = fopen(s->path, "re");
- if (!f) {
- if (errno == ENOENT)
- return 0;
-
- return log_error_errno(errno, "Failed to open %s: %m", s->path);
- }
-
- log_debug("Loading SysV script %s", s->path);
-
- for (;;) {
- _cleanup_free_ char *l = NULL;
-
- r = read_stripped_line(f, LONG_LINE_MAX, &l);
- if (r < 0)
- return log_error_errno(r, "Failed to read configuration file '%s': %m", s->path);
- if (r == 0)
- break;
-
- line++;
-
- if (l[0] != '#') {
- /* Try to figure out whether this init script supports
- * the reload operation. This heuristic looks for
- * "Usage" lines which include the reload option. */
- if (state == USAGE_CONTINUATION ||
- (state == NORMAL && strcasestr(l, "usage"))) {
- if (usage_contains_reload(l)) {
- supports_reload = true;
- state = NORMAL;
- } else if (endswith(l, "\\"))
- state = USAGE_CONTINUATION;
- else
- state = NORMAL;
- }
-
- continue;
- }
-
- if (state == NORMAL && streq(l, "### BEGIN INIT INFO")) {
- state = LSB;
- s->has_lsb = true;
- continue;
- }
-
- if (IN_SET(state, LSB_DESCRIPTION, LSB) && streq(l, "### END INIT INFO")) {
- state = NORMAL;
- continue;
- }
-
- char *t = l + 1;
- t += strspn(t, WHITESPACE);
-
- if (state == NORMAL) {
-
- /* Try to parse Red Hat style description */
-
- if (startswith_no_case(t, "description:")) {
-
- size_t k;
- const char *j;
-
- k = strlen(t);
- if (k > 0 && t[k-1] == '\\') {
- state = DESCRIPTION;
- t[k-1] = 0;
- }
-
- j = empty_to_null(strstrip(t+12));
-
- r = free_and_strdup(&chkconfig_description, j);
- if (r < 0)
- return log_oom();
-
- } else if (startswith_no_case(t, "pidfile:")) {
- const char *fn;
-
- state = NORMAL;
-
- fn = strstrip(t+8);
- if (!path_is_absolute(fn)) {
- log_error("[%s:%u] PID file not absolute. Ignoring.", s->path, line);
- continue;
- }
-
- r = free_and_strdup(&s->pid_file, fn);
- if (r < 0)
- return log_oom();
- }
-
- } else if (state == DESCRIPTION) {
-
- /* Try to parse Red Hat style description
- * continuation */
-
- size_t k;
- const char *j;
-
- k = strlen(t);
- if (k > 0 && t[k-1] == '\\')
- t[k-1] = 0;
- else
- state = NORMAL;
-
- j = strstrip(t);
- if (!isempty(j) && !strextend_with_separator(&chkconfig_description, " ", j))
- return log_oom();
-
- } else if (IN_SET(state, LSB, LSB_DESCRIPTION)) {
-
- if (startswith_no_case(t, "Provides:")) {
- state = LSB;
-
- r = handle_provides(s, line, t, t + 9);
- if (r < 0)
- return r;
-
- } else if (startswith_no_case(t, "Required-Start:") ||
- startswith_no_case(t, "Should-Start:") ||
- startswith_no_case(t, "X-Start-Before:") ||
- startswith_no_case(t, "X-Start-After:")) {
-
- state = LSB;
-
- r = handle_dependencies(s, line, t, strchr(t, ':') + 1);
- if (r < 0)
- return r;
-
- } else if (startswith_no_case(t, "Description:")) {
- const char *j;
-
- state = LSB_DESCRIPTION;
-
- j = empty_to_null(strstrip(t+12));
-
- r = free_and_strdup(&long_description, j);
- if (r < 0)
- return log_oom();
-
- } else if (startswith_no_case(t, "Short-Description:")) {
- const char *j;
-
- state = LSB;
-
- j = empty_to_null(strstrip(t+18));
-
- r = free_and_strdup(&short_description, j);
- if (r < 0)
- return log_oom();
-
- } else if (state == LSB_DESCRIPTION) {
-
- if (startswith(l, "#\t") || startswith(l, "# ")) {
- const char *j;
-
- j = strstrip(t);
- if (!isempty(j) && !strextend_with_separator(&long_description, " ", j))
- return log_oom();
- } else
- state = LSB;
- }
- }
- }
-
- s->reload = supports_reload;
-
- /* We use the long description only if
- * no short description is set. */
-
- if (short_description)
- description = short_description;
- else if (chkconfig_description)
- description = chkconfig_description;
- else if (long_description)
- description = long_description;
- else
- description = NULL;
-
- if (description) {
- char *d;
-
- d = strjoin(s->has_lsb ? "LSB: " : "SYSV: ", description);
- if (!d)
- return log_oom();
-
- s->description = d;
- }
-
- s->loaded = true;
- return 0;
-}
-
-static int fix_order(SysvStub *s, Hashmap *all_services) {
- SysvStub *other;
- int r;
-
- assert(s);
-
- if (!s->loaded)
- return 0;
-
- if (s->sysv_start_priority < 0)
- return 0;
-
- HASHMAP_FOREACH(other, all_services) {
- if (s == other)
- continue;
-
- if (!other->loaded)
- continue;
-
- if (other->sysv_start_priority < 0)
- continue;
-
- /* If both units have modern headers we don't care
- * about the priorities */
- if (s->has_lsb && other->has_lsb)
- continue;
-
- if (other->sysv_start_priority < s->sysv_start_priority) {
- r = strv_extend(&s->after, other->name);
- if (r < 0)
- return log_oom();
-
- } else if (other->sysv_start_priority > s->sysv_start_priority) {
- r = strv_extend(&s->before, other->name);
- if (r < 0)
- return log_oom();
- } else
- continue;
-
- /* FIXME: Maybe we should compare the name here lexicographically? */
- }
-
- return 0;
-}
-
-static int acquire_search_path(const char *def, const char *envvar, char ***ret) {
- _cleanup_strv_free_ char **l = NULL;
- const char *e;
- int r;
-
- assert(def);
- assert(envvar);
-
- e = getenv(envvar);
- if (e) {
- r = path_split_and_make_absolute(e, &l);
- if (r < 0)
- return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar);
- }
-
- if (strv_isempty(l)) {
- strv_free(l);
-
- l = strv_new(def);
- if (!l)
- return log_oom();
- }
-
- if (!path_strv_resolve_uniq(l, NULL))
- return log_oom();
-
- *ret = TAKE_PTR(l);
-
- return 0;
-}
-
-static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
- _cleanup_strv_free_ char **sysvinit_path = NULL;
- int r;
-
- assert(lp);
-
- r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path);
- if (r < 0)
- return r;
-
- STRV_FOREACH(path, sysvinit_path) {
- _cleanup_closedir_ DIR *d = NULL;
-
- d = opendir(*path);
- if (!d) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path);
- continue;
- }
-
- FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
- _cleanup_free_ char *fpath = NULL, *name = NULL;
- _cleanup_(sysvstub_freep) SysvStub *service = NULL;
- struct stat st;
-
- if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
- log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
- continue;
- }
-
- if (!(st.st_mode & S_IXUSR))
- continue;
-
- if (!S_ISREG(st.st_mode))
- continue;
-
- name = sysv_translate_name(de->d_name);
- if (!name)
- return log_oom();
-
- if (hashmap_contains(all_services, name))
- continue;
-
- r = unit_file_exists(RUNTIME_SCOPE_SYSTEM, lp, name);
- if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) {
- log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
- continue;
- } else if (r != 0) {
- log_debug("Native unit for %s already exists, skipping.", name);
- continue;
- }
-
- fpath = path_join(*path, de->d_name);
- if (!fpath)
- return log_oom();
-
- log_struct(LOG_WARNING,
- LOG_MESSAGE("SysV service '%s' lacks a native systemd unit file, "
- "automatically generating a unit file for compatibility.\n"
- "Please update package to include a native systemd unit file.\n"
- "%s This compatibility logic is deprecated, expect removal soon. %s",
- fpath,
- glyph(GLYPH_WARNING_SIGN),
- glyph(GLYPH_WARNING_SIGN)),
- LOG_MESSAGE_ID(SD_MESSAGE_SYSV_GENERATOR_DEPRECATED_STR),
- LOG_ITEM("SYSVSCRIPT=%s", fpath),
- LOG_ITEM("UNIT=%s", name));
-
- service = new(SysvStub, 1);
- if (!service)
- return log_oom();
-
- *service = (SysvStub) {
- .sysv_start_priority = -1,
- .name = TAKE_PTR(name),
- .path = TAKE_PTR(fpath),
- };
-
- r = hashmap_put(all_services, service->name, service);
- if (r < 0)
- return log_oom();
-
- TAKE_PTR(service);
- }
- }
-
- return 0;
-}
-
-static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
- Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
- _cleanup_strv_free_ char **sysvrcnd_path = NULL;
- SysvStub *service;
- int r;
-
- assert(lp);
-
- r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path);
- if (r < 0)
- return r;
-
- STRV_FOREACH(p, sysvrcnd_path)
- for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *path = NULL;
-
- path = path_join(*p, rcnd_table[i].path);
- if (!path) {
- r = log_oom();
- goto finish;
- }
-
- d = opendir(path);
- if (!d) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Opening %s failed, ignoring: %m", path);
-
- continue;
- }
-
- FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
- _cleanup_free_ char *name = NULL, *fpath = NULL;
- int a, b;
-
- if (de->d_name[0] != 'S')
- continue;
-
- if (strlen(de->d_name) < 4)
- continue;
-
- a = undecchar(de->d_name[1]);
- b = undecchar(de->d_name[2]);
-
- if (a < 0 || b < 0)
- continue;
-
- fpath = path_join(*p, de->d_name);
- if (!fpath) {
- r = log_oom();
- goto finish;
- }
-
- name = sysv_translate_name(de->d_name + 3);
- if (!name) {
- r = log_oom();
- goto finish;
- }
-
- service = hashmap_get(all_services, name);
- if (!service) {
- log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name);
- continue;
- }
-
- service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
-
- r = set_ensure_put(&runlevel_services[i], NULL, service);
- if (r < 0) {
- log_oom();
- goto finish;
- }
- }
- }
-
- for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++)
- SET_FOREACH(service, runlevel_services[i]) {
- r = strv_extend(&service->before, rcnd_table[i].target);
- if (r < 0) {
- log_oom();
- goto finish;
- }
- r = strv_extend(&service->wanted_by, rcnd_table[i].target);
- if (r < 0) {
- log_oom();
- goto finish;
- }
- }
-
- r = 0;
-
-finish:
- for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++)
- set_free(runlevel_services[i]);
-
- return r;
-}
-
-static int run(const char *dest, const char *dest_early, const char *dest_late) {
- _cleanup_hashmap_free_ Hashmap *all_services = NULL;
- _cleanup_(lookup_paths_done) LookupPaths lp = {};
- SysvStub *service;
- int r;
-
- if (in_initrd()) {
- log_debug("Skipping generator, running in the initrd.");
- return EXIT_SUCCESS;
- }
-
- assert_se(arg_dest = dest_late);
-
- r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
- if (r < 0)
- return r;
-
- all_services = hashmap_new(&sysvstub_hash_ops);
- if (!all_services)
- return log_oom();
-
- r = enumerate_sysv(&lp, all_services);
- if (r < 0)
- return r;
-
- r = set_dependencies_from_rcnd(&lp, all_services);
- if (r < 0)
- return r;
-
- HASHMAP_FOREACH(service, all_services)
- (void) load_sysv(service);
-
- HASHMAP_FOREACH(service, all_services) {
- (void) fix_order(service, all_services);
- (void) generate_unit_file(service);
- }
-
- return 0;
-}
-
-DEFINE_MAIN_GENERATOR_FUNCTION(run);
############################################################
-if want_tests != 'false' and conf.get('HAVE_SYSV_COMPAT') == 1
- exe = executables_by_name.get('systemd-sysv-generator')
- test('sysv-generator-test',
- files('sysv-generator-test.py'),
- depends : exe,
- suite : 'sysv')
-endif
-
-############################################################
-
if want_tests != 'false' and conf.get('HAVE_BLKID') == 1
exe = executables_by_name.get('bootctl')
test('test-bootctl-json',
+++ /dev/null
-#!/usr/bin/env python3
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# systemd-sysv-generator integration test
-#
-# © 2015 Canonical Ltd.
-# Author: Martin Pitt <martin.pitt@ubuntu.com>
-
-import collections
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-import unittest
-
-from configparser import RawConfigParser
-from glob import glob
-
-sysv_generator = './systemd-sysv-generator'
-
-class MultiDict(collections.OrderedDict):
- def __setitem__(self, key, value):
- if isinstance(value, list) and key in self:
- self[key].extend(value)
- else:
- super(MultiDict, self).__setitem__(key, value)
-
-class SysvGeneratorTest(unittest.TestCase):
- def setUp(self):
- self.workdir = tempfile.mkdtemp(prefix='sysv-gen-test.')
- self.init_d_dir = os.path.join(self.workdir, 'init.d')
- os.mkdir(self.init_d_dir)
- self.rcnd_dir = self.workdir
- self.unit_dir = os.path.join(self.workdir, 'systemd')
- os.mkdir(self.unit_dir)
- self.out_dir = os.path.join(self.workdir, 'output')
- os.mkdir(self.out_dir)
-
- def tearDown(self):
- shutil.rmtree(self.workdir)
-
- #
- # Helper methods
- #
-
- def run_generator(self, expect_error=False):
- '''Run sysv-generator.
-
- Fail if stderr contains any "Fail", unless expect_error is True.
- Return (stderr, filename -> ConfigParser) pair with output to stderr and
- parsed generated units.
- '''
- env = os.environ.copy()
- # We might debug log about errors that aren't actually fatal so let's bump the log level to info to
- # prevent those logs from interfering with the test.
- env['SYSTEMD_LOG_LEVEL'] = 'info'
- env['SYSTEMD_LOG_TARGET'] = 'console'
- env['SYSTEMD_SYSVINIT_PATH'] = self.init_d_dir
- env['SYSTEMD_SYSVRCND_PATH'] = self.rcnd_dir
- env['SYSTEMD_UNIT_PATH'] = self.unit_dir
- gen = subprocess.Popen(
- [sysv_generator, 'ignored', 'ignored', self.out_dir],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- universal_newlines=True, env=env)
- (out, err) = gen.communicate()
- if not expect_error:
- self.assertFalse('Fail' in err, err)
- self.assertEqual(gen.returncode, 0, err)
-
- results = {}
- for service in glob(self.out_dir + '/*.service'):
- if os.path.islink(service):
- continue
- try:
- # for python3 we need here strict=False to parse multiple
- # lines with the same key
- cp = RawConfigParser(dict_type=MultiDict, strict=False)
- except TypeError:
- # RawConfigParser in python2 does not have the strict option
- # but it allows multiple lines with the same key by default
- cp = RawConfigParser(dict_type=MultiDict)
- cp.optionxform = lambda o: o # don't lower-case option names
- with open(service) as f:
- cp.read_file(f)
- results[os.path.basename(service)] = cp
-
- return (err, results)
-
- def add_sysv(self, fname, keys, enable=False, prio=1):
- '''Create a SysV init script with the given keys in the LSB header
-
- There are sensible default values for all fields.
- If enable is True, links will be created in the rcN.d dirs. In that
- case, the priority can be given with "prio" (default to 1).
-
- Return path of generated script.
- '''
- name_without_sh = fname.endswith('.sh') and fname[:-3] or fname
- keys.setdefault('Provides', name_without_sh)
- keys.setdefault('Required-Start', '$local_fs')
- keys.setdefault('Required-Stop', keys['Required-Start'])
- keys.setdefault('Default-Start', '2 3 4 5')
- keys.setdefault('Default-Stop', '0 1 6')
- keys.setdefault('Short-Description', 'test {} service'.format(name_without_sh))
- keys.setdefault('Description', 'long description for test {} service'.format(name_without_sh))
- script = os.path.join(self.init_d_dir, fname)
- with open(script, 'w') as f:
- f.write('#!/bin/init-d-interpreter\n### BEGIN INIT INFO\n')
- for k, v in keys.items():
- if v is not None:
- f.write('#{:>20} {}\n'.format(k + ':', v))
- f.write('### END INIT INFO\ncode --goes here\n')
- os.chmod(script, 0o755)
-
- if enable:
- def make_link(prefix, runlevel):
- d = os.path.join(self.rcnd_dir, 'rc{}.d'.format(runlevel))
- if not os.path.isdir(d):
- os.mkdir(d)
- os.symlink('../init.d/' + fname, os.path.join(d, prefix + fname))
-
- for rl in keys['Default-Start'].split():
- make_link('S%02i' % prio, rl)
- for rl in keys['Default-Stop'].split():
- make_link('K%02i' % (99 - prio), rl)
-
- return script
-
- def assert_enabled(self, unit, targets):
- '''assert that a unit is enabled in precisely the given targets'''
-
- all_targets = ['multi-user', 'graphical']
-
- # should be enabled
- for target in all_targets:
- link = os.path.join(self.out_dir, '{}.target.wants'.format(target), unit)
- if target in targets:
- unit_file = os.readlink(link)
- # os.path.exists() will fail on a dangling symlink
- self.assertTrue(os.path.exists(link))
- self.assertEqual(os.path.basename(unit_file), unit)
- else:
- self.assertFalse(os.path.exists(link),
- '{} unexpectedly exists'.format(link))
-
- #
- # test cases
- #
-
- def test_nothing(self):
- '''no input files'''
-
- results = self.run_generator()[1]
- self.assertEqual(results, {})
- self.assertEqual(os.listdir(self.out_dir), [])
-
- def test_simple_disabled(self):
- '''simple service without dependencies, disabled'''
-
- self.add_sysv('foo', {}, enable=False)
- err, results = self.run_generator()
- self.assertEqual(len(results), 1)
-
- # no enablement links or other stuff
- self.assertEqual(os.listdir(self.out_dir), ['foo.service'])
-
- s = results['foo.service']
- self.assertEqual(s.sections(), ['Unit', 'Service'])
- self.assertEqual(s.get('Unit', 'Description'), 'LSB: test foo service')
- # $local_fs does not need translation, don't expect any dependency
- # fields here
- self.assertEqual(set(s.options('Unit')),
- set(['Documentation', 'SourcePath', 'Description']))
-
- self.assertEqual(s.get('Service', 'Type'), 'forking')
- init_script = os.path.join(self.init_d_dir, 'foo')
- self.assertEqual(s.get('Service', 'ExecStart'),
- '{} start'.format(init_script))
- self.assertEqual(s.get('Service', 'ExecStop'),
- '{} stop'.format(init_script))
-
- self.assertNotIn('Overwriting', err)
-
- def test_simple_enabled_all(self):
- '''simple service without dependencies, enabled in all runlevels'''
-
- self.add_sysv('foo', {}, enable=True)
- err, results = self.run_generator()
- self.assertEqual(list(results), ['foo.service'])
- self.assert_enabled('foo.service', ['multi-user', 'graphical'])
- self.assertNotIn('Overwriting', err)
-
- def test_simple_escaped(self):
- '''simple service without dependencies, that requires escaping the name'''
-
- self.add_sysv('foo+', {})
- self.add_sysv('foo-admin', {})
- err, results = self.run_generator()
- self.assertEqual(set(results), {'foo-admin.service', 'foo\\x2b.service'})
- self.assertNotIn('Overwriting', err)
-
- def test_simple_enabled_some(self):
- '''simple service without dependencies, enabled in some runlevels'''
-
- self.add_sysv('foo', {'Default-Start': '2 4'}, enable=True)
- err, results = self.run_generator()
- self.assertEqual(list(results), ['foo.service'])
- self.assert_enabled('foo.service', ['multi-user'])
-
- def test_lsb_macro_dep_single(self):
- '''single LSB macro dependency: $network'''
-
- self.add_sysv('foo', {'Required-Start': '$network'})
- s = self.run_generator()[1]['foo.service']
- self.assertEqual(set(s.options('Unit')),
- set(['Documentation', 'SourcePath', 'Description', 'After', 'Wants']))
- self.assertEqual(s.get('Unit', 'After'), 'network-online.target')
- self.assertEqual(s.get('Unit', 'Wants'), 'network-online.target')
-
- def test_lsb_macro_dep_multi(self):
- '''multiple LSB macro dependencies'''
-
- self.add_sysv('foo', {'Required-Start': '$named $portmap'})
- s = self.run_generator()[1]['foo.service']
- self.assertEqual(set(s.options('Unit')),
- set(['Documentation', 'SourcePath', 'Description', 'After']))
- self.assertEqual(s.get('Unit', 'After').split(), ['nss-lookup.target', 'rpcbind.target'])
-
- def test_lsb_deps(self):
- '''LSB header dependencies to other services'''
-
- # also give symlink priorities here; they should be ignored
- self.add_sysv('foo', {'Required-Start': 'must1 must2',
- 'Should-Start': 'may1 ne_may2'},
- enable=True, prio=40)
- self.add_sysv('must1', {}, enable=True, prio=10)
- self.add_sysv('must2', {}, enable=True, prio=15)
- self.add_sysv('may1', {}, enable=True, prio=20)
- # do not create ne_may2
- err, results = self.run_generator()
- self.assertEqual(sorted(results),
- ['foo.service', 'may1.service', 'must1.service', 'must2.service'])
-
- # foo should depend on all of them
- self.assertEqual(sorted(results['foo.service'].get('Unit', 'After').split()),
- ['may1.service', 'must1.service', 'must2.service', 'ne_may2.service'])
-
- # other services should not depend on each other
- self.assertFalse(results['must1.service'].has_option('Unit', 'After'))
- self.assertFalse(results['must2.service'].has_option('Unit', 'After'))
- self.assertFalse(results['may1.service'].has_option('Unit', 'After'))
-
- def test_symlink_prio_deps(self):
- '''script without LSB headers use rcN.d priority'''
-
- # create two init.d scripts without LSB header and enable them with
- # startup priorities
- for prio, name in [(10, 'provider'), (15, 'consumer')]:
- with open(os.path.join(self.init_d_dir, name), 'w') as f:
- f.write('#!/bin/init-d-interpreter\ncode --goes here\n')
- os.fchmod(f.fileno(), 0o755)
-
- d = os.path.join(self.rcnd_dir, 'rc2.d')
- if not os.path.isdir(d):
- os.mkdir(d)
- os.symlink('../init.d/' + name, os.path.join(d, 'S{:>2}{}'.format(prio, name)))
-
- err, results = self.run_generator()
- self.assertEqual(sorted(results), ['consumer.service', 'provider.service'])
- self.assertFalse(results['provider.service'].has_option('Unit', 'After'))
- self.assertEqual(results['consumer.service'].get('Unit', 'After'),
- 'provider.service')
-
- def test_multiple_provides(self):
- '''multiple Provides: names'''
-
- self.add_sysv('foo', {'Provides': 'foo bar baz'})
- err, results = self.run_generator()
- self.assertEqual(list(results), ['foo.service'])
- self.assertEqual(set(results['foo.service'].options('Unit')),
- set(['Documentation', 'SourcePath', 'Description']))
- # should create symlinks for the alternative names
- for f in ['bar.service', 'baz.service']:
- self.assertEqual(os.readlink(os.path.join(self.out_dir, f)),
- 'foo.service')
- self.assertNotIn('Overwriting', err)
-
- def test_provides_escaped(self):
- '''a script that Provides: a name that requires escaping'''
-
- self.add_sysv('foo', {'Provides': 'foo foo+'})
- err, results = self.run_generator()
- self.assertEqual(list(results), ['foo.service'])
- self.assertEqual(os.readlink(os.path.join(self.out_dir, 'foo\\x2b.service')),
- 'foo.service')
- self.assertNotIn('Overwriting', err)
-
- def test_same_provides_in_multiple_scripts(self):
- '''multiple init.d scripts provide the same name'''
-
- self.add_sysv('foo', {'Provides': 'foo common'}, enable=True, prio=1)
- self.add_sysv('bar', {'Provides': 'bar common'}, enable=True, prio=2)
- err, results = self.run_generator()
- self.assertEqual(sorted(results), ['bar.service', 'foo.service'])
- # should create symlink for the alternative name for either unit
- self.assertIn(os.readlink(os.path.join(self.out_dir, 'common.service')),
- ['foo.service', 'bar.service'])
-
- def test_provide_other_script(self):
- '''init.d scripts provides the name of another init.d script'''
-
- self.add_sysv('foo', {'Provides': 'foo bar'}, enable=True)
- self.add_sysv('bar', {'Provides': 'bar'}, enable=True)
- err, results = self.run_generator()
- self.assertEqual(sorted(results), ['bar.service', 'foo.service'])
- # we do expect an overwrite here, bar.service should overwrite the
- # alias link from foo.service
- self.assertIn('Overwriting', err)
-
- def test_nonexecutable_script(self):
- '''ignores non-executable init.d script'''
-
- os.chmod(self.add_sysv('foo', {}), 0o644)
- err, results = self.run_generator()
- self.assertEqual(results, {})
-
- def test_sh_suffix(self):
- '''init.d script with .sh suffix'''
-
- self.add_sysv('foo.sh', {}, enable=True)
- err, results = self.run_generator()
- s = results['foo.service']
-
- self.assertEqual(s.sections(), ['Unit', 'Service'])
- # should not have a .sh
- self.assertEqual(s.get('Unit', 'Description'), 'LSB: test foo service')
-
- # calls correct script with .sh
- init_script = os.path.join(self.init_d_dir, 'foo.sh')
- self.assertEqual(s.get('Service', 'ExecStart'),
- '{} start'.format(init_script))
- self.assertEqual(s.get('Service', 'ExecStop'),
- '{} stop'.format(init_script))
-
- self.assert_enabled('foo.service', ['multi-user', 'graphical'])
-
- def test_sh_suffix_with_provides(self):
- '''init.d script with .sh suffix and Provides:'''
-
- self.add_sysv('foo.sh', {'Provides': 'foo bar'})
- err, results = self.run_generator()
- # ensure we don't try to create a symlink to itself
- self.assertNotIn('itself', err)
- self.assertEqual(list(results), ['foo.service'])
- self.assertEqual(results['foo.service'].get('Unit', 'Description'),
- 'LSB: test foo service')
-
- # should create symlink for the alternative name
- self.assertEqual(os.readlink(os.path.join(self.out_dir, 'bar.service')),
- 'foo.service')
-
- def test_hidden_files(self):
- '''init.d script with hidden file suffix'''
-
- script = self.add_sysv('foo', {}, enable=True)
- # backup files (not enabled in rcN.d/)
- shutil.copy(script, script + '.dpkg-new')
- shutil.copy(script, script + '.dpkg-dist')
- shutil.copy(script, script + '.swp')
- shutil.copy(script, script + '.rpmsave')
-
- err, results = self.run_generator()
- self.assertEqual(list(results), ['foo.service'])
-
- self.assert_enabled('foo.service', ['multi-user', 'graphical'])
-
- def test_backup_file(self):
- '''init.d script with backup file'''
-
- script = self.add_sysv('foo', {}, enable=True)
- # backup files (not enabled in rcN.d/)
- shutil.copy(script, script + '.bak')
- shutil.copy(script, script + '.old')
- shutil.copy(script, script + '.tmp')
- shutil.copy(script, script + '.new')
-
- err, results = self.run_generator()
- print(err)
- self.assertEqual(sorted(results), ['foo.service', 'foo.tmp.service'])
-
- # ensure we don't try to create a symlink to itself
- self.assertNotIn('itself', err)
-
- self.assert_enabled('foo.service', ['multi-user', 'graphical'])
- self.assert_enabled('foo.bak.service', [])
- self.assert_enabled('foo.old.service', [])
-
- def test_existing_native_unit(self):
- '''existing native unit'''
-
- with open(os.path.join(self.unit_dir, 'foo.service'), 'w') as f:
- f.write('[Unit]\n')
-
- self.add_sysv('foo.sh', {'Provides': 'foo bar'}, enable=True)
- err, results = self.run_generator()
- self.assertEqual(list(results), [])
- # no enablement or alias links, as native unit is disabled
- self.assertEqual(os.listdir(self.out_dir), [])
-
-
-if __name__ == '__main__':
- unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))
(! systemctl show-environment | grep "^IMPORT_THIS=")
(! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
-# test for sysv-generator (issue #24990)
-if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
- # This is configurable via -Dsysvinit-path=, but we can't get the value
- # at runtime, so let's just support the two most common paths for now.
- [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
-
- # OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built
- # but may not create the directory if there's no services that use it.
- mkdir -p "$SYSVINIT_PATH"
-
- # invalid dependency
- cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
-#!/usr/bin/env bash
-
-### BEGIN INIT INFO
-# Provides:test1 test2
-# Required-Start:test1 $remote_fs $network
-# Required-Stop:test1 $remote_fs $network
-# Description:Test
-# Short-Description: Test
-### END INIT INFO
-
-case "$1" in
- start)
- echo "Starting issue-24990.service"
- sleep 1000 &
- ;;
- stop)
- echo "Stopping issue-24990.service"
- sleep 10 &
- ;;
- *)
- echo "Usage: service test {start|stop|restart|status}"
- ;;
-esac
-EOF
-
- chmod +x "$SYSVINIT_PATH/issue-24990"
- systemctl daemon-reload
- [[ -L /run/systemd/generator.late/test1.service ]]
- [[ -L /run/systemd/generator.late/test2.service ]]
- assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
- assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
- output=$(systemctl cat issue-24990)
- assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
- assert_in "Description=LSB: Test" "$output"
- assert_in "After=test1.service" "$output"
- assert_in "After=remote-fs.target" "$output"
- assert_in "After=network-online.target" "$output"
- assert_in "Wants=network-online.target" "$output"
- assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
- assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
- systemctl status issue-24990 || :
- systemctl show issue-24990
- assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
- assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
-
- if ! systemctl is-active network-online.target; then
- systemctl start network-online.target
- fi
-
- systemctl restart issue-24990
- systemctl stop issue-24990
-
- # valid dependency
- cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
-#!/usr/bin/env bash
-
-### BEGIN INIT INFO
-# Provides:test1 test2
-# Required-Start:$remote_fs
-# Required-Stop:$remote_fs
-# Description:Test
-# Short-Description: Test
-### END INIT INFO
-
-case "$1" in
- start)
- echo "Starting issue-24990.service"
- sleep 1000 &
- ;;
- stop)
- echo "Stopping issue-24990.service"
- sleep 10 &
- ;;
- *)
- echo "Usage: service test {start|stop|restart|status}"
- ;;
-esac
-EOF
-
- chmod +x "$SYSVINIT_PATH/issue-24990"
- systemctl daemon-reload
- [[ -L /run/systemd/generator.late/test1.service ]]
- [[ -L /run/systemd/generator.late/test2.service ]]
- assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
- assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
- output=$(systemctl cat issue-24990)
- assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
- assert_in "Description=LSB: Test" "$output"
- assert_in "After=remote-fs.target" "$output"
- assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
- assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
- systemctl status issue-24990 || :
- systemctl show issue-24990
- assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
- assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
-
- systemctl restart issue-24990
- systemctl stop issue-24990
-fi
-
# %J in WantedBy= causes ABRT (#26467)
cat >/run/systemd/system/test-WantedBy.service <<EOF
[Service]
{% if CREATE_LOG_DIRS %}
L$ /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
{% endif %}
-
-{% if HAVE_SYSV_COMPAT %}
-# /run/lock/subsys is used for serializing SysV service execution, and
-# hence without use on SysV-less systems.
-d /run/lock/subsys 0755 root root -
-{% endif %}