New upstream version 246.6

This commit is contained in:
Michael Biebl 2020-09-21 18:40:40 +02:00
parent 7de6915edf
commit 883b242a8a
33 changed files with 199 additions and 84 deletions

3
TODO
View File

@ -581,6 +581,9 @@ Features:
* sd-bus: add vtable flag, that may be used to request client creds implicitly
and asynchronously before dispatching the operation
* sd-bus: parse addresses given in sd_bus_set_addresses immediately and not
only when used. Add unit tests.
* make use of ethtool veth peer info in machined, for automatically finding out
host-side interface pointing to the container.

View File

@ -10,3 +10,4 @@
<!ENTITY MEMORY_ACCOUNTING_DEFAULT @MEMORY_ACCOUNTING_DEFAULT_YES_NO@>
<!ENTITY KILL_USER_PROCESSES @KILL_USER_PROCESSES_YES_NO@>
<!ENTITY DEBUGTTY @DEBUGTTY@>
<!ENTITY RC_LOCAL_PATH @RC_LOCAL_PATH@>

View File

@ -556,10 +556,13 @@
<listitem><para>Takes a file system path. Configures where to place the user's home directory. When
LUKS2 storage is used refers to the path to the loopback file, otherwise to the path to the home
directory. When unspecified defaults to <filename>/home/$USER.home</filename> when LUKS storage is
used and <filename>/home/$USER.homedir</filename> for the other storage mechanisms. Not defined for
the <literal>cifs</literal> storage mechanism. To use LUKS2 storage on a regular block device (for
example a USB stick) pass the path to the block device here.</para></listitem>
directory (which may be in <filename>/home/</filename> or any other accessible filesystem). When
unspecified defaults to <filename>/home/$USER.home</filename> when LUKS storage is used and
<filename>/home/$USER.homedir</filename> for the other storage mechanisms. Not defined for the
<literal>cifs</literal> storage mechanism. To use LUKS2 storage on a regular block device (for
example a USB stick) pass the path to the block device here. Specifying the path to a directory here
when using LUKS2 storage is not allowed. Similar, specifying the path to a regular file or device
node is not allowed if any of the other storage backends are used.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -1,8 +1,11 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
%entities;
]>
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<refentry id="systemd-rc-local-generator" conditional='HAVE_SYSV_COMPAT'>
<refentryinfo>
<title>systemd-rc-local-generator</title>
@ -16,7 +19,7 @@
<refnamediv>
<refname>systemd-rc-local-generator</refname>
<refpurpose>Compatibility generator for starting <filename>/etc/rc.local</filename> during boot</refpurpose>
<refpurpose>Compatibility generator for starting <filename>&RC_LOCAL_PATH;</filename> during boot</refpurpose>
</refnamediv>
<refsynopsisdiv>
@ -27,17 +30,17 @@
<title>Description</title>
<para><filename>systemd-rc-local-generator</filename> is a generator that checks whether
<filename>/etc/rc.local</filename> exists and is executable, and if it is pulls the
<filename>rc-local.service</filename> unit into the boot process. This unit is responsible for running this script
during late boot. Note that the script will be run with slightly different semantics than the original System V
version, which was run "last" in the boot process, which is a concept that does not translate to systemd. The
script is run after <filename>network.target</filename>, but in parallel with most other regular system
services.</para>
<filename>&RC_LOCAL_PATH;</filename> exists and is executable, and if it is pulls the
<filename>rc-local.service</filename> unit into the boot process. This unit is responsible for running
this script during late boot. Note that the script will be run with slightly different semantics than the
original System V version, which was run "last" in the boot process, which is a concept that does not
translate to systemd. The script is run after <filename>network.target</filename>, but in parallel with
most other regular system services.</para>
<para>Support for <filename>/etc/rc.local</filename> is provided
for compatibility with specific System V systems only. However, it is strongly recommended to avoid making use of
this script today, and instead provide proper unit files with appropriate dependencies for any scripts to run
during the boot process.</para>
<para>Support for <filename>&RC_LOCAL_PATH;</filename> is provided for compatibility with specific System
V systems only. However, it is strongly recommended to avoid making use of this script today, and instead
provide proper unit files with appropriate dependencies for any scripts to run during the boot process.
Note that the path to the script is set a compile time and varies between distributions.</para>
<para><filename>systemd-rc-local-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>

View File

@ -335,18 +335,14 @@
<varlistentry>
<term><varname>RemainAfterElapse=</varname></term>
<listitem><para>Takes a boolean argument. If true, an elapsed
timer will stay loaded, and its state remains queryable. If
false, an elapsed timer unit that cannot elapse anymore is
unloaded. Turning this off is particularly useful for
transient timer units that shall disappear after they first
elapse. Note that this setting has an effect on repeatedly
starting a timer unit that only elapses once: if
<varname>RemainAfterElapse=</varname> is on, it will not be
started again, and is guaranteed to elapse only once. However,
if <varname>RemainAfterElapse=</varname> is off, it might be
started again if it is already elapsed, and thus be triggered
multiple times. Defaults to
<listitem><para>Takes a boolean argument. If true, a timer will stay loaded, and its state remains
queryable even after it elapsed and the associated unit (as configured with <varname>Unit=</varname>,
see above) deactivated again. If false, an elapsed timer unit that cannot elapse anymore is unloaded
once its associated unit deactivated again. Turning this off is particularly useful for transient
timer units. Note that this setting has an effect when repeatedly starting a timer unit: if
<varname>RemainAfterElapse=</varname> is on, starting the timer a second time has no effect. However,
if <varname>RemainAfterElapse=</varname> is off and the timer unit was already unloaded, it can be
started again, and thus the service can be triggered multiple times. Defaults to
<varname>yes</varname>.</para></listitem>
</varlistentry>
</variablelist>

View File

@ -209,7 +209,7 @@ conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR', join_paths(pkgsysc
conf.set_quoted('SYSTEM_DATA_UNIT_PATH', systemunitdir)
conf.set_quoted('SYSTEM_SYSVINIT_PATH', sysvinit_path)
conf.set_quoted('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
conf.set_quoted('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-local'))
conf.set_quoted('RC_LOCAL_PATH', get_option('rc-local'))
conf.set('ANSI_OK_COLOR', 'ANSI_' + get_option('ok-color').underscorify().to_upper())
@ -296,7 +296,7 @@ substs.set('CERTIFICATEROOT', get_option('certif
substs.set('RANDOM_SEED', join_paths(randomseeddir, 'random-seed'))
substs.set('SYSTEM_SYSVINIT_PATH', sysvinit_path)
substs.set('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
substs.set('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-local'))
substs.set('RC_LOCAL_PATH', get_option('rc-local'))
substs.set('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no')
substs.set('STATUS_UNIT_FORMAT_DEFAULT', status_unit_format_default)
substs.set('HIGH_RLIMIT_NOFILE', conf.get('HIGH_RLIMIT_NOFILE'))

View File

@ -209,17 +209,21 @@ static int parse_env_file_internal(
case DOUBLE_QUOTE_VALUE_ESCAPE:
state = DOUBLE_QUOTE_VALUE;
if (c == '"') {
if (strchr(SHELL_NEED_ESCAPE, c)) {
/* If this is a char that needs escaping, just unescape it. */
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = '"';
} else if (!strchr(NEWLINE, c)) {
value[n_value++] = c;
} else if (c != '\n') {
/* If other char than what needs escaping, keep the "\" in place, like the
* real shell does. */
if (!GREEDY_REALLOC(value, value_alloc, n_value+3))
return -ENOMEM;
value[n_value++] = '\\';
value[n_value++] = c;
}
/* Escaped newlines (aka "continuation lines") are eaten up entirely */
break;
case COMMENT:

View File

@ -3,11 +3,7 @@
set -eu
for i in *.h */*.h; do
if [[ $i == 'wireguard.h' ]]; then
curl https://raw.githubusercontent.com/WireGuard/WireGuard/master/src/uapi/$i -o $i
else
curl https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/$i -o $i
fi
curl https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/$i -o $i
sed -i -e 's/__user //g' -e '/^#include <linux\/compiler.h>/ d' $i
done

View File

@ -635,6 +635,7 @@ int fd_set_sndbuf(int fd, size_t n, bool increase) {
/* SO_SNDBUF above may set to the kernel limit, instead of the requested size.
* So, we need to check the actual buffer size here. */
l = sizeof(value);
r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
return 1;
@ -665,6 +666,7 @@ int fd_set_rcvbuf(int fd, size_t n, bool increase) {
/* SO_RCVBUF above may set to the kernel limit, instead of the requested size.
* So, we need to check the actual buffer size here. */
l = sizeof(value);
r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
return 1;

View File

@ -1472,7 +1472,9 @@ static int install_random_seed(const char *esp) {
}
r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderSystemToken", NULL, NULL, &token_size);
if (r < 0) {
if (r == -ENODATA)
log_debug_errno(r, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
else if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to test system token validity: %m");
} else {

View File

@ -2396,7 +2396,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hH:M:qj", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "hH:M:qjl", options, NULL)) >= 0)
switch (c) {

View File

@ -507,8 +507,8 @@ static void automount_trigger_notify(Unit *u, Unit *other) {
assert(other);
/* Filter out invocations with bogus state */
if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT)
return;
assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
assert(other->type == UNIT_MOUNT);
/* Don't propagate state changes from the mount if we are already down */
if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING))

View File

@ -748,10 +748,23 @@ static void path_trigger_notify(Unit *u, Unit *other) {
assert(u);
assert(other);
/* Invoked whenever the unit we trigger changes state or gains
* or loses a job */
/* Invoked whenever the unit we trigger changes state or gains or loses a job */
if (other->load_state != UNIT_LOADED)
/* Filter out invocations with bogus state */
assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
/* Don't propagate state changes from the triggered unit if we are already down */
if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
return;
/* Propagate start limit hit state */
if (other->start_limit_hit) {
path_enter_dead(p, PATH_FAILURE_UNIT_START_LIMIT_HIT);
return;
}
/* Don't propagate anything if there's still a job queued */
if (other->job)
return;
if (p->state == PATH_RUNNING &&
@ -790,6 +803,7 @@ static const char* const path_result_table[_PATH_RESULT_MAX] = {
[PATH_SUCCESS] = "success",
[PATH_FAILURE_RESOURCES] = "resources",
[PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);

View File

@ -45,6 +45,7 @@ typedef enum PathResult {
PATH_SUCCESS,
PATH_FAILURE_RESOURCES,
PATH_FAILURE_START_LIMIT_HIT,
PATH_FAILURE_UNIT_START_LIMIT_HIT,
_PATH_RESULT_MAX,
_PATH_RESULT_INVALID = -1
} PathResult;

View File

@ -3284,13 +3284,8 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
assert(other);
/* Filter out invocations with bogus state */
if (!IN_SET(other->load_state,
UNIT_LOADED,
UNIT_NOT_FOUND,
UNIT_BAD_SETTING,
UNIT_ERROR,
UNIT_MASKED) || other->type != UNIT_SERVICE)
return;
assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
assert(other->type == UNIT_SERVICE);
/* Don't propagate state changes from the service if we are already down */
if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING))

View File

@ -746,8 +746,8 @@ static void timer_trigger_notify(Unit *u, Unit *other) {
assert(u);
assert(other);
if (other->load_state != UNIT_LOADED)
return;
/* Filter out invocations with bogus state */
assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
/* Reenable all timers that depend on unit state */
LIST_FOREACH(value, v, t->values)

View File

@ -949,7 +949,7 @@ int transaction_add_job_and_dependencies(
/* Safety check that the unit is a valid state, i.e. not in UNIT_STUB or UNIT_MERGED which should only be set
* temporarily. */
if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_MASKED))
if (!UNIT_IS_LOAD_COMPLETE(unit->load_state))
return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
if (type != JOB_STOP) {

View File

@ -49,6 +49,10 @@ static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) {
return IN_SET(t, UNIT_INACTIVE, UNIT_FAILED);
}
static inline bool UNIT_IS_LOAD_COMPLETE(UnitLoadState t) {
return t >= 0 && t < _UNIT_LOAD_STATE_MAX && t != UNIT_STUB && t != UNIT_MERGED;
}
/* Stores the 'reason' a dependency was created as a bit mask, i.e. due to which configuration source it came to be. We
* use this so that we can selectively flush out parts of dependencies again. Note that the same dependency might be
* created as a result of multiple "reasons", hence the bitmask. */

View File

@ -425,7 +425,7 @@ static int add_mount(
* the systemd mount-timeout doesn't interfere.
* By placing these options first, they can be over-ridden by
* settings in /etc/fstab. */
opts = strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts, ",fg");
opts = strjoina("x-systemd.mount-timeout=infinity,retry=10000,nofail,", opts, ",fg");
SET_FLAG(flags, NOFAIL, true);
}

View File

@ -1267,15 +1267,22 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
assert(h);
switch (home_get_state(h)) {
case HOME_INACTIVE:
case HOME_INACTIVE: {
int t;
if (h->record->storage < 0)
break; /* if no storage is defined we don't know what precisely to look for, hence
* HOME_INACTIVE is OK in that case too. */
if (IN_SET(user_record_test_image_path(h->record), USER_TEST_MAYBE, USER_TEST_UNDEFINED))
t = user_record_test_image_path(h->record);
if (IN_SET(t, USER_TEST_MAYBE, USER_TEST_UNDEFINED))
break; /* And if the image path test isn't conclusive, let's also go on */
_fallthrough_;
if (IN_SET(t, -EBADFD, -ENOTDIR))
return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Selected home image of user %s already exists or has wrong inode type.", h->user_name);
return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Selected home image of user %s already exists.", h->user_name);
}
case HOME_UNFIXATED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Home of user %s already exists.", h->user_name);
case HOME_ABSENT:
@ -2709,6 +2716,19 @@ int home_set_current_message(Home *h, sd_bus_message *m) {
return 1;
}
int home_wait_for_worker(Home *h) {
assert(h);
if (h->worker_pid <= 0)
return 0;
log_info("Worker process for home %s is still running while exiting. Waiting for it to finish.", h->user_name);
(void) wait_for_terminate(h->worker_pid, NULL);
(void) hashmap_remove_value(h->manager->homes_by_worker_pid, PID_TO_PTR(h->worker_pid), h);
h->worker_pid = 0;
return 1;
}
static const char* const home_state_table[_HOME_STATE_MAX] = {
[HOME_UNFIXATED] = "unfixated",
[HOME_ABSENT] = "absent",

View File

@ -164,5 +164,7 @@ int home_auto_login(Home *h, char ***ret_seats);
int home_set_current_message(Home *h, sd_bus_message *m);
int home_wait_for_worker(Home *h);
const char *home_state_to_string(HomeState state);
HomeState home_state_from_string(const char *s);

View File

@ -232,8 +232,14 @@ int manager_new(Manager **ret) {
}
Manager* manager_free(Manager *m) {
Home *h;
Iterator i;
assert(m);
HASHMAP_FOREACH(h, m->homes_by_worker_pid, i)
(void) home_wait_for_worker(h);
hashmap_free(m->homes_by_uid);
hashmap_free(m->homes_by_name);
hashmap_free(m->homes_by_worker_pid);

View File

@ -18,8 +18,9 @@ fi
MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID
ENTRY_DIR="/$MACHINE_ID/$KERNEL_VERSION"
BOOT_ROOT=${ENTRY_DIR_ABS%$ENTRY_DIR}
BOOT_ROOT=${ENTRY_DIR_ABS%/$MACHINE_ID/$KERNEL_VERSION}
BOOT_MNT=$(stat -c %m $BOOT_ROOT)
ENTRY_DIR=/${ENTRY_DIR_ABS#$BOOT_MNT}
if [[ $COMMAND == remove ]]; then
rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"

View File

@ -783,7 +783,6 @@ static int parse_tcp_address(sd_bus *b, const char **p, char **guid) {
int r;
struct addrinfo *result, hints = {
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_ADDRCONFIG,
};
assert(b);

View File

@ -66,7 +66,10 @@ int stub_pid1(sd_id128_t uuid) {
if (pid == 0) {
/* Return in the child */
assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0);
setsid();
if (setsid() < 0)
return log_error_errno(errno, "Failed to become session leader in payload process: %m");
return 0;
}

View File

@ -2172,7 +2172,7 @@ static int setup_pts(const char *dest) {
}
static int setup_stdio_as_dev_console(void) {
int terminal;
_cleanup_close_ int terminal = -1;
int r;
terminal = open_terminal("/dev/console", O_RDWR);
@ -2187,6 +2187,7 @@ static int setup_stdio_as_dev_console(void) {
/* invalidates 'terminal' on success and failure */
r = rearrange_stdio(terminal, terminal, terminal);
TAKE_FD(terminal);
if (r < 0)
return log_error_errno(r, "Failed to move console to stdin/stdout/stderr: %m");

View File

@ -3142,7 +3142,7 @@ static int find_root(char **ret, int *ret_fd) {
if (!s)
return log_oom();
fd = open(arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, 0777);
fd = open(arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, 0666);
if (fd < 0)
return log_error_errno(errno, "Failed to create '%s': %m", arg_node);

View File

@ -59,7 +59,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
assert_se(arg_dest = dest);
if (check_executable(RC_LOCAL_SCRIPT_PATH_START) >= 0) {
if (check_executable(RC_LOCAL_PATH) >= 0) {
log_debug("Automatically adding rc-local.service.");
r = add_symlink("rc-local.service", "multi-user.target");

View File

@ -770,7 +770,12 @@ int dissect_image(
}
}
if (!m->partitions[PARTITION_ROOT].found) {
if (m->partitions[PARTITION_ROOT].found) {
/* If we found the primary arch, then invalidate the secondary arch to avoid any ambiguities,
* since we never want to mount the secondary arch in this case. */
m->partitions[PARTITION_ROOT_SECONDARY].found = false;
m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
} else {
/* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
* either, then check if there's a single generic one, and use that. */
@ -815,12 +820,6 @@ int dissect_image(
if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
return -EADDRNOTAVAIL;
/* If we found the primary root with the hash, then we definitely want to suppress any secondary root
* (which would be weird, after all the root hash should only be assigned to one pair of
* partitions... */
m->partitions[PARTITION_ROOT_SECONDARY].found = false;
m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
/* If we found a verity setup, then the root partition is necessarily read-only. */
m->partitions[PARTITION_ROOT].rw = false;
@ -992,7 +991,7 @@ static int mount_partition(
/* If requested, turn on discard support. */
if (fstype_can_discard(fstype) &&
((flags & DISSECT_IMAGE_DISCARD) ||
((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node)))) {
((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node) > 0))) {
options = strdup("discard");
if (!options)
return -ENOMEM;

View File

@ -418,7 +418,6 @@ static int resolve_remote(Connection *c) {
static const struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_ADDRCONFIG
};
const char *node, *service;

View File

@ -2,6 +2,7 @@
#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "strv.h"
@ -132,6 +133,46 @@ static void test_load_env_file_5(void) {
assert_se(data[2] == NULL);
}
static void test_write_and_load_env_file(void) {
const char *v;
/* Make sure that our writer, parser and the shell agree on what our env var files mean */
FOREACH_STRING(v,
"obbardc-laptop",
"obbardc\\-laptop",
"obbardc-lap\\top",
"obbardc-lap\\top",
"obbardc-lap\\\\top",
"double\"quote",
"single\'quote",
"dollar$dollar",
"newline\nnewline") {
_cleanup_(unlink_and_freep) char *p = NULL;
_cleanup_strv_free_ char **l = NULL;
_cleanup_free_ char *j = NULL, *w = NULL, *cmd = NULL, *from_shell = NULL;
_cleanup_pclose_ FILE *f = NULL;
size_t sz;
assert_se(tempfn_random_child(NULL, NULL, &p) >= 0);
assert_se(j = strjoin("TEST=", v));
assert_se(write_env_file(p, STRV_MAKE(j)) >= 0);
assert_se(cmd = strjoin(". ", p, " && /bin/echo -n \"$TEST\""));
assert_se(f = popen(cmd, "re"));
assert_se(read_full_stream(f, &from_shell, &sz) >= 0);
assert_se(sz == strlen(v));
assert_se(streq(from_shell, v));
assert_se(load_env_file(NULL, p, &l) >= 0);
assert_se(strv_equal(l, STRV_MAKE(j)));
assert_se(parse_env_file(NULL, p, "TEST", &w) >= 0);
assert_se(streq_ptr(w, v));
}
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
@ -140,4 +181,8 @@ int main(int argc, char *argv[]) {
test_load_env_file_3();
test_load_env_file_4();
test_load_env_file_5();
test_write_and_load_env_file();
return 0;
}

View File

@ -151,6 +151,18 @@ static void test_parse_env_file(void) {
assert_se(r >= 0);
}
static void test_one_shell_var(const char *file, const char *variable, const char *value) {
_cleanup_free_ char *cmd = NULL, *from_shell = NULL;
_cleanup_pclose_ FILE *f = NULL;
size_t sz;
assert_se(cmd = strjoin(". ", file, " && /bin/echo -n \"$", variable, "\""));
assert_se(f = popen(cmd, "re"));
assert_se(read_full_stream(f, &from_shell, &sz) >= 0);
assert_se(sz == strlen(value));
assert_se(streq(from_shell, value));
}
static void test_parse_multiline_env_file(void) {
_cleanup_(unlink_tempfilep) char
t[] = "/tmp/test-fileio-in-XXXXXX",
@ -162,8 +174,8 @@ static void test_parse_multiline_env_file(void) {
assert_se(fmkostemp_safe(t, "w", &f) == 0);
fputs("one=BAR\\\n"
" VAR\\\n"
"\tGAR\n"
"\\ \\ \\ \\ VAR\\\n"
"\\\tGAR\n"
"#comment\n"
"two=\"bar\\\n"
" var\\\n"
@ -173,9 +185,13 @@ static void test_parse_multiline_env_file(void) {
" var \\\n"
"\tgar \"\n", f);
fflush(f);
assert_se(fflush_and_check(f) >= 0);
fclose(f);
test_one_shell_var(t, "one", "BAR VAR\tGAR");
test_one_shell_var(t, "two", "bar var\tgar");
test_one_shell_var(t, "tri", "bar var \tgar ");
r = load_env_file(NULL, t, &a);
assert_se(r >= 0);

View File

@ -8,16 +8,16 @@
# (at your option) any later version.
# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if @RC_LOCAL_SCRIPT_PATH_START@ is executable.
# systemd-rc-local-generator if @RC_LOCAL_PATH@ is executable.
[Unit]
Description=@RC_LOCAL_SCRIPT_PATH_START@ Compatibility
Description=@RC_LOCAL_PATH@ Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=@RC_LOCAL_SCRIPT_PATH_START@
ConditionFileIsExecutable=@RC_LOCAL_PATH@
After=network.target
[Service]
Type=forking
ExecStart=@RC_LOCAL_SCRIPT_PATH_START@ start
ExecStart=@RC_LOCAL_PATH@ start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no