mirror of
https://github.com/systemd/systemd.git
synced 2025-01-21 22:04:01 +03:00
Merge pull request #5131 from keszybz/environment-generators
Environment generators
This commit is contained in:
commit
a4dde27d73
2
.gitignore
vendored
2
.gitignore
vendored
@ -19,6 +19,7 @@
|
||||
/*.tar.bz2
|
||||
/*.tar.gz
|
||||
/*.tar.xz
|
||||
/30-systemd-environment-d-generator
|
||||
/GPATH
|
||||
/GRTAGS
|
||||
/GSYMS
|
||||
@ -194,6 +195,7 @@
|
||||
/test-env-util
|
||||
/test-escape
|
||||
/test-event
|
||||
/test-exec-util
|
||||
/test-execute
|
||||
/test-extract-word
|
||||
/test-fd-util
|
||||
|
@ -11,6 +11,7 @@ MANPAGES += \
|
||||
man/bootup.7 \
|
||||
man/busctl.1 \
|
||||
man/daemon.7 \
|
||||
man/environment.d.5 \
|
||||
man/file-hierarchy.7 \
|
||||
man/halt.8 \
|
||||
man/hostname.5 \
|
||||
@ -110,6 +111,7 @@ MANPAGES += \
|
||||
man/systemd-debug-generator.8 \
|
||||
man/systemd-delta.1 \
|
||||
man/systemd-detect-virt.1 \
|
||||
man/systemd-environment-d-generator.8 \
|
||||
man/systemd-escape.1 \
|
||||
man/systemd-fsck@.service.8 \
|
||||
man/systemd-fstab-generator.8 \
|
||||
@ -146,6 +148,7 @@ MANPAGES += \
|
||||
man/systemd.1 \
|
||||
man/systemd.automount.5 \
|
||||
man/systemd.device.5 \
|
||||
man/systemd.environment-generator.7 \
|
||||
man/systemd.exec.5 \
|
||||
man/systemd.generator.7 \
|
||||
man/systemd.journal-fields.7 \
|
||||
@ -184,6 +187,7 @@ MANPAGES += \
|
||||
man/udev_new.3 \
|
||||
man/udevadm.8
|
||||
MANPAGES_ALIAS += \
|
||||
man/30-systemd-environment-d-generator.8 \
|
||||
man/SD_ALERT.3 \
|
||||
man/SD_BUS_ERROR_ACCESS_DENIED.3 \
|
||||
man/SD_BUS_ERROR_ADDRESS_IN_USE.3 \
|
||||
@ -542,6 +546,7 @@ MANPAGES_ALIAS += \
|
||||
man/udev_ref.3 \
|
||||
man/udev_unref.3 \
|
||||
man/user.conf.d.5
|
||||
man/30-systemd-environment-d-generator.8: man/systemd-environment-d-generator.8
|
||||
man/SD_ALERT.3: man/sd-daemon.3
|
||||
man/SD_BUS_ERROR_ACCESS_DENIED.3: man/sd-bus-errors.3
|
||||
man/SD_BUS_ERROR_ADDRESS_IN_USE.3: man/sd-bus-errors.3
|
||||
@ -900,6 +905,9 @@ man/udev_monitor_unref.3: man/udev_monitor_new_from_netlink.3
|
||||
man/udev_ref.3: man/udev_new.3
|
||||
man/udev_unref.3: man/udev_new.3
|
||||
man/user.conf.d.5: man/systemd-system.conf.5
|
||||
man/30-systemd-environment-d-generator.html: man/systemd-environment-d-generator.html
|
||||
$(html-alias)
|
||||
|
||||
man/SD_ALERT.html: man/sd-daemon.html
|
||||
$(html-alias)
|
||||
|
||||
@ -2640,6 +2648,7 @@ EXTRA_DIST += \
|
||||
man/crypttab.xml \
|
||||
man/daemon.xml \
|
||||
man/dnssec-trust-anchors.d.xml \
|
||||
man/environment.d.xml \
|
||||
man/file-hierarchy.xml \
|
||||
man/halt.xml \
|
||||
man/hostname.xml \
|
||||
@ -2772,6 +2781,7 @@ EXTRA_DIST += \
|
||||
man/systemd-debug-generator.xml \
|
||||
man/systemd-delta.xml \
|
||||
man/systemd-detect-virt.xml \
|
||||
man/systemd-environment-d-generator.xml \
|
||||
man/systemd-escape.xml \
|
||||
man/systemd-firstboot.xml \
|
||||
man/systemd-fsck@.service.xml \
|
||||
@ -2832,6 +2842,7 @@ EXTRA_DIST += \
|
||||
man/systemd-volatile-root.service.xml \
|
||||
man/systemd.automount.xml \
|
||||
man/systemd.device.xml \
|
||||
man/systemd.environment-generator.xml \
|
||||
man/systemd.exec.xml \
|
||||
man/systemd.generator.xml \
|
||||
man/systemd.journal-fields.xml \
|
||||
|
46
Makefile.am
46
Makefile.am
@ -68,6 +68,7 @@ catalogstatedir=$(systemdstatedir)/catalog
|
||||
xinitrcdir=$(sysconfdir)/X11/xinit/xinitrc.d
|
||||
|
||||
# Our own, non-special dirs
|
||||
environmentdir=$(prefix)/lib/environment.d
|
||||
pkgsysconfdir=$(sysconfdir)/systemd
|
||||
userunitdir=$(prefix)/lib/systemd/user
|
||||
userpresetdir=$(prefix)/lib/systemd/user-preset
|
||||
@ -80,6 +81,8 @@ networkdir=$(rootprefix)/lib/systemd/network
|
||||
pkgincludedir=$(includedir)/systemd
|
||||
systemgeneratordir=$(rootlibexecdir)/system-generators
|
||||
usergeneratordir=$(prefix)/lib/systemd/user-generators
|
||||
systemenvgeneratordir=$(prefix)/lib/systemd/system-environment-generators
|
||||
userenvgeneratordir=$(prefix)/lib/systemd/user-environment-generators
|
||||
systemshutdowndir=$(rootlibexecdir)/system-shutdown
|
||||
systemsleepdir=$(rootlibexecdir)/system-sleep
|
||||
systemunitdir=$(rootprefix)/lib/systemd/system
|
||||
@ -207,6 +210,8 @@ AM_CPPFLAGS = \
|
||||
-DSYSTEMD_CRYPTSETUP_PATH=\"$(rootlibexecdir)/systemd-cryptsetup\" \
|
||||
-DSYSTEM_GENERATOR_PATH=\"$(systemgeneratordir)\" \
|
||||
-DUSER_GENERATOR_PATH=\"$(usergeneratordir)\" \
|
||||
-DSYSTEM_ENV_GENERATOR_PATH=\"$(systemenvgeneratordir)\" \
|
||||
-DUSER_ENV_GENERATOR_PATH=\"$(userenvgeneratordir)\" \
|
||||
-DSYSTEM_SHUTDOWN_PATH=\"$(systemshutdowndir)\" \
|
||||
-DSYSTEM_SLEEP_PATH=\"$(systemsleepdir)\" \
|
||||
-DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \
|
||||
@ -307,6 +312,10 @@ endef
|
||||
install-directories-hook:
|
||||
$(MKDIR_P) $(addprefix $(DESTDIR),$(INSTALL_DIRS))
|
||||
|
||||
install-environment-conf-hook: install-directories-hook
|
||||
$(AM_V_LN)$(LN_S) --relative -f $(DESTDIR)$(sysconfdir)/environment \
|
||||
$(DESTDIR)$(environmentdir)/99-environment.conf
|
||||
|
||||
install-aliases-hook:
|
||||
set -- $(SYSTEM_UNIT_ALIASES) && \
|
||||
dir=$(systemunitdir) && $(install-aliases)
|
||||
@ -340,11 +349,14 @@ INSTALL_EXEC_HOOKS += \
|
||||
install-target-wants-hook \
|
||||
install-directories-hook \
|
||||
install-aliases-hook \
|
||||
install-touch-usr-hook
|
||||
|
||||
INSTALL_EXEC_HOOKS += \
|
||||
install-touch-usr-hook \
|
||||
install-busnames-target-wants-hook
|
||||
|
||||
if ENABLE_ENVIRONMENT_D
|
||||
INSTALL_EXEC_HOOKS += \
|
||||
install-environment-conf-hook
|
||||
endif
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
AM_V_M4 = $(AM_V_M4_$(V))
|
||||
AM_V_M4_ = $(AM_V_M4_$(AM_DEFAULT_VERBOSITY))
|
||||
@ -425,6 +437,11 @@ systemgenerator_PROGRAMS = \
|
||||
systemd-system-update-generator \
|
||||
systemd-debug-generator
|
||||
|
||||
if ENABLE_ENVIRONMENT_D
|
||||
userenvgenerator_PROGRAMS = \
|
||||
30-systemd-environment-d-generator
|
||||
endif
|
||||
|
||||
dist_bashcompletion_data = \
|
||||
shell-completion/bash/busctl \
|
||||
shell-completion/bash/journalctl \
|
||||
@ -764,7 +781,9 @@ EXTRA_DIST += \
|
||||
tools/make-man-rules.py \
|
||||
tools/make-directive-index.py \
|
||||
tools/xml_helper.py \
|
||||
man/glib-event-glue.c
|
||||
man/glib-event-glue.c \
|
||||
man/50-xdg-data-dirs.sh \
|
||||
man/90-rearrange-path.py
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
noinst_LTLIBRARIES += \
|
||||
@ -876,6 +895,8 @@ libbasic_la_SOURCES = \
|
||||
src/basic/bus-label.h \
|
||||
src/basic/ratelimit.h \
|
||||
src/basic/ratelimit.c \
|
||||
src/basic/exec-util.c \
|
||||
src/basic/exec-util.h \
|
||||
src/basic/exit-status.c \
|
||||
src/basic/exit-status.h \
|
||||
src/basic/virt.c \
|
||||
@ -1533,6 +1554,7 @@ tests += \
|
||||
test-ellipsize \
|
||||
test-util \
|
||||
test-mount-util \
|
||||
test-exec-util \
|
||||
test-cpu-set-util \
|
||||
test-hexdecoct \
|
||||
test-escape \
|
||||
@ -1921,6 +1943,12 @@ test_mount_util_SOURCES = \
|
||||
test_mount_util_LDADD = \
|
||||
libsystemd-shared.la
|
||||
|
||||
test_exec_util_SOURCES = \
|
||||
src/test/test-exec-util.c
|
||||
|
||||
test_exec_util_LDADD = \
|
||||
libsystemd-shared.la
|
||||
|
||||
test_hexdecoct_SOURCES = \
|
||||
src/test/test-hexdecoct.c
|
||||
|
||||
@ -2821,6 +2849,13 @@ systemd_system_update_generator_SOURCES = \
|
||||
systemd_system_update_generator_LDADD = \
|
||||
libsystemd-shared.la
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
30_systemd_environment_d_generator_SOURCES = \
|
||||
src/environment-d-generator/environment-d-generator.c
|
||||
|
||||
30_systemd_environment_d_generator_LDADD = \
|
||||
libsystemd-shared.la
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
if ENABLE_HIBERNATE
|
||||
systemgenerator_PROGRAMS += \
|
||||
@ -6222,6 +6257,8 @@ substitutions = \
|
||||
'|sysctldir=$(sysctldir)|' \
|
||||
'|systemgeneratordir=$(systemgeneratordir)|' \
|
||||
'|usergeneratordir=$(usergeneratordir)|' \
|
||||
'|systemenvgeneratordir=$(systemenvgeneratordir)|' \
|
||||
'|userenvgeneratordir=$(userenvgeneratordir)|' \
|
||||
'|CERTIFICATEROOT=$(CERTIFICATEROOT)|' \
|
||||
'|PACKAGE_VERSION=$(PACKAGE_VERSION)|' \
|
||||
'|PACKAGE_NAME=$(PACKAGE_NAME)|' \
|
||||
@ -6485,6 +6522,7 @@ INSTALL_DIRS += \
|
||||
endif
|
||||
|
||||
INSTALL_DIRS += \
|
||||
$(environmentdir) \
|
||||
$(prefix)/lib/modules-load.d \
|
||||
$(sysconfdir)/modules-load.d \
|
||||
$(prefix)/lib/systemd/network \
|
||||
|
@ -1040,6 +1040,14 @@ if test "x$enable_tmpfiles" != "xno"; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_TMPFILES, [test "$have_tmpfiles" = "yes"])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
have_environment_d=no
|
||||
AC_ARG_ENABLE(environment-d, AS_HELP_STRING([--disable-environment-d], [disable environment.d support]))
|
||||
if test "x$enable_environment_d" != "xno"; then
|
||||
have_environment_d=yes
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_ENVIRONMENT_D, [test "$have_environment_d" = "yes"])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
have_sysusers=no
|
||||
AC_ARG_ENABLE(sysusers, AS_HELP_STRING([--disable-sysusers], [disable sysusers support]))
|
||||
@ -1652,6 +1660,7 @@ AC_MSG_RESULT([
|
||||
vconsole: ${have_vconsole}
|
||||
quotacheck: ${have_quotacheck}
|
||||
tmpfiles: ${have_tmpfiles}
|
||||
environment.d: ${have_environment_d}
|
||||
sysusers: ${have_sysusers}
|
||||
firstboot: ${have_firstboot}
|
||||
randomseed: ${have_randomseed}
|
||||
|
12
man/50-xdg-data-dirs.sh
Executable file
12
man/50-xdg-data-dirs.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# set the default value
|
||||
XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share/:/usr/share}"
|
||||
|
||||
# add a directory if it exists
|
||||
if [[ -d /opt/foo/share ]]; then
|
||||
XDG_DATA_DIRS=/opt/foo/share:${XDG_DATA_DIRS}
|
||||
fi
|
||||
|
||||
# write our output
|
||||
echo XDG_DATA_DIRS=$XDG_DATA_DIRS
|
40
man/90-rearrange-path.py
Executable file
40
man/90-rearrange-path.py
Executable file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
"""
|
||||
|
||||
Proof-of-concept systemd environment generator that makes sure that bin dirs
|
||||
are always after matching sbin dirs in the path.
|
||||
(Changes /sbin:/bin:/foo/bar to /bin:/sbin:/foo/bar.)
|
||||
|
||||
This generator shows how to override the configuration possibly created by
|
||||
earlier generators. It would be easier to write in bash, but let's have it
|
||||
in Python just to prove that we can, and to serve as a template for more
|
||||
interesting generators.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
def rearrange_bin_sbin(path):
|
||||
"""Make sure any pair of …/bin, …/sbin directories is in this order
|
||||
|
||||
>>> rearrange_bin_sbin('/bin:/sbin:/usr/sbin:/usr/bin')
|
||||
'/bin:/sbin:/usr/bin:/usr/sbin'
|
||||
"""
|
||||
items = [pathlib.Path(p) for p in path.split(':')]
|
||||
for i in range(len(items)):
|
||||
if 'sbin' in items[i].parts:
|
||||
ind = items[i].parts.index('sbin')
|
||||
bin = pathlib.Path(*items[i].parts[:ind], 'bin', *items[i].parts[ind+1:])
|
||||
if bin in items[i+1:]:
|
||||
j = i + 1 + items[i+1:].index(bin)
|
||||
items[i], items[j] = items[j], items[i]
|
||||
return ':'.join(p.as_posix() for p in items)
|
||||
|
||||
if __name__ == '__main__':
|
||||
path = os.environ['PATH'] # This should be always set.
|
||||
# If it's not, we'll just crash, we is OK too.
|
||||
new = rearrange_bin_sbin(path)
|
||||
if new != path:
|
||||
print('PATH={}'.format(new))
|
122
man/environment.d.xml
Normal file
122
man/environment.d.xml
Normal file
@ -0,0 +1,122 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!--
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Red Hat, Inc.
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<refentry id="environment.d" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>environment.d</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
<firstname>Ray</firstname>
|
||||
<surname>Strode</surname>
|
||||
<email>rstrode@redhat.com</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>environment.d</refentrytitle>
|
||||
<manvolnum>5</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>environment.d</refname>
|
||||
<refpurpose>Definition of user session environment</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>~/.config/environment.d/*.conf</filename></para>
|
||||
<para><filename>/etc/environment.d/*.conf</filename></para>
|
||||
<para><filename>/run/environment.d/*.conf</filename></para>
|
||||
<para><filename>/usr/lib/environment.d/*.conf</filename></para>
|
||||
<para><filename>/etc/environment</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>The <filename>environment.d</filename> directories contain a list of "global" environment
|
||||
variable assignments for the user environment.
|
||||
<citerefentry><refentrytitle>systemd-environment-d-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
parses them and updates the environment exported by the systemd user instance to the services it
|
||||
starts.</para>
|
||||
|
||||
<para>It is recommended to use numerical prefixes for file names to simplify ordering.</para>
|
||||
|
||||
<para>For backwards compatibility, a symlink to <filename>/etc/environment</filename> is
|
||||
installed, so this file is also parsed.</para>
|
||||
</refsect1>
|
||||
|
||||
<xi:include href="standard-conf.xml" xpointer="confd" />
|
||||
|
||||
<refsect1>
|
||||
<title>Configuration Format</title>
|
||||
|
||||
<para>The configuration files contain a list of
|
||||
<literal><replaceable>KEY</replaceable>=<replaceable>VALUE</replaceable></literal> environment
|
||||
variable assignments, separated by newlines. The right hand side of these assignments may
|
||||
reference previously defined environment variables, using the <literal>${OTHER_KEY}</literal>
|
||||
and <literal>$OTHER_KEY</literal> format. It is also possible to use
|
||||
|
||||
<literal>${<replaceable>FOO</replaceable>:-<replaceable>DEFAULT_VALUE</replaceable>}</literal>
|
||||
to expand in the same way as <literal>${<replaceable>FOO</replaceable>}</literal> unless the
|
||||
expansion would be empty, in which case it expands to <replaceable>DEFAULT_VALUE</replaceable>,
|
||||
and use
|
||||
<literal>${<replaceable>FOO</replaceable>:+<replaceable>ALTERNATE_VALUE</replaceable>}</literal>
|
||||
to expand to <replaceable>ALTERNATE_VALUE</replaceable> as long as
|
||||
<literal>${<replaceable>FOO</replaceable>}</literal> would have expanded to a non-empty value.
|
||||
No other elements of shell syntax are supported.</para>
|
||||
|
||||
<para>Each<replaceable>KEY</replaceable> must be a valid variable name. Empty lines
|
||||
and lines beginning with the comment character <literal>#</literal> are ignored.</para>
|
||||
|
||||
<refsect2>
|
||||
<title>Example</title>
|
||||
<example>
|
||||
<title>Setup environment to allow access to a program installed in
|
||||
<filename noindex='true'>/opt/foo</filename></title>
|
||||
|
||||
<para><filename>/etc/environment.d/60-foo.conf</filename>:
|
||||
</para>
|
||||
<programlisting>
|
||||
FOO_DEBUG=force-software-gl,log-verbose
|
||||
PATH=/opt/foo/bin:$PATH
|
||||
LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}/opt/foo/lib
|
||||
XDG_DATA_DIRS=/opt/foo/share:${XDG_DATA_DIRS:-/usr/local/share/:/usr/share/}
|
||||
</programlisting>
|
||||
</example>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-environment-d-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.environment-generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
80
man/systemd-environment-d-generator.xml
Normal file
80
man/systemd-environment-d-generator.xml
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
|
||||
<!ENTITY % entities SYSTEM "custom-entities.ent" >
|
||||
%entities;
|
||||
]>
|
||||
|
||||
<!--
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<refentry id="systemd-environment-d-generator">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-environment-d-generator</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
<firstname>Zbigniew</firstname>
|
||||
<surname>Jędrzejewski-Szmek</surname>
|
||||
<email>zbyszek@in.waw.pl</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-environment-d-generator</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-environment-d-generator</refname>
|
||||
<refname>30-systemd-environment-d-generator</refname>
|
||||
<refpurpose>Load variables specified by <filename>environment.d</filename>
|
||||
</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>&userenvgeneratordir;/30-systemd-environment-d-generator</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-environment-d-generator</filename> is a
|
||||
<citerefentry><refentrytitle>systemd.environment-generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
that reads environment configuration specified by
|
||||
<citerefentry><refentrytitle>environment.d</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
configuration files and passes it to the
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
user manager instance.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.environment-generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
160
man/systemd.environment-generator.xml
Normal file
160
man/systemd.environment-generator.xml
Normal file
@ -0,0 +1,160 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
|
||||
<!ENTITY % entities SYSTEM "custom-entities.ent" >
|
||||
%entities;
|
||||
]>
|
||||
|
||||
<!--
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<refentry id="systemd.environment-generator" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<refentryinfo>
|
||||
<title>systemd.environment-generator</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
<firstname>Zbigniew</firstname>
|
||||
<surname>Jędrzejewski-Szmek</surname>
|
||||
<email>zbyszek@in.waw.pl</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd.environment-generator</refentrytitle>
|
||||
<manvolnum>7</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd.environment-generator</refname>
|
||||
<refpurpose>Systemd environment file generators</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>&systemenvgeneratordir;/some-generator</command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>&userenvgeneratordir;/some-generator</command>
|
||||
</cmdsynopsis>
|
||||
|
||||
<para>
|
||||
<literallayout><filename>/run/systemd/system-environment-generators/*</filename>
|
||||
<filename>/etc/systemd/system-environment-generators/*</filename>
|
||||
<filename>/usr/local/lib/systemd/system-environment-generators/*</filename>
|
||||
<filename>&systemenvgeneratordir;/*</filename></literallayout>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literallayout><filename>/run/systemd/user-environment-generators/*</filename>
|
||||
<filename>/etc/systemd/user-environment-generators/*</filename>
|
||||
<filename>/usr/local/lib/systemd/user-environment-generators/*</filename>
|
||||
<filename>&userenvgeneratordir;/*</filename></literallayout>
|
||||
</para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
<para>Generators are small executables that live in
|
||||
<filename>&systemenvgeneratordir;/</filename> and other directories listed above.
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> will
|
||||
execute those binaries very early at the startup of each manager and at configuration
|
||||
reload time, before running the generators described in
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
and before starting any units. Environment generators can override the environment that the
|
||||
manager exports to services and other processes.</para>
|
||||
|
||||
<para>Generators are loaded from a set of paths determined during compilation, as listed
|
||||
above. System and user environment generators are loaded from directories with names ending in
|
||||
<filename>system-environment-generators/</filename> and
|
||||
<filename>user-environment-generators/</filename>, respectively. Generators found in directories
|
||||
listed earlier override the ones with the same name in directories lower in the list. A symlink
|
||||
to <filename>/dev/null</filename> or an empty file can be used to mask a generator, thereby
|
||||
preventing it from running. Please note that the order of the two directories with the highest
|
||||
priority is reversed with respect to the unit load path, and generators in
|
||||
<filename>/run</filename> overwrite those in <filename>/etc</filename>.</para>
|
||||
|
||||
<para>After installing new generators or updating the configuration, <command>systemctl
|
||||
daemon-reload</command> may be executed. This will re-run all generators, updating environment
|
||||
configuration. It will be used for any services that are started subsequently.</para>
|
||||
|
||||
<para>Environment file generators are executed similarly to unit file generators described
|
||||
in
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
with the following differences:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Generators are executed sequentially in the alphanumerical order of the final
|
||||
component of their name. The output of each generator output is immediately parsed and used
|
||||
to update the environment for generators that run after that. Thus, later generators can use
|
||||
and/or modify the output of earlier generators.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Generators are run by every manager instance, their output can be different for each
|
||||
user.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>It is recommended to use numerical prefixes for generator names to simplify ordering.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Examples</title>
|
||||
|
||||
<example>
|
||||
<title>A simple generator that extends an environment variable if a directory exists in the file system</title>
|
||||
|
||||
<programlisting># 50-xdg-data-dirs.sh
|
||||
|
||||
<xi:include href="50-xdg-data-dirs.sh" parse="text" /></programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>A more complicated generator which reads existing configuration and mutates one variable</title>
|
||||
|
||||
<programlisting># 90-rearrange-path.py
|
||||
|
||||
<xi:include href="90-rearrange-path.py" parse="text" /></programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Debugging a generator</title>
|
||||
|
||||
<programlisting>SYSTEMD_LOG_LEVEL=debug VAR_A=something VAR_B="something else" \
|
||||
&systemenvgeneratordir;/path-to-generator
|
||||
</programlisting>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See also</title>
|
||||
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd-environment-d-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
@ -342,7 +342,8 @@ find $dir</programlisting>
|
||||
<citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-sysv-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.environment-generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
@ -137,7 +137,6 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const ch
|
||||
va_list ap;
|
||||
|
||||
assert(strv);
|
||||
assert(suffix);
|
||||
|
||||
va_start(ap, dir);
|
||||
dirs = strv_new_ap(dir, ap);
|
||||
@ -153,7 +152,6 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, c
|
||||
_cleanup_strv_free_ char **dirs = NULL;
|
||||
|
||||
assert(strv);
|
||||
assert(suffix);
|
||||
|
||||
dirs = strv_split_nulstr(d);
|
||||
if (!dirs)
|
||||
|
@ -73,18 +73,18 @@
|
||||
#define NOTIFY_BUFFER_MAX PIPE_BUF
|
||||
|
||||
#ifdef HAVE_SPLIT_USR
|
||||
#define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
|
||||
# define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
|
||||
#else
|
||||
#define _CONF_PATHS_SPLIT_USR(n)
|
||||
# define _CONF_PATHS_SPLIT_USR(n)
|
||||
#endif
|
||||
|
||||
/* Return a nulstr for a standard cascade of configuration paths,
|
||||
* suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
|
||||
* to implement drop-in directories for extending configuration
|
||||
* files. */
|
||||
#define CONF_PATHS_NULSTR(n) \
|
||||
"/etc/" n "\0" \
|
||||
"/run/" n "\0" \
|
||||
"/usr/local/lib/" n "\0" \
|
||||
"/usr/lib/" n "\0" \
|
||||
#define CONF_PATHS_NULSTR(n) \
|
||||
"/etc/" n "\0" \
|
||||
"/run/" n "\0" \
|
||||
"/usr/local/lib/" n "\0" \
|
||||
"/usr/lib/" n "\0" \
|
||||
_CONF_PATHS_SPLIT_USR(n)
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "extract-word.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
@ -247,7 +248,7 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_pure_ static bool env_match(const char *t, const char *pattern) {
|
||||
static bool env_match(const char *t, const char *pattern) {
|
||||
assert(t);
|
||||
assert(pattern);
|
||||
|
||||
@ -273,6 +274,19 @@ _pure_ static bool env_match(const char *t, const char *pattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool env_entry_has_name(const char *entry, const char *name) {
|
||||
const char *t;
|
||||
|
||||
assert(entry);
|
||||
assert(name);
|
||||
|
||||
t = startswith(entry, name);
|
||||
if (!t)
|
||||
return false;
|
||||
|
||||
return *t == '=';
|
||||
}
|
||||
|
||||
char **strv_env_delete(char **x, unsigned n_lists, ...) {
|
||||
size_t n, i = 0;
|
||||
char **k, **r;
|
||||
@ -386,18 +400,24 @@ char **strv_env_unset_many(char **l, ...) {
|
||||
|
||||
int strv_env_replace(char ***l, char *p) {
|
||||
char **f;
|
||||
const char *t, *name;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* Replace first occurrence of the env var or add a new one in the
|
||||
* string list. Drop other occurences. Edits in-place. Does not copy p.
|
||||
* p must be a valid key=value assignment.
|
||||
*/
|
||||
|
||||
t = strchr(p, '=');
|
||||
assert(t);
|
||||
|
||||
name = strndupa(p, t - p);
|
||||
|
||||
for (f = *l; f && *f; f++)
|
||||
if (env_match(*f, p)) {
|
||||
free(*f);
|
||||
*f = p;
|
||||
strv_env_unset(f + 1, p);
|
||||
if (env_entry_has_name(*f, name)) {
|
||||
free_and_replace(*f, p);
|
||||
strv_env_unset(f + 1, *f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -434,7 +454,7 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *strv_env_get_n(char **l, const char *name, size_t k) {
|
||||
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
|
||||
char **i;
|
||||
|
||||
assert(name);
|
||||
@ -442,18 +462,25 @@ char *strv_env_get_n(char **l, const char *name, size_t k) {
|
||||
if (k <= 0)
|
||||
return NULL;
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
STRV_FOREACH_BACKWARDS(i, l)
|
||||
if (strneq(*i, name, k) &&
|
||||
(*i)[k] == '=')
|
||||
return *i + k + 1;
|
||||
|
||||
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
|
||||
const char *t;
|
||||
|
||||
t = strndupa(name, k);
|
||||
return getenv(t);
|
||||
};
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *strv_env_get(char **l, const char *name) {
|
||||
assert(name);
|
||||
|
||||
return strv_env_get_n(l, name, strlen(name));
|
||||
return strv_env_get_n(l, name, strlen(name), 0);
|
||||
}
|
||||
|
||||
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
|
||||
@ -492,19 +519,26 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
|
||||
return e;
|
||||
}
|
||||
|
||||
char *replace_env(const char *format, char **env) {
|
||||
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
|
||||
enum {
|
||||
WORD,
|
||||
CURLY,
|
||||
VARIABLE
|
||||
VARIABLE,
|
||||
VARIABLE_RAW,
|
||||
TEST,
|
||||
DEFAULT_VALUE,
|
||||
ALTERNATE_VALUE,
|
||||
} state = WORD;
|
||||
|
||||
const char *e, *word = format;
|
||||
char *r = NULL, *k;
|
||||
const char *e, *word = format, *test_value;
|
||||
char *k;
|
||||
_cleanup_free_ char *r = NULL;
|
||||
size_t i, len;
|
||||
int nest = 0;
|
||||
|
||||
assert(format);
|
||||
|
||||
for (e = format; *e; e ++) {
|
||||
for (e = format, i = 0; *e && i < n; e ++, i ++) {
|
||||
|
||||
switch (state) {
|
||||
|
||||
@ -517,24 +551,36 @@ char *replace_env(const char *format, char **env) {
|
||||
if (*e == '{') {
|
||||
k = strnappend(r, word, e-word-1);
|
||||
if (!k)
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
free(r);
|
||||
r = k;
|
||||
|
||||
word = e-1;
|
||||
state = VARIABLE;
|
||||
|
||||
nest++;
|
||||
} else if (*e == '$') {
|
||||
k = strnappend(r, word, e-word);
|
||||
if (!k)
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
free(r);
|
||||
r = k;
|
||||
|
||||
word = e+1;
|
||||
state = WORD;
|
||||
|
||||
} else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
|
||||
k = strnappend(r, word, e-word-1);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
free(r);
|
||||
r = k;
|
||||
|
||||
word = e-1;
|
||||
state = VARIABLE_RAW;
|
||||
|
||||
} else
|
||||
state = WORD;
|
||||
break;
|
||||
@ -543,11 +589,68 @@ char *replace_env(const char *format, char **env) {
|
||||
if (*e == '}') {
|
||||
const char *t;
|
||||
|
||||
t = strempty(strv_env_get_n(env, word+2, e-word-2));
|
||||
t = strv_env_get_n(env, word+2, e-word-2, flags);
|
||||
|
||||
k = strappend(r, t);
|
||||
if (!k)
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
free(r);
|
||||
r = k;
|
||||
|
||||
word = e+1;
|
||||
state = WORD;
|
||||
} else if (*e == ':') {
|
||||
if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
|
||||
/* Treat this as unsupported syntax, i.e. do no replacement */
|
||||
state = WORD;
|
||||
else {
|
||||
len = e-word-2;
|
||||
state = TEST;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TEST:
|
||||
if (*e == '-')
|
||||
state = DEFAULT_VALUE;
|
||||
else if (*e == '+')
|
||||
state = ALTERNATE_VALUE;
|
||||
else {
|
||||
state = WORD;
|
||||
break;
|
||||
}
|
||||
|
||||
test_value = e+1;
|
||||
break;
|
||||
|
||||
case DEFAULT_VALUE: /* fall through */
|
||||
case ALTERNATE_VALUE:
|
||||
assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
|
||||
|
||||
if (*e == '{') {
|
||||
nest++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*e != '}')
|
||||
break;
|
||||
|
||||
nest--;
|
||||
if (nest == 0) { // || !strchr(e+1, '}')) {
|
||||
const char *t;
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
t = strv_env_get_n(env, word+2, len, flags);
|
||||
|
||||
if (t && state == ALTERNATE_VALUE)
|
||||
t = v = replace_env_n(test_value, e-test_value, env, flags);
|
||||
else if (!t && state == DEFAULT_VALUE)
|
||||
t = v = replace_env_n(test_value, e-test_value, env, flags);
|
||||
|
||||
k = strappend(r, t);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
free(r);
|
||||
r = k;
|
||||
@ -556,18 +659,39 @@ char *replace_env(const char *format, char **env) {
|
||||
state = WORD;
|
||||
}
|
||||
break;
|
||||
|
||||
case VARIABLE_RAW:
|
||||
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
|
||||
|
||||
if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
|
||||
const char *t;
|
||||
|
||||
t = strv_env_get_n(env, word+1, e-word-1, flags);
|
||||
|
||||
k = strappend(r, t);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
free(r);
|
||||
r = k;
|
||||
|
||||
word = e--;
|
||||
i--;
|
||||
state = WORD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
k = strnappend(r, word, e-word);
|
||||
if (!k)
|
||||
goto fail;
|
||||
if (state == VARIABLE_RAW) {
|
||||
const char *t;
|
||||
|
||||
free(r);
|
||||
return k;
|
||||
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
|
||||
|
||||
fail:
|
||||
return mfree(r);
|
||||
t = strv_env_get_n(env, word+1, e-word-1, flags);
|
||||
return strappend(r, t);
|
||||
} else
|
||||
return strnappend(r, word, e-word);
|
||||
}
|
||||
|
||||
char **replace_env_argv(char **argv, char **env) {
|
||||
@ -623,7 +747,7 @@ char **replace_env_argv(char **argv, char **env) {
|
||||
}
|
||||
|
||||
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
|
||||
ret[k] = replace_env(*i, env);
|
||||
ret[k] = replace_env(*i, env, 0);
|
||||
if (!ret[k]) {
|
||||
strv_free(ret);
|
||||
return NULL;
|
||||
@ -644,3 +768,39 @@ int getenv_bool(const char *p) {
|
||||
|
||||
return parse_boolean(e);
|
||||
}
|
||||
|
||||
int serialize_environment(FILE *f, char **environment) {
|
||||
char **e;
|
||||
|
||||
STRV_FOREACH(e, environment) {
|
||||
_cleanup_free_ char *ce;
|
||||
|
||||
ce = cescape(*e);
|
||||
if (!ce)
|
||||
return -ENOMEM;
|
||||
|
||||
fprintf(f, "env=%s\n", *e);
|
||||
}
|
||||
|
||||
/* caller should call ferror() */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int deserialize_environment(char ***environment, const char *line) {
|
||||
char *uce = NULL;
|
||||
int r;
|
||||
|
||||
assert(line);
|
||||
assert(environment);
|
||||
|
||||
assert(startswith(line, "env="));
|
||||
r = cunescape(line + 4, UNESCAPE_RELAX, &uce);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!env_assignment_is_valid(uce))
|
||||
return -EINVAL;
|
||||
|
||||
return strv_env_replace(environment, uce);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
@ -28,9 +29,19 @@ bool env_name_is_valid(const char *e);
|
||||
bool env_value_is_valid(const char *e);
|
||||
bool env_assignment_is_valid(const char *e);
|
||||
|
||||
char *replace_env(const char *format, char **env);
|
||||
enum {
|
||||
REPLACE_ENV_USE_ENVIRONMENT = 1u,
|
||||
REPLACE_ENV_ALLOW_BRACELESS = 2u,
|
||||
REPLACE_ENV_ALLOW_EXTENDED = 4u,
|
||||
};
|
||||
|
||||
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
|
||||
char **replace_env_argv(char **argv, char **env);
|
||||
|
||||
static inline char *replace_env(const char *format, char **env, unsigned flags) {
|
||||
return replace_env_n(format, strlen(format), env, flags);
|
||||
}
|
||||
|
||||
bool strv_env_is_valid(char **e);
|
||||
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
|
||||
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
|
||||
@ -46,7 +57,10 @@ char **strv_env_unset(char **l, const char *p); /* In place ... */
|
||||
char **strv_env_unset_many(char **l, ...) _sentinel_;
|
||||
int strv_env_replace(char ***l, char *p); /* In place ... */
|
||||
|
||||
char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
|
||||
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
|
||||
char *strv_env_get(char **x, const char *n) _pure_;
|
||||
|
||||
int getenv_bool(const char *p);
|
||||
|
||||
int serialize_environment(FILE *f, char **environment);
|
||||
int deserialize_environment(char ***environment, const char *line);
|
||||
|
360
src/basic/exec-util.c
Normal file
360
src/basic/exec-util.c
Normal file
@ -0,0 +1,360 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "conf-files.h"
|
||||
#include "env-util.h"
|
||||
#include "exec-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
#include "process-util.h"
|
||||
#include "set.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Put this test here for a lack of better place */
|
||||
assert_cc(EAGAIN == EWOULDBLOCK);
|
||||
|
||||
static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
|
||||
|
||||
pid_t _pid;
|
||||
|
||||
if (null_or_empty_path(path)) {
|
||||
log_debug("%s is empty (a mask).", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_pid = fork();
|
||||
if (_pid < 0)
|
||||
return log_error_errno(errno, "Failed to fork: %m");
|
||||
if (_pid == 0) {
|
||||
char *_argv[2];
|
||||
|
||||
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
||||
|
||||
if (stdout_fd >= 0) {
|
||||
/* If the fd happens to be in the right place, go along with that */
|
||||
if (stdout_fd != STDOUT_FILENO &&
|
||||
dup2(stdout_fd, STDOUT_FILENO) < 0)
|
||||
return -errno;
|
||||
|
||||
fd_cloexec(STDOUT_FILENO, false);
|
||||
}
|
||||
|
||||
if (!argv) {
|
||||
_argv[0] = (char*) path;
|
||||
_argv[1] = NULL;
|
||||
argv = _argv;
|
||||
} else
|
||||
argv[0] = (char*) path;
|
||||
|
||||
execv(path, argv);
|
||||
log_error_errno(errno, "Failed to execute %s: %m", path);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_debug("Spawned %s as " PID_FMT ".", path, _pid);
|
||||
*pid = _pid;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_execute(
|
||||
char **directories,
|
||||
usec_t timeout,
|
||||
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
|
||||
void* const callback_args[_STDOUT_CONSUME_MAX],
|
||||
int output_fd,
|
||||
char *argv[]) {
|
||||
|
||||
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
char **path;
|
||||
int r;
|
||||
|
||||
/* We fork this all off from a child process so that we can somewhat cleanly make
|
||||
* use of SIGALRM to set a time limit.
|
||||
*
|
||||
* If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
|
||||
*/
|
||||
|
||||
(void) reset_all_signal_handlers();
|
||||
(void) reset_signal_mask();
|
||||
|
||||
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
||||
|
||||
r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!callbacks) {
|
||||
pids = hashmap_new(NULL);
|
||||
if (!pids)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
/* Abort execution of this process after the timout. We simply rely on SIGALRM as
|
||||
* default action terminating the process, and turn on alarm(). */
|
||||
|
||||
if (timeout != USEC_INFINITY)
|
||||
alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
|
||||
|
||||
STRV_FOREACH(path, paths) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
pid_t pid;
|
||||
|
||||
t = strdup(*path);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
if (callbacks) {
|
||||
fd = open_serialization_fd(basename(*path));
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to open serialization file: %m");
|
||||
}
|
||||
|
||||
r = do_spawn(t, argv, fd, &pid);
|
||||
if (r <= 0)
|
||||
continue;
|
||||
|
||||
if (pids) {
|
||||
r = hashmap_put(pids, PID_TO_PTR(pid), t);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
t = NULL;
|
||||
} else {
|
||||
r = wait_for_terminate_and_warn(t, pid, true);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||
return log_error_errno(errno, "Failed to seek on serialization fd: %m");
|
||||
|
||||
r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
|
||||
fd = -1;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to process output from %s: %m", *path);
|
||||
}
|
||||
}
|
||||
|
||||
if (callbacks) {
|
||||
r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Callback two failed: %m");
|
||||
}
|
||||
|
||||
while (!hashmap_isempty(pids)) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
pid_t pid;
|
||||
|
||||
pid = PTR_TO_PID(hashmap_first_key(pids));
|
||||
assert(pid > 0);
|
||||
|
||||
t = hashmap_remove(pids, PID_TO_PTR(pid));
|
||||
assert(t);
|
||||
|
||||
wait_for_terminate_and_warn(t, pid, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int execute_directories(
|
||||
const char* const* directories,
|
||||
usec_t timeout,
|
||||
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
|
||||
void* const callback_args[_STDOUT_CONSUME_MAX],
|
||||
char *argv[]) {
|
||||
|
||||
pid_t executor_pid;
|
||||
char *name;
|
||||
char **dirs = (char**) directories;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(!strv_isempty(dirs));
|
||||
|
||||
name = basename(dirs[0]);
|
||||
assert(!isempty(name));
|
||||
|
||||
if (callbacks) {
|
||||
assert(callback_args);
|
||||
assert(callbacks[STDOUT_GENERATE]);
|
||||
assert(callbacks[STDOUT_COLLECT]);
|
||||
assert(callbacks[STDOUT_CONSUME]);
|
||||
|
||||
fd = open_serialization_fd(name);
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to open serialization file: %m");
|
||||
}
|
||||
|
||||
/* Executes all binaries in the directories serially or in parallel and waits for
|
||||
* them to finish. Optionally a timeout is applied. If a file with the same name
|
||||
* exists in more than one directory, the earliest one wins. */
|
||||
|
||||
executor_pid = fork();
|
||||
if (executor_pid < 0)
|
||||
return log_error_errno(errno, "Failed to fork: %m");
|
||||
|
||||
if (executor_pid == 0) {
|
||||
r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
|
||||
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = wait_for_terminate_and_warn(name, executor_pid, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Execution failed: %m");
|
||||
if (r > 0) {
|
||||
/* non-zero return code from child */
|
||||
log_error("Forker process failed.");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
if (!callbacks)
|
||||
return 0;
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||
return log_error_errno(errno, "Failed to rewind serialization fd: %m");
|
||||
|
||||
r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
|
||||
fd = -1;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse returned data: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gather_environment_generate(int fd, void *arg) {
|
||||
char ***env = arg, **x, **y;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_strv_free_ char **new;
|
||||
int r;
|
||||
|
||||
/* Read a series of VAR=value assignments from fd, use them to update the list of
|
||||
* variables in env. Also update the exported environment.
|
||||
*
|
||||
* fd is always consumed, even on error.
|
||||
*/
|
||||
|
||||
assert(env);
|
||||
|
||||
f = fdopen(fd, "r");
|
||||
if (!f) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = load_env_file_pairs(f, NULL, NULL, &new);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH_PAIR(x, y, new) {
|
||||
char *p;
|
||||
|
||||
if (!env_name_is_valid(*x)) {
|
||||
log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
|
||||
continue;
|
||||
}
|
||||
|
||||
p = strjoin(*x, "=", *y);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_env_replace(env, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (setenv(*x, *y, true) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int gather_environment_collect(int fd, void *arg) {
|
||||
char ***env = arg;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
/* Write out a series of env=cescape(VAR=value) assignments to fd. */
|
||||
|
||||
assert(env);
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
if (!f) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = serialize_environment(f, *env);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ferror(f))
|
||||
return errno > 0 ? -errno : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gather_environment_consume(int fd, void *arg) {
|
||||
char ***env = arg;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char line[LINE_MAX];
|
||||
int r = 0, k;
|
||||
|
||||
/* Read a series of env=cescape(VAR=value) assignments from fd into env. */
|
||||
|
||||
assert(env);
|
||||
|
||||
f = fdopen(fd, "r");
|
||||
if (!f) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
FOREACH_LINE(line, f, return -EIO) {
|
||||
truncate_nl(line);
|
||||
|
||||
k = deserialize_environment(env, line);
|
||||
if (k < 0)
|
||||
log_error_errno(k, "Invalid line \"%s\": %m", line);
|
||||
if (k < 0 && r == 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
const gather_stdout_callback_t gather_environment[] = {
|
||||
gather_environment_generate,
|
||||
gather_environment_collect,
|
||||
gather_environment_consume,
|
||||
};
|
40
src/basic/exec-util.h
Normal file
40
src/basic/exec-util.h
Normal file
@ -0,0 +1,40 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
typedef int (*gather_stdout_callback_t) (int fd, void *arg);
|
||||
|
||||
enum {
|
||||
STDOUT_GENERATE, /* from generators to helper process */
|
||||
STDOUT_COLLECT, /* from helper process to main process */
|
||||
STDOUT_CONSUME, /* process data in main process */
|
||||
_STDOUT_CONSUME_MAX,
|
||||
};
|
||||
|
||||
int execute_directories(
|
||||
const char* const* directories,
|
||||
usec_t timeout,
|
||||
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
|
||||
void* const callback_args[_STDOUT_CONSUME_MAX],
|
||||
char *argv[]);
|
||||
|
||||
extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ctype.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -586,14 +587,9 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_env_file_push(
|
||||
static int check_utf8ness_and_warn(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
|
||||
const char *k;
|
||||
va_list aq, *ap = userdata;
|
||||
const char *key, char *value) {
|
||||
|
||||
if (!utf8_is_valid(key)) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
@ -611,6 +607,23 @@ static int parse_env_file_push(
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
|
||||
const char *k;
|
||||
va_list aq, *ap = userdata;
|
||||
int r;
|
||||
|
||||
r = check_utf8ness_and_warn(filename, line, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
va_copy(aq, *ap);
|
||||
|
||||
while ((k = va_arg(aq, const char *))) {
|
||||
@ -662,27 +675,19 @@ static int load_env_file_push(
|
||||
char *p;
|
||||
int r;
|
||||
|
||||
if (!utf8_is_valid(key)) {
|
||||
_cleanup_free_ char *t = utf8_escape_invalid(key);
|
||||
r = check_utf8ness_and_warn(filename, line, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (value && !utf8_is_valid(value)) {
|
||||
_cleanup_free_ char *t = utf8_escape_invalid(value);
|
||||
|
||||
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
p = strjoin(key, "=", strempty(value));
|
||||
p = strjoin(key, "=", value);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(m, p);
|
||||
if (r < 0)
|
||||
r = strv_env_replace(m, p);
|
||||
if (r < 0) {
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (n_pushed)
|
||||
(*n_pushed)++;
|
||||
@ -716,19 +721,9 @@ static int load_env_file_push_pairs(
|
||||
char ***m = userdata;
|
||||
int r;
|
||||
|
||||
if (!utf8_is_valid(key)) {
|
||||
_cleanup_free_ char *t = utf8_escape_invalid(key);
|
||||
|
||||
log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (value && !utf8_is_valid(value)) {
|
||||
_cleanup_free_ char *t = utf8_escape_invalid(value);
|
||||
|
||||
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
|
||||
return -EINVAL;
|
||||
}
|
||||
r = check_utf8ness_and_warn(filename, line, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend(m, key);
|
||||
if (r < 0)
|
||||
@ -767,6 +762,51 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
|
||||
char ***env = userdata;
|
||||
char *expanded_value;
|
||||
|
||||
assert(env);
|
||||
|
||||
if (!value) {
|
||||
log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!env_name_is_valid(key)) {
|
||||
log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
expanded_value = replace_env(value, *env,
|
||||
REPLACE_ENV_USE_ENVIRONMENT|
|
||||
REPLACE_ENV_ALLOW_BRACELESS|
|
||||
REPLACE_ENV_ALLOW_EXTENDED);
|
||||
if (!expanded_value)
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(value, expanded_value);
|
||||
|
||||
return load_env_file_push(filename, line, key, value, env, n_pushed);
|
||||
}
|
||||
|
||||
int merge_env_file(
|
||||
char ***env,
|
||||
FILE *f,
|
||||
const char *fname) {
|
||||
|
||||
/* NOTE: this function supports braceful and braceless variable expansions,
|
||||
* plus "extended" substitutions, unlike other exported parsing functions.
|
||||
*/
|
||||
|
||||
return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
|
||||
}
|
||||
|
||||
static void write_env_var(FILE *f, const char *v) {
|
||||
const char *p;
|
||||
|
||||
@ -1342,6 +1382,25 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
int open_serialization_fd(const char *ident) {
|
||||
int fd = -1;
|
||||
|
||||
fd = memfd_create(ident, MFD_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
const char *path;
|
||||
|
||||
path = getpid() == 1 ? "/run/systemd" : "/tmp";
|
||||
fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
log_debug("Serializing %s to %s.", ident, path);
|
||||
} else
|
||||
log_debug("Serializing %s to memfd.", ident);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int link_tmpfile(int fd, const char *path, const char *target) {
|
||||
|
||||
assert(fd >= 0);
|
||||
|
@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
|
||||
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
|
||||
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
|
||||
|
||||
int merge_env_file(char ***env, FILE *f, const char *fname);
|
||||
|
||||
int write_env_file(const char *fname, char **l);
|
||||
|
||||
int executable_is_script(const char *path, char **interpreter);
|
||||
@ -84,6 +86,7 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
|
||||
|
||||
int open_tmpfile_unlinkable(const char *directory, int flags);
|
||||
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
|
||||
int open_serialization_fd(const char *ident);
|
||||
|
||||
int link_tmpfile(int fd, const char *path, const char *target);
|
||||
|
||||
|
@ -564,9 +564,6 @@ int strv_extend_front(char ***l, const char *value) {
|
||||
|
||||
/* Like strv_extend(), but prepends rather than appends the new entry */
|
||||
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
n = strv_length(*l);
|
||||
|
||||
/* Increase and overflow check. */
|
||||
@ -574,9 +571,12 @@ int strv_extend_front(char ***l, const char *value) {
|
||||
if (m < n)
|
||||
return -ENOMEM;
|
||||
|
||||
v = strdup(value);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
if (value) {
|
||||
v = strdup(value);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
v = NULL;
|
||||
|
||||
c = realloc_multiply(*l, sizeof(char*), m);
|
||||
if (!c) {
|
||||
|
143
src/basic/util.c
143
src/basic/util.c
@ -59,9 +59,6 @@
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Put this test here for a lack of better place */
|
||||
assert_cc(EAGAIN == EWOULDBLOCK);
|
||||
|
||||
int saved_argc = 0;
|
||||
char **saved_argv = NULL;
|
||||
static int saved_in_initrd = -1;
|
||||
@ -80,146 +77,6 @@ size_t page_size(void) {
|
||||
return pgsz;
|
||||
}
|
||||
|
||||
static int do_execute(char **directories, usec_t timeout, char *argv[]) {
|
||||
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
|
||||
_cleanup_set_free_free_ Set *seen = NULL;
|
||||
char **directory;
|
||||
|
||||
/* We fork this all off from a child process so that we can
|
||||
* somewhat cleanly make use of SIGALRM to set a time limit */
|
||||
|
||||
(void) reset_all_signal_handlers();
|
||||
(void) reset_signal_mask();
|
||||
|
||||
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
||||
|
||||
pids = hashmap_new(NULL);
|
||||
if (!pids)
|
||||
return log_oom();
|
||||
|
||||
seen = set_new(&string_hash_ops);
|
||||
if (!seen)
|
||||
return log_oom();
|
||||
|
||||
STRV_FOREACH(directory, directories) {
|
||||
_cleanup_closedir_ DIR *d;
|
||||
struct dirent *de;
|
||||
|
||||
d = opendir(*directory);
|
||||
if (!d) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
|
||||
}
|
||||
|
||||
FOREACH_DIRENT(de, d, break) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
if (!dirent_is_file(de))
|
||||
continue;
|
||||
|
||||
if (set_contains(seen, de->d_name)) {
|
||||
log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = set_put_strdup(seen, de->d_name);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
path = strjoin(*directory, "/", de->d_name);
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
if (null_or_empty_path(path)) {
|
||||
log_debug("%s is empty (a mask).", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
log_error_errno(errno, "Failed to fork: %m");
|
||||
continue;
|
||||
} else if (pid == 0) {
|
||||
char *_argv[2];
|
||||
|
||||
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
||||
|
||||
if (!argv) {
|
||||
_argv[0] = path;
|
||||
_argv[1] = NULL;
|
||||
argv = _argv;
|
||||
} else
|
||||
argv[0] = path;
|
||||
|
||||
execv(path, argv);
|
||||
return log_error_errno(errno, "Failed to execute %s: %m", path);
|
||||
}
|
||||
|
||||
log_debug("Spawned %s as " PID_FMT ".", path, pid);
|
||||
|
||||
r = hashmap_put(pids, PID_TO_PTR(pid), path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
path = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Abort execution of this process after the timout. We simply
|
||||
* rely on SIGALRM as default action terminating the process,
|
||||
* and turn on alarm(). */
|
||||
|
||||
if (timeout != USEC_INFINITY)
|
||||
alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
|
||||
|
||||
while (!hashmap_isempty(pids)) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
pid_t pid;
|
||||
|
||||
pid = PTR_TO_PID(hashmap_first_key(pids));
|
||||
assert(pid > 0);
|
||||
|
||||
path = hashmap_remove(pids, PID_TO_PTR(pid));
|
||||
assert(path);
|
||||
|
||||
wait_for_terminate_and_warn(path, pid, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
|
||||
pid_t executor_pid;
|
||||
int r;
|
||||
char *name;
|
||||
char **dirs = (char**) directories;
|
||||
|
||||
assert(!strv_isempty(dirs));
|
||||
|
||||
name = basename(dirs[0]);
|
||||
assert(!isempty(name));
|
||||
|
||||
/* Executes all binaries in the directories in parallel and waits
|
||||
* for them to finish. Optionally a timeout is applied. If a file
|
||||
* with the same name exists in more than one directory, the
|
||||
* earliest one wins. */
|
||||
|
||||
executor_pid = fork();
|
||||
if (executor_pid < 0) {
|
||||
log_error_errno(errno, "Failed to fork: %m");
|
||||
return;
|
||||
|
||||
} else if (executor_pid == 0) {
|
||||
r = do_execute(dirs, timeout, argv);
|
||||
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
wait_for_terminate_and_warn(name, executor_pid, true);
|
||||
}
|
||||
|
||||
bool plymouth_running(void) {
|
||||
return access("/run/plymouth/pid", F_OK) >= 0;
|
||||
}
|
||||
|
@ -65,8 +65,6 @@ static inline const char* enable_disable(bool b) {
|
||||
return b ? "enable" : "disable";
|
||||
}
|
||||
|
||||
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
|
||||
|
||||
bool plymouth_running(void);
|
||||
|
||||
bool display_is_local(const char *display) _pure_;
|
||||
|
@ -31,6 +31,8 @@
|
||||
%_binfmtdir @binfmtdir@
|
||||
%_systemdgeneratordir @systemgeneratordir@
|
||||
%_systemdusergeneratordir @usergeneratordir@
|
||||
%_systemd_system_env_generator_dir @systemenvgeneratordir@
|
||||
%_systemd_user_env_generator_dir @userenvgeneratordir@
|
||||
|
||||
%systemd_requires \
|
||||
Requires(post): systemd \
|
||||
|
@ -1830,8 +1830,10 @@ int main(int argc, char *argv[]) {
|
||||
before_startup = now(CLOCK_MONOTONIC);
|
||||
|
||||
r = manager_startup(m, arg_serialization, fds);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to fully start up daemon: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* This will close all file descriptors that were opened, but
|
||||
* not claimed by any unit. */
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "dirent-util.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "exec-util.h"
|
||||
#include "exit-status.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -102,6 +103,7 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
|
||||
static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
|
||||
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
|
||||
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
|
||||
static int manager_run_environment_generators(Manager *m);
|
||||
static int manager_run_generators(Manager *m);
|
||||
|
||||
static void manager_watch_jobs_in_progress(Manager *m) {
|
||||
@ -530,9 +532,9 @@ static int manager_default_environment(Manager *m) {
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
/* The system manager always starts with a clean
|
||||
* environment for its children. It does not import
|
||||
* the kernel or the parents exported variables.
|
||||
* the kernel's or the parents' exported variables.
|
||||
*
|
||||
* The initial passed environ is untouched to keep
|
||||
* The initial passed environment is untouched to keep
|
||||
* /proc/self/environ valid; it is used for tagging
|
||||
* the init process inside containers. */
|
||||
m->environment = strv_new("PATH=" DEFAULT_PATH,
|
||||
@ -540,11 +542,10 @@ static int manager_default_environment(Manager *m) {
|
||||
|
||||
/* Import locale variables LC_*= from configuration */
|
||||
locale_setup(&m->environment);
|
||||
} else {
|
||||
} else
|
||||
/* The user manager passes its own environment
|
||||
* along to its children. */
|
||||
m->environment = strv_copy(environ);
|
||||
}
|
||||
|
||||
if (!m->environment)
|
||||
return -ENOMEM;
|
||||
@ -1262,6 +1263,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_run_environment_generators(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Make sure the transient directory always exists, so that it remains in the search path */
|
||||
if (!m->test_run) {
|
||||
r = mkdir_p_label(m->lookup_paths.transient, 0755);
|
||||
@ -2437,22 +2442,14 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
|
||||
}
|
||||
|
||||
int manager_open_serialization(Manager *m, FILE **_f) {
|
||||
int fd = -1;
|
||||
int fd;
|
||||
FILE *f;
|
||||
|
||||
assert(_f);
|
||||
|
||||
fd = memfd_create("systemd-serialization", MFD_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
const char *path;
|
||||
|
||||
path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
|
||||
fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
log_debug("Serializing state to %s.", path);
|
||||
} else
|
||||
log_debug("Serializing state to memfd.");
|
||||
fd = open_serialization_fd("systemd-state");
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
f = fdopen(fd, "w+");
|
||||
if (!f) {
|
||||
@ -2461,7 +2458,6 @@ int manager_open_serialization(Manager *m, FILE **_f) {
|
||||
}
|
||||
|
||||
*_f = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2469,7 +2465,6 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
|
||||
Iterator i;
|
||||
Unit *u;
|
||||
const char *t;
|
||||
char **e;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -2499,17 +2494,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
|
||||
dual_timestamp_serialize(f, "units-load-finish-timestamp", &m->units_load_finish_timestamp);
|
||||
}
|
||||
|
||||
if (!switching_root) {
|
||||
STRV_FOREACH(e, m->environment) {
|
||||
_cleanup_free_ char *ce;
|
||||
|
||||
ce = cescape(*e);
|
||||
if (!ce)
|
||||
return -ENOMEM;
|
||||
|
||||
fprintf(f, "env=%s\n", *e);
|
||||
}
|
||||
}
|
||||
if (!switching_root)
|
||||
(void) serialize_environment(f, m->environment);
|
||||
|
||||
if (m->notify_fd >= 0) {
|
||||
int copy;
|
||||
@ -2672,21 +2658,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
|
||||
else if ((val = startswith(l, "units-load-finish-timestamp=")))
|
||||
dual_timestamp_deserialize(val, &m->units_load_finish_timestamp);
|
||||
else if (startswith(l, "env=")) {
|
||||
_cleanup_free_ char *uce = NULL;
|
||||
char **e;
|
||||
|
||||
r = cunescape(l + 4, UNESCAPE_RELAX, &uce);
|
||||
r = deserialize_environment(&m->environment, l);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
e = strv_env_set(m->environment, uce);
|
||||
if (!e) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
strv_free(m->environment);
|
||||
m->environment = e;
|
||||
return r;
|
||||
|
||||
} else if ((val = startswith(l, "notify-fd="))) {
|
||||
int fd;
|
||||
@ -2827,6 +2801,10 @@ int manager_reload(Manager *m) {
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = manager_run_environment_generators(m);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
/* Find new unit paths */
|
||||
q = manager_run_generators(m);
|
||||
if (q < 0 && r >= 0)
|
||||
@ -3018,10 +2996,56 @@ void manager_check_finished(Manager *m) {
|
||||
manager_invalidate_startup_units(m);
|
||||
}
|
||||
|
||||
static bool generator_path_any(const char* const* paths) {
|
||||
char **path;
|
||||
bool found = false;
|
||||
|
||||
/* Optimize by skipping the whole process by not creating output directories
|
||||
* if no generators are found. */
|
||||
STRV_FOREACH(path, (char**) paths)
|
||||
if (access(*path, F_OK) == 0)
|
||||
found = true;
|
||||
else if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static const char* system_env_generator_binary_paths[] = {
|
||||
"/run/systemd/system-environment-generators",
|
||||
"/etc/systemd/system-environment-generators",
|
||||
"/usr/local/lib/systemd/system-environment-generators",
|
||||
SYSTEM_ENV_GENERATOR_PATH,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char* user_env_generator_binary_paths[] = {
|
||||
"/run/systemd/user-environment-generators",
|
||||
"/etc/systemd/user-environment-generators",
|
||||
"/usr/local/lib/systemd/user-environment-generators",
|
||||
USER_ENV_GENERATOR_PATH,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int manager_run_environment_generators(Manager *m) {
|
||||
char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
|
||||
const char **paths;
|
||||
void* args[] = {&tmp, &tmp, &m->environment};
|
||||
|
||||
if (m->test_run)
|
||||
return 0;
|
||||
|
||||
paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths;
|
||||
|
||||
if (!generator_path_any(paths))
|
||||
return 0;
|
||||
|
||||
return execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
|
||||
}
|
||||
|
||||
static int manager_run_generators(Manager *m) {
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
const char *argv[5];
|
||||
char **path;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -3033,18 +3057,9 @@ static int manager_run_generators(Manager *m) {
|
||||
if (!paths)
|
||||
return log_oom();
|
||||
|
||||
/* Optimize by skipping the whole process by not creating output directories
|
||||
* if no generators are found. */
|
||||
STRV_FOREACH(path, paths) {
|
||||
if (access(*path, F_OK) >= 0)
|
||||
goto found;
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
|
||||
}
|
||||
if (!generator_path_any((const char* const*) paths))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
|
||||
found:
|
||||
r = lookup_paths_mkdir_generator(&m->lookup_paths);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
@ -3056,7 +3071,8 @@ static int manager_run_generators(Manager *m) {
|
||||
argv[4] = NULL;
|
||||
|
||||
RUN_WITH_UMASK(0022)
|
||||
execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
|
||||
execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC,
|
||||
NULL, NULL, (char**) argv);
|
||||
|
||||
finish:
|
||||
lookup_paths_trim_generator(&m->lookup_paths);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "def.h"
|
||||
#include "exec-util.h"
|
||||
#include "fileio.h"
|
||||
#include "killall.h"
|
||||
#include "log.h"
|
||||
@ -321,7 +322,7 @@ int main(int argc, char *argv[]) {
|
||||
arguments[0] = NULL;
|
||||
arguments[1] = arg_verb;
|
||||
arguments[2] = NULL;
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
|
||||
|
||||
if (!in_container && !in_initrd() &&
|
||||
access("/run/initramfs/shutdown", X_OK) == 0) {
|
||||
|
1
src/environment-d-generator/Makefile
Symbolic link
1
src/environment-d-generator/Makefile
Symbolic link
@ -0,0 +1 @@
|
||||
../Makefile
|
107
src/environment-d-generator/environment-d-generator.c
Normal file
107
src/environment-d-generator/environment-d-generator.c
Normal file
@ -0,0 +1,107 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "sd-path.h"
|
||||
|
||||
#include "conf-files.h"
|
||||
#include "def.h"
|
||||
#include "escape.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "path-lookup.h"
|
||||
|
||||
static int environment_dirs(char ***ret) {
|
||||
_cleanup_strv_free_ char **dirs = NULL;
|
||||
_cleanup_free_ char *c = NULL;
|
||||
int r;
|
||||
|
||||
dirs = strv_split_nulstr(CONF_PATHS_NULSTR("environment.d"));
|
||||
if (!dirs)
|
||||
return -ENOMEM;
|
||||
|
||||
/* ~/.config/systemd/environment.d */
|
||||
r = sd_path_home(SD_PATH_USER_CONFIGURATION, "environment.d", &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_front(&dirs, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = dirs;
|
||||
dirs = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_and_print(void) {
|
||||
_cleanup_strv_free_ char **dirs = NULL, **files = NULL, **env = NULL;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
r = environment_dirs(&dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = conf_files_list_strv(&files, ".conf", NULL, (const char **) dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* This will mutate the existing environment, based on the presumption
|
||||
* that in case of failure, a partial update is better than none. */
|
||||
|
||||
STRV_FOREACH(i, files) {
|
||||
r = merge_env_file(&env, NULL, *i);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
}
|
||||
|
||||
STRV_FOREACH(i, env) {
|
||||
char *t;
|
||||
_cleanup_free_ char *q = NULL;
|
||||
|
||||
t = strchr(*i, '=');
|
||||
assert(t);
|
||||
|
||||
q = shell_maybe_quote(t + 1);
|
||||
if (!q)
|
||||
return log_oom();
|
||||
|
||||
printf("%.*s=%s\n", (int) (t - *i), *i, q);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
if (argc > 1) {
|
||||
log_error("This program takes no arguments.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = load_and_print();
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to load environment.d: %m");
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
@ -520,8 +520,7 @@ int lookup_paths_init(
|
||||
append = true;
|
||||
}
|
||||
|
||||
/* FIXME: empty components in other places should be
|
||||
* rejected. */
|
||||
/* FIXME: empty components in other places should be rejected. */
|
||||
|
||||
r = path_split_and_make_absolute(e, &paths);
|
||||
if (r < 0)
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "sd-messages.h"
|
||||
|
||||
#include "def.h"
|
||||
#include "exec-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
@ -106,7 +107,7 @@ static int execute(char **modes, char **states) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
|
||||
|
||||
log_struct(LOG_INFO,
|
||||
"MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
|
||||
@ -125,7 +126,7 @@ static int execute(char **modes, char **states) {
|
||||
NULL);
|
||||
|
||||
arguments[1] = (char*) "post";
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -47,13 +47,16 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) {
|
||||
|
||||
static void test_conf_files_list(bool use_root) {
|
||||
char tmp_dir[] = "/tmp/test-conf-files-XXXXXX";
|
||||
_cleanup_strv_free_ char **found_files = NULL;
|
||||
const char *root_dir, *search_1, *search_2, *expect_a, *expect_b;
|
||||
_cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL;
|
||||
const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c;
|
||||
|
||||
log_debug("/* %s */", __func__);
|
||||
|
||||
setup_test_dir(tmp_dir,
|
||||
"/dir1/a.conf",
|
||||
"/dir2/a.conf",
|
||||
"/dir2/b.conf",
|
||||
"/dir2/c.foo",
|
||||
NULL);
|
||||
|
||||
if (use_root) {
|
||||
@ -68,6 +71,9 @@ static void test_conf_files_list(bool use_root) {
|
||||
|
||||
expect_a = strjoina(tmp_dir, "/dir1/a.conf");
|
||||
expect_b = strjoina(tmp_dir, "/dir2/b.conf");
|
||||
expect_c = strjoina(tmp_dir, "/dir2/c.foo");
|
||||
|
||||
log_debug("/* Check when filtered by suffix */");
|
||||
|
||||
assert_se(conf_files_list(&found_files, ".conf", root_dir, search_1, search_2, NULL) == 0);
|
||||
strv_print(found_files);
|
||||
@ -77,10 +83,24 @@ static void test_conf_files_list(bool use_root) {
|
||||
assert_se(streq_ptr(found_files[1], expect_b));
|
||||
assert_se(found_files[2] == NULL);
|
||||
|
||||
log_debug("/* Check when unfiltered */");
|
||||
assert_se(conf_files_list(&found_files2, NULL, root_dir, search_1, search_2, NULL) == 0);
|
||||
strv_print(found_files2);
|
||||
|
||||
assert_se(found_files2);
|
||||
assert_se(streq_ptr(found_files2[0], expect_a));
|
||||
assert_se(streq_ptr(found_files2[1], expect_b));
|
||||
assert_se(streq_ptr(found_files2[2], expect_c));
|
||||
assert_se(found_files2[3] == NULL);
|
||||
|
||||
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
test_conf_files_list(false);
|
||||
test_conf_files_list(true);
|
||||
return 0;
|
||||
|
@ -45,6 +45,16 @@ static void test_strv_env_delete(void) {
|
||||
assert_se(strv_length(d) == 2);
|
||||
}
|
||||
|
||||
static void test_strv_env_get(void) {
|
||||
char **l;
|
||||
|
||||
l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4");
|
||||
|
||||
assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2"));
|
||||
assert_se(streq(strv_env_get(l, "THREE"), "3"));
|
||||
assert_se(streq(strv_env_get(l, "FOUR"), "4"));
|
||||
}
|
||||
|
||||
static void test_strv_env_unset(void) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
@ -102,7 +112,90 @@ static void test_strv_env_merge(void) {
|
||||
assert_se(strv_length(r) == 5);
|
||||
}
|
||||
|
||||
static void test_replace_env_arg(void) {
|
||||
static void test_env_strv_get_n(void) {
|
||||
const char *_env[] = {
|
||||
"FOO=NO NO NO",
|
||||
"FOO=BAR BAR",
|
||||
"BAR=waldo",
|
||||
"PATH=unset",
|
||||
NULL
|
||||
};
|
||||
char **env = (char**) _env;
|
||||
|
||||
assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR"));
|
||||
assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
|
||||
assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR"));
|
||||
assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
|
||||
|
||||
assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset"));
|
||||
assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset"));
|
||||
assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
|
||||
assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
|
||||
|
||||
env[3] = NULL; /* kill our $PATH */
|
||||
|
||||
assert_se(!strv_env_get_n(env, "PATH__", 4, 0));
|
||||
assert_se(!strv_env_get_n(env, "PATH", 4, 0));
|
||||
assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT),
|
||||
getenv("PATH")));
|
||||
assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT),
|
||||
getenv("PATH")));
|
||||
}
|
||||
|
||||
static void test_replace_env(bool braceless) {
|
||||
const char *env[] = {
|
||||
"FOO=BAR BAR",
|
||||
"BAR=waldo",
|
||||
NULL
|
||||
};
|
||||
_cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL;
|
||||
unsigned flags = REPLACE_ENV_ALLOW_BRACELESS*braceless;
|
||||
|
||||
t = replace_env("FOO=$FOO=${FOO}", (char**) env, flags);
|
||||
assert_se(streq(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR"));
|
||||
|
||||
s = replace_env("BAR=$BAR=${BAR}", (char**) env, flags);
|
||||
assert_se(streq(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo"));
|
||||
|
||||
q = replace_env("BARBAR=$BARBAR=${BARBAR}", (char**) env, flags);
|
||||
assert_se(streq(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR="));
|
||||
|
||||
q = replace_env("BAR=$BAR$BAR${BAR}${BAR}", (char**) env, flags);
|
||||
assert_se(streq(q, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo"));
|
||||
|
||||
p = replace_env("${BAR}$BAR$BAR", (char**) env, flags);
|
||||
assert_se(streq(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR"));
|
||||
}
|
||||
|
||||
static void test_replace_env2(bool extended) {
|
||||
const char *env[] = {
|
||||
"FOO=foo",
|
||||
"BAR=bar",
|
||||
NULL
|
||||
};
|
||||
_cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL, *x = NULL;
|
||||
unsigned flags = REPLACE_ENV_ALLOW_EXTENDED*extended;
|
||||
|
||||
t = replace_env("FOO=${FOO:-${BAR}}", (char**) env, flags);
|
||||
assert_se(streq(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}"));
|
||||
|
||||
s = replace_env("BAR=${XXX:-${BAR}}", (char**) env, flags);
|
||||
assert_se(streq(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}"));
|
||||
|
||||
q = replace_env("XXX=${XXX:+${BAR}}", (char**) env, flags);
|
||||
assert_se(streq(q, extended ? "XXX=" : "XXX=${XXX:+bar}"));
|
||||
|
||||
r = replace_env("FOO=${FOO:+${BAR}}", (char**) env, flags);
|
||||
assert_se(streq(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}"));
|
||||
|
||||
p = replace_env("FOO=${FOO:-${BAR}post}", (char**) env, flags);
|
||||
assert_se(streq(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}"));
|
||||
|
||||
x = replace_env("XXX=${XXX:+${BAR}post}", (char**) env, flags);
|
||||
assert_se(streq(x, extended ? "XXX=" : "XXX=${XXX:+barpost}"));
|
||||
}
|
||||
|
||||
static void test_replace_env_argv(void) {
|
||||
const char *env[] = {
|
||||
"FOO=BAR BAR",
|
||||
"BAR=waldo",
|
||||
@ -120,6 +213,12 @@ static void test_replace_env_arg(void) {
|
||||
"${FOO",
|
||||
"FOO$$${FOO}",
|
||||
"$$FOO${FOO}",
|
||||
"${FOO:-${BAR}}",
|
||||
"${QUUX:-${FOO}}",
|
||||
"${FOO:+${BAR}}",
|
||||
"${QUUX:+${BAR}}",
|
||||
"${FOO:+|${BAR}|}}",
|
||||
"${FOO:+|${BAR}{|}",
|
||||
NULL
|
||||
};
|
||||
_cleanup_strv_free_ char **r = NULL;
|
||||
@ -137,7 +236,13 @@ static void test_replace_env_arg(void) {
|
||||
assert_se(streq(r[8], "${FOO"));
|
||||
assert_se(streq(r[9], "FOO$BAR BAR"));
|
||||
assert_se(streq(r[10], "$FOOBAR BAR"));
|
||||
assert_se(strv_length(r) == 11);
|
||||
assert_se(streq(r[11], "${FOO:-waldo}"));
|
||||
assert_se(streq(r[12], "${QUUX:-BAR BAR}"));
|
||||
assert_se(streq(r[13], "${FOO:+waldo}"));
|
||||
assert_se(streq(r[14], "${QUUX:+waldo}"));
|
||||
assert_se(streq(r[15], "${FOO:+|waldo|}}"));
|
||||
assert_se(streq(r[16], "${FOO:+|waldo{|}"));
|
||||
assert_se(strv_length(r) == 17);
|
||||
}
|
||||
|
||||
static void test_env_clean(void) {
|
||||
@ -211,10 +316,16 @@ static void test_env_assignment_is_valid(void) {
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_strv_env_delete();
|
||||
test_strv_env_get();
|
||||
test_strv_env_unset();
|
||||
test_strv_env_set();
|
||||
test_strv_env_merge();
|
||||
test_replace_env_arg();
|
||||
test_env_strv_get_n();
|
||||
test_replace_env(false);
|
||||
test_replace_env(true);
|
||||
test_replace_env2(false);
|
||||
test_replace_env2(true);
|
||||
test_replace_env_argv();
|
||||
test_env_clean();
|
||||
test_env_name_is_valid();
|
||||
test_env_value_is_valid();
|
||||
|
348
src/test/test-exec-util.c
Normal file
348
src/test/test-exec-util.c
Normal file
@ -0,0 +1,348 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
Copyright 2013 Thomas H.P. Andersen
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "copy.h"
|
||||
#include "def.h"
|
||||
#include "env-util.h"
|
||||
#include "exec-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "rm-rf.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
static int here = 0, here2 = 0, here3 = 0;
|
||||
void *ignore_stdout_args[] = {&here, &here2, &here3};
|
||||
|
||||
/* noop handlers, just check that arguments are passed correctly */
|
||||
static int ignore_stdout_func(int fd, void *arg) {
|
||||
assert(fd >= 0);
|
||||
assert(arg == &here);
|
||||
safe_close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int ignore_stdout_func2(int fd, void *arg) {
|
||||
assert(fd >= 0);
|
||||
assert(arg == &here2);
|
||||
safe_close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int ignore_stdout_func3(int fd, void *arg) {
|
||||
assert(fd >= 0);
|
||||
assert(arg == &here3);
|
||||
safe_close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const gather_stdout_callback_t ignore_stdout[] = {
|
||||
ignore_stdout_func,
|
||||
ignore_stdout_func2,
|
||||
ignore_stdout_func3,
|
||||
};
|
||||
|
||||
static void test_execute_directory(bool gather_stdout) {
|
||||
char template_lo[] = "/tmp/test-exec-util.XXXXXXX";
|
||||
char template_hi[] = "/tmp/test-exec-util.XXXXXXX";
|
||||
const char * dirs[] = {template_hi, template_lo, NULL};
|
||||
const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
|
||||
|
||||
log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
|
||||
|
||||
assert_se(mkdtemp(template_lo));
|
||||
assert_se(mkdtemp(template_hi));
|
||||
|
||||
name = strjoina(template_lo, "/script");
|
||||
name2 = strjoina(template_hi, "/script2");
|
||||
name3 = strjoina(template_lo, "/useless");
|
||||
overridden = strjoina(template_lo, "/overridden");
|
||||
override = strjoina(template_hi, "/overridden");
|
||||
masked = strjoina(template_lo, "/masked");
|
||||
mask = strjoina(template_hi, "/masked");
|
||||
|
||||
assert_se(write_string_file(name,
|
||||
"#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(name2,
|
||||
"#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(overridden,
|
||||
"#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(override,
|
||||
"#!/bin/sh\necho 'Executing '$0",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(masked,
|
||||
"#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(symlink("/dev/null", mask) == 0);
|
||||
assert_se(touch(name3) >= 0);
|
||||
|
||||
assert_se(chmod(name, 0755) == 0);
|
||||
assert_se(chmod(name2, 0755) == 0);
|
||||
assert_se(chmod(overridden, 0755) == 0);
|
||||
assert_se(chmod(override, 0755) == 0);
|
||||
assert_se(chmod(masked, 0755) == 0);
|
||||
|
||||
if (gather_stdout)
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
|
||||
else
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
|
||||
|
||||
assert_se(chdir(template_lo) == 0);
|
||||
assert_se(access("it_works", F_OK) >= 0);
|
||||
assert_se(access("failed", F_OK) < 0);
|
||||
|
||||
assert_se(chdir(template_hi) == 0);
|
||||
assert_se(access("it_works2", F_OK) >= 0);
|
||||
assert_se(access("failed", F_OK) < 0);
|
||||
|
||||
(void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
(void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
}
|
||||
|
||||
static void test_execution_order(void) {
|
||||
char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX";
|
||||
char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX";
|
||||
const char *dirs[] = {template_hi, template_lo, NULL};
|
||||
const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
|
||||
const char *output, *t;
|
||||
_cleanup_free_ char *contents = NULL;
|
||||
|
||||
assert_se(mkdtemp(template_lo));
|
||||
assert_se(mkdtemp(template_hi));
|
||||
|
||||
output = strjoina(template_hi, "/output");
|
||||
|
||||
log_info("/* %s >>%s */", __func__, output);
|
||||
|
||||
/* write files in "random" order */
|
||||
name2 = strjoina(template_lo, "/90-bar");
|
||||
name = strjoina(template_hi, "/80-foo");
|
||||
name3 = strjoina(template_lo, "/last");
|
||||
overridden = strjoina(template_lo, "/30-override");
|
||||
override = strjoina(template_hi, "/30-override");
|
||||
masked = strjoina(template_lo, "/10-masked");
|
||||
mask = strjoina(template_hi, "/10-masked");
|
||||
|
||||
t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
|
||||
assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
|
||||
assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
|
||||
assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
|
||||
assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
|
||||
assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
t = strjoina("#!/bin/sh\necho MASKED >>", output);
|
||||
assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
assert_se(symlink("/dev/null", mask) == 0);
|
||||
|
||||
assert_se(chmod(name, 0755) == 0);
|
||||
assert_se(chmod(name2, 0755) == 0);
|
||||
assert_se(chmod(name3, 0755) == 0);
|
||||
assert_se(chmod(overridden, 0755) == 0);
|
||||
assert_se(chmod(override, 0755) == 0);
|
||||
assert_se(chmod(masked, 0755) == 0);
|
||||
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
|
||||
|
||||
assert_se(read_full_file(output, &contents, NULL) >= 0);
|
||||
assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
|
||||
|
||||
(void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
(void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
}
|
||||
|
||||
static int gather_stdout_one(int fd, void *arg) {
|
||||
char ***s = arg, *t;
|
||||
char buf[128] = {};
|
||||
|
||||
assert_se(s);
|
||||
assert_se(read(fd, buf, sizeof buf) >= 0);
|
||||
safe_close(fd);
|
||||
|
||||
assert_se(t = strndup(buf, sizeof buf));
|
||||
assert_se(strv_push(s, t) >= 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int gather_stdout_two(int fd, void *arg) {
|
||||
char ***s = arg, **t;
|
||||
|
||||
STRV_FOREACH(t, *s)
|
||||
assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
|
||||
safe_close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int gather_stdout_three(int fd, void *arg) {
|
||||
char **s = arg;
|
||||
char buf[128] = {};
|
||||
|
||||
assert_se(read(fd, buf, sizeof buf - 1) > 0);
|
||||
safe_close(fd);
|
||||
assert_se(*s = strndup(buf, sizeof buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const gather_stdout_callback_t const gather_stdout[] = {
|
||||
gather_stdout_one,
|
||||
gather_stdout_two,
|
||||
gather_stdout_three,
|
||||
};
|
||||
|
||||
|
||||
static void test_stdout_gathering(void) {
|
||||
char template[] = "/tmp/test-exec-util.XXXXXXX";
|
||||
const char *dirs[] = {template, NULL};
|
||||
const char *name, *name2, *name3;
|
||||
int r;
|
||||
|
||||
char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
|
||||
_cleanup_free_ char *output = NULL;
|
||||
|
||||
void* args[] = {&tmp, &tmp, &output};
|
||||
|
||||
assert_se(mkdtemp(template));
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
/* write files */
|
||||
name = strjoina(template, "/10-foo");
|
||||
name2 = strjoina(template, "/20-bar");
|
||||
name3 = strjoina(template, "/30-last");
|
||||
|
||||
assert_se(write_string_file(name,
|
||||
"#!/bin/sh\necho a\necho b\necho c\n",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(name2,
|
||||
"#!/bin/sh\necho d\n",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(name3,
|
||||
"#!/bin/sh\nsleep 1",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
assert_se(chmod(name, 0755) == 0);
|
||||
assert_se(chmod(name2, 0755) == 0);
|
||||
assert_se(chmod(name3, 0755) == 0);
|
||||
|
||||
r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
|
||||
assert_se(r >= 0);
|
||||
|
||||
log_info("got: %s", output);
|
||||
|
||||
assert_se(streq(output, "a\nb\nc\nd\n"));
|
||||
}
|
||||
|
||||
static void test_environment_gathering(void) {
|
||||
char template[] = "/tmp/test-exec-util.XXXXXXX", **p;
|
||||
const char *dirs[] = {template, NULL};
|
||||
const char *name, *name2, *name3;
|
||||
int r;
|
||||
|
||||
char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
|
||||
_cleanup_strv_free_ char **env = NULL;
|
||||
|
||||
void* const args[] = { &tmp, &tmp, &env };
|
||||
|
||||
assert_se(mkdtemp(template));
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
/* write files */
|
||||
name = strjoina(template, "/10-foo");
|
||||
name2 = strjoina(template, "/20-bar");
|
||||
name3 = strjoina(template, "/30-last");
|
||||
|
||||
assert_se(write_string_file(name,
|
||||
"#!/bin/sh\n"
|
||||
"echo A=23\n",
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(name2,
|
||||
"#!/bin/sh\n"
|
||||
"echo A=22:$A\n\n\n", /* substitution from previous generator */
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(name3,
|
||||
"#!/bin/sh\n"
|
||||
"echo A=$A:24\n"
|
||||
"echo B=12\n"
|
||||
"echo C=000\n"
|
||||
"echo C=001\n" /* variable overwriting */
|
||||
/* various invalid entries */
|
||||
"echo unset A\n"
|
||||
"echo unset A=\n"
|
||||
"echo unset A=B\n"
|
||||
"echo unset \n"
|
||||
"echo A B=C\n"
|
||||
"echo A\n"
|
||||
/* test variable assignment without newline */
|
||||
"echo PATH=$PATH:/no/such/file", /* no newline */
|
||||
WRITE_STRING_FILE_CREATE) == 0);
|
||||
|
||||
assert_se(chmod(name, 0755) == 0);
|
||||
assert_se(chmod(name2, 0755) == 0);
|
||||
assert_se(chmod(name3, 0755) == 0);
|
||||
|
||||
r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
|
||||
assert_se(r >= 0);
|
||||
|
||||
STRV_FOREACH(p, env)
|
||||
log_info("got env: \"%s\"", *p);
|
||||
|
||||
assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
|
||||
assert_se(streq(strv_env_get(env, "B"), "12"));
|
||||
assert_se(streq(strv_env_get(env, "C"), "001"));
|
||||
assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file"));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
test_execute_directory(true);
|
||||
test_execute_directory(false);
|
||||
test_execution_order();
|
||||
test_stdout_gathering();
|
||||
test_environment_gathering();
|
||||
|
||||
return 0;
|
||||
}
|
@ -94,10 +94,20 @@ static void test_same_fd(void) {
|
||||
assert_se(same_fd(b, a) == 0);
|
||||
}
|
||||
|
||||
static void test_open_serialization_fd(void) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
fd = open_serialization_fd("test");
|
||||
assert_se(fd >= 0);
|
||||
|
||||
write(fd, "test\n", 5);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_close_many();
|
||||
test_close_nointr();
|
||||
test_same_fd();
|
||||
test_open_serialization_fd();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -71,6 +71,8 @@ static void test_parse_env_file(void) {
|
||||
"seven=\"sevenval\" #nocomment\n"
|
||||
"eight=eightval #nocomment\n"
|
||||
"export nine=nineval\n"
|
||||
"ten=ignored\n"
|
||||
"ten=ignored\n"
|
||||
"ten=", f);
|
||||
|
||||
fflush(f);
|
||||
@ -204,6 +206,113 @@ static void test_parse_multiline_env_file(void) {
|
||||
unlink(p);
|
||||
}
|
||||
|
||||
static void test_merge_env_file(void) {
|
||||
char t[] = "/tmp/test-fileio-XXXXXX";
|
||||
int fd, r;
|
||||
FILE *f;
|
||||
_cleanup_strv_free_ char **a = NULL;
|
||||
char **i;
|
||||
|
||||
fd = mkostemp_safe(t);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
log_info("/* %s (%s) */", __func__, t);
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
assert_se(f);
|
||||
|
||||
r = write_string_stream(f,
|
||||
"one=1 \n"
|
||||
"twelve=${one}2\n"
|
||||
"twentyone=2${one}\n"
|
||||
"one=2\n"
|
||||
"twentytwo=2${one}\n"
|
||||
"xxx_minus_three=$xxx - 3\n"
|
||||
"xxx=0x$one$one$one\n"
|
||||
"yyy=${one:-fallback}\n"
|
||||
"zzz=${one:+replacement}\n"
|
||||
"zzzz=${foobar:-${nothing}}\n"
|
||||
"zzzzz=${nothing:+${nothing}}\n"
|
||||
, false);
|
||||
assert(r >= 0);
|
||||
|
||||
r = merge_env_file(&a, NULL, t);
|
||||
assert_se(r >= 0);
|
||||
strv_sort(a);
|
||||
|
||||
STRV_FOREACH(i, a)
|
||||
log_info("Got: <%s>", *i);
|
||||
|
||||
assert_se(streq(a[0], "one=2"));
|
||||
assert_se(streq(a[1], "twelve=12"));
|
||||
assert_se(streq(a[2], "twentyone=21"));
|
||||
assert_se(streq(a[3], "twentytwo=22"));
|
||||
assert_se(streq(a[4], "xxx=0x222"));
|
||||
assert_se(streq(a[5], "xxx_minus_three= - 3"));
|
||||
assert_se(streq(a[6], "yyy=2"));
|
||||
assert_se(streq(a[7], "zzz=replacement"));
|
||||
assert_se(streq(a[8], "zzzz="));
|
||||
assert_se(streq(a[9], "zzzzz="));
|
||||
assert_se(a[10] == NULL);
|
||||
|
||||
r = merge_env_file(&a, NULL, t);
|
||||
assert_se(r >= 0);
|
||||
strv_sort(a);
|
||||
|
||||
STRV_FOREACH(i, a)
|
||||
log_info("Got2: <%s>", *i);
|
||||
|
||||
assert_se(streq(a[0], "one=2"));
|
||||
assert_se(streq(a[1], "twelve=12"));
|
||||
assert_se(streq(a[2], "twentyone=21"));
|
||||
assert_se(streq(a[3], "twentytwo=22"));
|
||||
assert_se(streq(a[4], "xxx=0x222"));
|
||||
assert_se(streq(a[5], "xxx_minus_three=0x222 - 3"));
|
||||
assert_se(streq(a[6], "yyy=2"));
|
||||
assert_se(streq(a[7], "zzz=replacement"));
|
||||
assert_se(streq(a[8], "zzzz="));
|
||||
assert_se(streq(a[9], "zzzzz="));
|
||||
assert_se(a[10] == NULL);
|
||||
}
|
||||
|
||||
static void test_merge_env_file_invalid(void) {
|
||||
char t[] = "/tmp/test-fileio-XXXXXX";
|
||||
int fd, r;
|
||||
FILE *f;
|
||||
_cleanup_strv_free_ char **a = NULL;
|
||||
char **i;
|
||||
|
||||
fd = mkostemp_safe(t);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
log_info("/* %s (%s) */", __func__, t);
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
assert_se(f);
|
||||
|
||||
r = write_string_stream(f,
|
||||
"unset one \n"
|
||||
"unset one= \n"
|
||||
"unset one=1 \n"
|
||||
"one \n"
|
||||
"one = \n"
|
||||
"one two =\n"
|
||||
"\x20two=\n"
|
||||
"#comment=comment\n"
|
||||
";comment2=comment2\n"
|
||||
"#\n"
|
||||
"\n\n" /* empty line */
|
||||
, false);
|
||||
assert(r >= 0);
|
||||
|
||||
r = merge_env_file(&a, NULL, t);
|
||||
assert_se(r >= 0);
|
||||
|
||||
STRV_FOREACH(i, a)
|
||||
log_info("Got: <%s>", *i);
|
||||
|
||||
assert_se(strv_isempty(a));
|
||||
}
|
||||
|
||||
static void test_executable_is_script(void) {
|
||||
char t[] = "/tmp/test-executable-XXXXXX";
|
||||
@ -555,11 +664,14 @@ static void test_tempfn(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
test_parse_env_file();
|
||||
test_parse_multiline_env_file();
|
||||
test_merge_env_file();
|
||||
test_merge_env_file_invalid();
|
||||
test_executable_is_script();
|
||||
test_status_field();
|
||||
test_capeff();
|
||||
|
@ -195,50 +195,6 @@ static void test_log2i(void) {
|
||||
assert_se(log2i(INT_MAX) == sizeof(int)*8-2);
|
||||
}
|
||||
|
||||
static void test_execute_directory(void) {
|
||||
char template_lo[] = "/tmp/test-readlink_and_make_absolute-lo.XXXXXXX";
|
||||
char template_hi[] = "/tmp/test-readlink_and_make_absolute-hi.XXXXXXX";
|
||||
const char * dirs[] = {template_hi, template_lo, NULL};
|
||||
const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
|
||||
|
||||
assert_se(mkdtemp(template_lo));
|
||||
assert_se(mkdtemp(template_hi));
|
||||
|
||||
name = strjoina(template_lo, "/script");
|
||||
name2 = strjoina(template_hi, "/script2");
|
||||
name3 = strjoina(template_lo, "/useless");
|
||||
overridden = strjoina(template_lo, "/overridden");
|
||||
override = strjoina(template_hi, "/overridden");
|
||||
masked = strjoina(template_lo, "/masked");
|
||||
mask = strjoina(template_hi, "/masked");
|
||||
|
||||
assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(overridden, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(override, "#!/bin/sh\necho 'Executing '$0", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(write_string_file(masked, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(symlink("/dev/null", mask) == 0);
|
||||
assert_se(chmod(name, 0755) == 0);
|
||||
assert_se(chmod(name2, 0755) == 0);
|
||||
assert_se(chmod(overridden, 0755) == 0);
|
||||
assert_se(chmod(override, 0755) == 0);
|
||||
assert_se(chmod(masked, 0755) == 0);
|
||||
assert_se(touch(name3) >= 0);
|
||||
|
||||
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL);
|
||||
|
||||
assert_se(chdir(template_lo) == 0);
|
||||
assert_se(access("it_works", F_OK) >= 0);
|
||||
assert_se(access("failed", F_OK) < 0);
|
||||
|
||||
assert_se(chdir(template_hi) == 0);
|
||||
assert_se(access("it_works2", F_OK) >= 0);
|
||||
assert_se(access("failed", F_OK) < 0);
|
||||
|
||||
(void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
(void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
}
|
||||
|
||||
static void test_raw_clone(void) {
|
||||
pid_t parent, pid, pid2;
|
||||
|
||||
@ -359,7 +315,6 @@ int main(int argc, char *argv[]) {
|
||||
test_protect_errno();
|
||||
test_in_set();
|
||||
test_log2i();
|
||||
test_execute_directory();
|
||||
test_raw_clone();
|
||||
test_physical_memory();
|
||||
test_physical_memory_scale();
|
||||
|
Loading…
x
Reference in New Issue
Block a user