xz-utils
Packages=
- libqrencode3
+ libqrencode4
locales
libidn2-0
* networkd gained support for Foo-Over-UDP, ERSPAN and ISATAP
tunnels. It also gained a new option ForceDHCPv6PDOtherInformation=
for forcing the "Other Information" bit in IPv6 RA messages. The
- bonding logic gained three new options AdActorSystemPriority=,
+ bonding logic gained four new options AdActorSystemPriority=,
AdUserPortKey=, AdActorSystem= for configuring various 802.3ad
- aspects. The tunnel logic gained a new IPv6RapidDeploymentPrefix=
- option for configuring IPv6 Rapid Deployment. The policy rule logic
- gained four new options IPProtocol=, SourcePort= and
- DestinationPort=, InvertRule=. The bridge logic gained support for
- the MulticastToUnicast= option. networkd also gained support for
- configuring static IPv4 ARP or IPv6 neighbor entries.
+ aspects, and DynamicTransmitLoadBalancing= for enabling dynamic
+ shuffling of flows. The tunnel logic gained a new
+ IPv6RapidDeploymentPrefix= option for configuring IPv6 Rapid
+ Deployment. The policy rule logic gained four new options IPProtocol=,
+ SourcePort= and DestinationPort=, InvertRule=. The bridge logic gained
+ support for the MulticastToUnicast= option. networkd also gained
+ support for configuring static IPv4 ARP or IPv6 neighbor entries.
* .preset files (as read by 'systemctl preset') may now be used to
instantiate services.
interface names even as systemd/udev are updated and the naming logic
is improved.
+ * sd-id128.h learnt two new auxiliary helpers: sd_id128_is_allf() and
+ SD_ID128_ALLF to test if a 128bit ID is set to all 0xFF bytes, and to
+ initialize one to all 0xFF.
+
Contributions from: afg, Alan Jenkins, Aleksei Timofeyev, Alexander
Filippov, Alexander Kurtz, Alexey Bogdanenko, Andreas Henriksson,
Andrew Jorgensen, Anita Zhang, apnix-uk, Arkan49, Arseny Maslennikov,
Debian and FreeBSD resolvconf tool.
* Support for suspend-then-hibernate has been added, i.e. a sleep mode
- where the system initially suspends, and after a time-out resumes and
+ where the system initially suspends, and after a timeout resumes and
hibernates again.
* networkd's ClientIdentifier= now accepts a new option "duid-only". If
/run/systemd/user directory that was already previously
supported, but is under the control of the user.
- * Job timeouts (i.e. time-outs on the time a job that is
+ * Job timeouts (i.e. timeouts on the time a job that is
queued stays in the run queue) can now optionally result in
immediate reboot or power-off actions (JobTimeoutAction= and
JobTimeoutRebootArgument=). This is useful on ".target"
ESP the boot loader was run from formatted as NUL-terminated UTF16 string, in
normal GUID syntax.
-* The EFI variable `LoaderConfigTimeout` contains the boot menu time-out
+* The EFI variable `LoaderConfigTimeout` contains the boot menu timeout
currently in use. It may be modified both by the boot loader and by the
host. The value should be formatted as numeric, NUL-terminated, decimal
string, in UTF-16. The time is specified in µs.
* Similarly, the EFI variable `LoaderConfigTimeoutOneShot` contains a boot menu
- time-out for a single following boot. It is set by the OS in order to request
+ timeout for a single following boot. It is set by the OS in order to request
display of the boot menu on the following boot. When set overrides
`LoaderConfigTimeout`. It is removed automatically after being read by the
boot loader, to ensure it only takes effect a single time. This value is
formatted the same way as `LoaderConfigTimeout`. If set to `0` the boot menu
- time-out is turned off, and the menu is shown indefinitely.
+ timeout is turned off, and the menu is shown indefinitely.
* The EFI variable `LoaderEntries` may contain a series of boot loader entry
identifiers, one after the other, each individually NUL terminated. This may
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.011:bd11/03/2015:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnCherryTrailCR:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+# Teclast F5
+sensor:modalias:acpi:KIOX010A*:dmi:*:svnTECLAST:pnF5:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
#########################################
# Trekstor
#########################################
# The list may contain a single item which must be marked with an
# asterisk.
#
-# Local changes to the a non-default resolution of the mouse (e.g. through
+# Local changes to the non-default resolution of the mouse (e.g. through
# third-party software) must not be entered into this file, use a local
# hwdb instead.
#
<varlistentry>
<term><varname>LoaderConfigTimeout</varname></term>
<term><varname>LoaderConfigTimeoutOneShot</varname></term>
- <listitem><para>The menu time-out in seconds. Read by the boot loader. <varname>LoaderConfigTimeout</varname>
+ <listitem><para>The menu timeout in seconds. Read by the boot loader. <varname>LoaderConfigTimeout</varname>
is maintained persistently, while <varname>LoaderConfigTimeoutOneShot</varname> is a one-time override which is
read once (in which case it takes precedence over <varname>LoaderConfigTimeout</varname>) and then
removed. <varname>LoaderConfigTimeout</varname> may be manipulated with the
the command line. If passed, additional metadata is read from the device to enhance the unit to create. For
example, a descriptive string for the transient units is generated from the file system label and device
model. Moreover if a removable block device (e.g. USB stick) is detected an automount unit instead of a regular
- mount unit is created, with a short idle time-out, in order to ensure the file-system is placed in a clean
+ mount unit is created, with a short idle timeout, in order to ensure the file-system is placed in a clean
state quickly after each access.</para></listitem>
</varlistentry>
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
details about named file descriptors and their ordering.</para>
- <para>This setting defaults to <option>null</option>.</para></listitem>
+ <para>This setting defaults to <option>null</option>.</para>
+
+ <para>Note that services which specify <option>DefaultDependencies=no</option> and use
+ <varname>StandardInput=</varname> or <varname>StandardOutput=</varname> with
+ <option>tty</option>/<option>tty-force</option>/<option>tty-fail</option>, should specify
+ <option>After=systemd-vconsole-setup.service</option>, to make sure that the tty intialization is
+ finished before they start.</para></listitem>
</varlistentry>
<varlistentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>DynamicTransmitLoadBalancing=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Specifies if dynamic shuffling of flows is enabled. Applies only
+ for balance-tlb mode. Defaults to unset.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>MinLinks=</varname></term>
<listitem>
actual execution of the service program is delayed until all active jobs are dispatched. This may be used
to avoid interleaving of output of shell services with the status output on the console. Note that this
type is useful only to improve console output, it is not useful as a general unit ordering tool, and the
- effect of this service type is subject to a 5s time-out, after which the service program is invoked
+ effect of this service type is subject to a 5s timeout, after which the service program is invoked
anyway.</para></listitem>
</itemizedlist>
<term><varname>JobTimeoutSec=</varname></term>
<term><varname>JobRunningTimeoutSec=</varname></term>
- <listitem><para>When a job for this unit is queued, a time-out <varname>JobTimeoutSec=</varname> may be
+ <listitem><para>When a job for this unit is queued, a timeout <varname>JobTimeoutSec=</varname> may be
configured. Similarly, <varname>JobRunningTimeoutSec=</varname> starts counting when the queued job is actually
started. If either time limit is reached, the job will be cancelled, the unit however will not change state or
even enter the <literal>failed</literal> mode. This value defaults to <literal>infinity</literal> (job timeouts
<term><varname>JobTimeoutRebootArgument=</varname></term>
<listitem><para><varname>JobTimeoutAction=</varname> optionally configures an additional action to take when
- the time-out is hit, see description of <varname>JobTimeoutSec=</varname> and
+ the timeout is hit, see description of <varname>JobTimeoutSec=</varname> and
<varname>JobRunningTimeoutSec=</varname> above. It takes the same values as
<varname>StartLimitAction=</varname>. Defaults to <option>none</option>.
<varname>JobTimeoutRebootArgument=</varname> configures an optional reboot string to pass to the
}
__get_interfaces(){
- { cd /sys/class/net && echo *; } | \
- while read -d' ' -r name; do
+ local name
+ for name in $(cd /sys/class/net && ls); do
[[ "$name" != "lo" ]] && echo "$name"
done
}
env | { while read a; do echo " ${a%%=*}"; done; };
}
-__get_interfaces() {
- { cd /sys/class/net && echo *; } | \
- while read -d' ' -r name; do
+__get_interfaces(){
+ local name
+ for name in $(cd /sys/class/net && ls); do
[[ "$name" != "lo" ]] && echo "$name"
done
}
}
__get_interfaces(){
- { cd /sys/class/net && echo *; } | \
- while read -d' ' -r name; do
+ local name
+ for name in $(cd /sys/class/net && ls); do
[[ "$name" != "lo" ]] && echo "$name"
done
}
printf '%s\n' "${devs[@]%/}"
}
+__get_all_devs() {
+ local i
+ for i in /dev/* /dev/*/*; do
+ echo $i
+ done
+}
+
+__get_all_device_units() {
+ systemctl list-units -t device --full --no-legend --no-pager 2>/dev/null | \
+ { while read -r a b; do echo "$a"; done; }
+}
+
_udevadm() {
- local i verb comps
+ local i verb comps builtin
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
- local OPTS='-h --help --version --debug'
+ local -A OPTS=(
+ [COMMON]='-h --help -V --version'
+ [DEBUG]='-d --debug'
+ [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db'
+ [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
+ [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -w --settle'
+ [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
+ -a --attr-match -A --attr-nomatch -p --property-match
+ -g --tag-match -y --sysname-match --name-match -b --parent-match'
+ [SETTLE]='-t --timeout -E --exit-if-exists'
+ [CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload'
+ [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout'
+ [MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
+ [MONITOR_ARG]='-s --subsystem-match -t --tag-match'
+ [TEST]='-a --action -N --resolve-names'
+ )
- local verbs=(info trigger settle control monitor hwdb test-builtin test)
+ local verbs=(info trigger settle control monitor test-builtin test)
+ local builtins=(blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess)
for ((i=0; i < COMP_CWORD; i++)); do
- if __contains_word "${COMP_WORDS[i]}" "${verbs[@]}" &&
- ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then
+ if __contains_word "${COMP_WORDS[i]}" "${verbs[@]}"; then
verb=${COMP_WORDS[i]}
break
fi
done
if [[ -z $verb ]]; then
- COMPREPLY=( $(compgen -W '${OPTS[*]} ${verbs[*]}' -- "$cur") )
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $(compgen -W '${OPTS[COMMON]} ${OPTS[DEBUG]}' -- "$cur") )
+ else
+ COMPREPLY=( $(compgen -W '${verbs[*]}' -- "$cur") )
+ fi
return 0
fi
case $verb in
'info')
+ if __contains_word "$prev" ${OPTS[INFO_ARG]}; then
+ case $prev in
+ -q|--query)
+ comps='name symlink path property all'
+ ;;
+ -p|--path)
+ comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
+ ;;
+ -n|--name)
+ comps=$( __get_all_devs )
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
if [[ $cur = -* ]]; then
- comps='--help --query= --path= --name= --root --attribute-walk --export-db --cleanup-db'
+ comps="${OPTS[COMMON]} ${OPTS[INFO_STANDALONE]} ${OPTS[INFO_ARG]}"
else
- comps=$( __get_all_sysdevs )
+ comps=$( __get_all_sysdevs; __get_all_device_units )
+ local IFS=$'\n'
fi
;;
'trigger')
- comps='--help --verbose --dry-run --type= --action= --subsystem-match=
- --subsystem-nomatch= --attr-match= --attr-nomatch= --property-match=
- --tag-match= --sysname-match= --parent-match='
+ if __contains_word "$prev" ${OPTS[TRIGGER_ARG]}; then
+ case $prev in
+ -t|--type)
+ comps='devices subsystems'
+ ;;
+ -c|--action)
+ comps='add change remove bind unbind'
+ ;;
+ -y|--sysname-match|-b|--parent-match)
+ comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
+ ;;
+ --name-match)
+ comps=$( __get_all_devs )
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ if [[ $cur = -* ]]; then
+ comps="${OPTS[COMMON]} ${OPTS[TRIGGER_STANDALONE]} ${OPTS[TRIGGER_ARG]}"
+ else
+ comps=$( __get_all_sysdevs; __get_all_device_units )
+ local IFS=$'\n'
+ fi
;;
'settle')
- comps='--help --timeout= --seq-start= --seq-end= --exit-if-exists= --quiet'
+ if __contains_word "$prev" ${OPTS[SETTLE]}; then
+ case $prev in
+ -E|--exit-if-exists)
+ comps=$( compgen -A file -- "$cur" )
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ comps="${OPTS[COMMON]} ${OPTS[SETTLE]}"
;;
'control')
- comps='--help --exit --log-priority= --stop-exec-queue --start-exec-queue
- --reload --property= --children-max= --timeout='
+ if __contains_word "$prev" ${OPTS[CONTROL_ARG]}; then
+ case $prev in
+ -l|--log-priority)
+ comps='alert crit debug emerg err info notice warning'
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ comps="${OPTS[COMMON]} ${OPTS[CONTROL_STANDALONE]} ${OPTS[CONTROL_ARG]}"
;;
'monitor')
- comps='--help --kernel --udev --property --subsystem-match= --tag-match='
- ;;
- 'hwdb')
- comps='--help --update --test='
+ if __contains_word "$prev" ${OPTS[MONITOR_ARG]}; then
+ case $prev in
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ comps="${OPTS[COMMON]} ${OPTS[MONITOR_STANDALONE]} ${OPTS[MONITOR_ARG]}"
;;
'test')
+ if __contains_word "$prev" ${OPTS[TEST]}; then
+ case $prev in
+ -a|--action)
+ comps='add change remove bind unbind'
+ ;;
+ -N|--resolve-names)
+ comps='early late never'
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
if [[ $cur = -* ]]; then
- comps='--help --action='
+ comps="${OPTS[COMMON]} ${OPTS[TEST]}"
else
comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
fi
;;
'test-builtin')
- comps='blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess'
+ for ((i=0; i < COMP_CWORD; i++)); do
+ if __contains_word "${COMP_WORDS[i]}" "${builtins[@]}"; then
+ builtin=${COMP_WORDS[i]}
+ break
+ fi
+ done
+
+ if [[ -z $builtin ]]; then
+ comps="${builtins[@]}"
+ elif [[ $cur = -* ]]; then
+ comps="${OPTS[COMMON]}"
+ else
+ comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
+ fi
;;
*)
comps=${VERBS[*]}
return 1;
}
-int read_full_stream(FILE *f, char **contents, size_t *size) {
+int read_full_stream(
+ FILE *f,
+ char **ret_contents,
+ size_t *ret_size) {
+
_cleanup_free_ char *buf = NULL;
struct stat st;
size_t n, l;
int fd;
assert(f);
- assert(contents);
+ assert(ret_contents);
- n = LINE_MAX;
+ n = LINE_MAX; /* Start size */
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
+ if (!ret_size) {
+ /* Safety check: if the caller doesn't want to know the size of what we just read it will rely on the
+ * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise
+ * there'd be ambiguity about what we just read. */
+
+ if (memchr(buf, 0, l))
+ return -EBADMSG;
+ }
+
buf[l] = 0;
- *contents = TAKE_PTR(buf);
+ *ret_contents = TAKE_PTR(buf);
- if (size)
- *size = l;
+ if (ret_size)
+ *ret_size = l;
return 0;
}
if (n >= limit)
return -ENOBUFS;
+ if (count >= INT_MAX) /* We couldn't return the counter anymore as "int", hence refuse this */
+ return -ENOBUFS;
+
errno = 0;
c = fgetc_unlocked(f);
if (c == EOF) {
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr);
- return 32 - u32ctz(be32toh(addr->s_addr));
+ return 32U - u32ctz(be32toh(addr->s_addr));
}
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
return memcmp(s1, s2, n);
}
+/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */
+static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) {
+ return memcmp_safe(s1, s2, MIN(n1, n2))
+ ?: CMP(n1, n2);
+}
+
int on_ac_power(void);
#define memzero(x,l) \
static inline unsigned u32ctz(uint32_t n) {
#if __SIZEOF_INT__ == 4
- return __builtin_ctz(n);
+ return n != 0 ? __builtin_ctz(n) : 32;
#else
#error "Wut?"
#endif
return unit_get_realized_cgroup_path(userdata, mask);
}
-char *unit_default_cgroup_path(Unit *u) {
+char *unit_default_cgroup_path(const Unit *u) {
_cleanup_free_ char *escaped = NULL, *slice = NULL;
int r;
void unit_add_to_cgroup_realize_queue(Unit *u);
const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask);
-char *unit_default_cgroup_path(Unit *u);
+char *unit_default_cgroup_path(const Unit *u);
int unit_set_cgroup_path(Unit *u, const char *path);
int unit_pick_cgroup_path(Unit *u);
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
+#include "nscd-flush.h"
#include "parse-util.h"
#include "random-util.h"
#include "serialize.h"
_cleanup_close_ int etc_passwd_lock_fd = -1;
uid_t num = UID_INVALID; /* a uid if is_user, and a gid otherwise */
gid_t gid = GID_INVALID; /* a gid if is_user, ignored otherwise */
+ bool flush_cache = false;
int r;
assert(d);
}
/* Great! Nothing is stored here, still. Store our newly acquired data. */
+ flush_cache = true;
} else {
/* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
* acquired, and use what's stored now. */
if (r < 0)
return r;
+ if (flush_cache) {
+ /* If we allocated a new dynamic UID, refresh nscd, so that it forgets about potentially cached
+ * negative entries. But let's do so after we release the /etc/passwd lock, so that there's no
+ * potential for nscd wanting to lock that for completing the invalidation. */
+ etc_passwd_lock_fd = safe_close(etc_passwd_lock_fd);
+ (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
+ }
+
if (is_user) {
*ret_uid = num;
*ret_gid = gid != GID_INVALID ? gid : num;
/* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
unlink_uid_lock(lock_fd, uid, d->name);
+
+ (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
return 1;
}
r = open_follow(&filename, &f, symlink_names, &id);
if (r >= 0)
break;
- filename = mfree(filename);
/* ENOENT means that the file is missing or is a dangling symlink.
* ENOTDIR means that one of paths we expect to be is a directory
else if (!IN_SET(r, -ENOENT, -ENOTDIR))
return r;
+ filename = mfree(filename);
/* Empty the symlink names for the next run */
set_clear_free(symlink_names);
}
/* Data specific to the mount subsystem */
struct libmnt_monitor *mount_monitor;
sd_event_source *mount_event_source;
+ sd_event_source *mount_timeout_source;
+ usec_t mount_last_read_usec;
+ usec_t mount_last_duration_usec;
/* Data specific to the swap filesystem */
FILE *proc_swaps;
static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static int mount_dispatch_proc_self_mountinfo_timer(sd_event_source *source, usec_t usec, void *userdata);
static bool MOUNT_STATE_WITH_PROCESS(MountState state) {
return IN_SET(state,
static void mount_shutdown(Manager *m) {
assert(m);
+ m->mount_timeout_source = sd_event_source_unref(m->mount_timeout_source);
m->mount_event_source = sd_event_source_unref(m->mount_event_source);
mnt_unref_monitor(m->mount_monitor);
mount_shutdown(m);
}
+static void mount_process_proc_self_mountinfo(Manager *m);
+
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- _cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
Manager *m = userdata;
- const char *what;
- Iterator i;
- Unit *u;
int r;
+ usec_t next_read = usec_add(m->mount_last_read_usec,
+ m->mount_last_duration_usec * 10);
+
+ if (now(CLOCK_MONOTONIC) < next_read) {
+ /* The (current) API for getting mount events from the Linux kernel
+ * involves getting a "something changed" notification, and then having
+ * to re-read the entire /proc/self/mountinfo file. When there are lots
+ * of mountpoints, this file is large and parsing it can take noticeable
+ * time. As most of the file won't have changed, this can be seen as wasted time.
+ * If there is a "mount storm" such as 1000 mount points being created
+ * in quick succession, this will result in 1000 successive notification.
+ * If we respond to every notification, we will do quadratically more work
+ * than if we respond just once after all the notifications have arrived.
+ * In this (pathological) case, a delay in scheduling would actually
+ * improve throughput as we would combine notifications and parse
+ * the file less often. We cannot expect the scheduler to notice
+ * this pathology without help.
+ * So when the rate of notifications means we are spending more than
+ * 10% of real time handling them, we set a timer and stop listening
+ * to notifications for a while.
+ * If/when Linux provides an API which provides only details of what
+ * has changed, this rate-limiting can be removed.
+ */
+
+ r = sd_event_source_set_enabled(source, SD_EVENT_OFF);
+ if (r < 0)
+ log_warning_errno(r, "Failed to disable monitoring of /proc/self/mounting, ignoring: %m");
+ if (!m->mount_timeout_source) {
+ r = sd_event_add_time(m->event, &m->mount_timeout_source,
+ CLOCK_MONOTONIC,
+ next_read,
+ 0,
+ mount_dispatch_proc_self_mountinfo_timer,
+ m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set timeout to reread /proc/self/mounting, ignoring: %m");
+ }
+ return 0;
+ }
assert(m);
assert(revents & EPOLLIN);
return 0;
}
+ mount_process_proc_self_mountinfo(m);
+ return 0;
+}
+
+static int mount_dispatch_proc_self_mountinfo_timer(sd_event_source *source, usec_t usec, void *userdata) {
+ Manager *m = userdata;
+ int r;
+
+ r = sd_event_source_set_enabled(m->mount_event_source, SD_EVENT_ON);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reenable /proc/self/mountinfo monitor, ignoring: %m");
+ m->mount_timeout_source = sd_event_source_unref(source);
+ mount_process_proc_self_mountinfo(m);
+ return 0;
+}
+
+static void mount_process_proc_self_mountinfo(Manager *m) {
+ _cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
+ const char *what;
+ Iterator i;
+ Unit *u;
+ int r;
+
+ m->mount_last_read_usec = now(CLOCK_MONOTONIC);
+ /* If an error occurs, assume 10ms */
+ m->mount_last_duration_usec = 10 * USEC_PER_MSEC;
+
r = mount_load_proc_self_mountinfo(m, true);
if (r < 0) {
/* Reset flags, just in case, for later calls */
LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT])
MOUNT(u)->proc_flags = 0;
- return 0;
+ return;
}
manager_dispatch_load_queue(m);
/* Let the device units know that the device is no longer mounted */
device_found_node(m, what, 0, DEVICE_FOUND_MOUNT);
}
-
- return 0;
+ m->mount_last_duration_usec = usec_sub_unsigned(now(CLOCK_MONOTONIC),
+ m->mount_last_read_usec);
}
static void mount_reset_failed(Unit *u) {
#include "unit.h"
#include "user-util.h"
-static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_prefix_and_instance(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
return unit_name_to_prefix_and_instance(u->id, ret);
}
-static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_prefix(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
return unit_name_to_prefix(u->id, ret);
}
-static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_prefix_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
_cleanup_free_ char *p = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(u);
return unit_name_unescape(p, ret);
}
-static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_instance_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
return unit_name_unescape(strempty(u->instance), ret);
}
-static int specifier_last_component(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_last_component(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
_cleanup_free_ char *prefix = NULL;
char *dash;
int r;
return 0;
}
-static int specifier_last_component_unescaped(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_last_component_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
return unit_name_unescape(p, ret);
}
-static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_filename(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
return unit_name_to_path(u->id, ret);
}
-static void bad_specifier(Unit *u, char specifier) {
+static void bad_specifier(const Unit *u, char specifier) {
log_unit_warning(u, "Specifier '%%%c' used in unit configuration, which is deprecated. Please update your unit file, as it does not work as intended.", specifier);
}
-static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_cgroup(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n;
assert(u);
return 0;
}
-static int specifier_cgroup_root(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_cgroup_root(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n;
assert(u);
return 0;
}
-static int specifier_cgroup_slice(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_cgroup_slice(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n;
assert(u);
bad_specifier(u, specifier);
if (UNIT_ISSET(u->slice)) {
- Unit *slice;
+ const Unit *slice;
slice = UNIT_DEREF(u->slice);
return 0;
}
-static int specifier_special_directory(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_special_directory(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n = NULL;
assert(u);
return r;
}
-bool unit_has_name(Unit *u, const char *name) {
+bool unit_has_name(const Unit *u, const char *name) {
assert(u);
assert(name);
}
int unit_fail_if_noncanonical(Unit *u, const char* where) {
- _cleanup_free_ char *canonical_where;
+ _cleanup_free_ char *canonical_where = NULL;
int r;
assert(u);
const char *unit_description(Unit *u) _pure_;
-bool unit_has_name(Unit *u, const char *name);
+bool unit_has_name(const Unit *u, const char *name);
UnitActiveState unit_active_state(Unit *u);
STATIC_DESTRUCTOR_REGISTER(arg_usr_fstype, freep);
STATIC_DESTRUCTOR_REGISTER(arg_usr_options, freep);
-
static int write_options(FILE *f, const char *options) {
_cleanup_free_ char *o = NULL;
#include "id128-util.h"
#include "main-func.h"
#include "missing_capability.h"
+#include "nscd-flush.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid);
if (r < 0)
- log_info_errno(r, "Failed to read product UUID, ignoring: %m");
- c->has_uuid = (r >= 0);
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to read product UUID, ignoring: %m");
+ else if (sd_id128_is_null(c->uuid) || sd_id128_is_allf(c->uuid))
+ log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(c->uuid));
+ else
+ c->has_uuid = true;
return 0;
}
if (sethostname_idempotent(hn) < 0)
return -errno;
+ (void) nscd_flush_cache(STRV_MAKE("hosts"));
+
return 0;
}
#include "journal-util.h"
#include "journald-context.h"
#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "string-util.h"
#include "syslog-util.h"
/* Try to acquire the current cgroup path */
r = cg_pid_get_path_shifted(c->pid, s->cgroup_root, &t);
- if (r < 0) {
+ if (r < 0 || empty_or_root(t)) {
- /* If that didn't work, we use the unit ID passed in as fallback, if we have nothing cached yet */
+ /* We use the unit ID passed in as fallback if we have nothing cached yet and cg_pid_get_path_shifted()
+ * failed or process is running in a root cgroup. Zombie processes are automatically migrated to root cgroup
+ * on cgroupsv1 and we want to be able to map log messages from them too. */
if (unit_id && !c->unit) {
c->unit = strdup(unit_id);
if (c->unit)
#include "lldp-neighbor.h"
#include "missing.h"
#include "unaligned.h"
+#include "util.h"
static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
siphash24_compress(id->chassis_id, id->chassis_id_size, state);
}
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
- int r;
-
- r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
- if (r != 0)
- return r;
-
- r = CMP(x->chassis_id_size, y->chassis_id_size);
- if (r != 0)
- return r;
-
- r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
- if (r != 0)
- return r;
-
- return CMP(x->port_id_size, y->port_id_size);
+ return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
+ ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
}
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
usec_t method_call_timeout;
};
-/* For method calls we time-out at 25s, like in the D-Bus reference implementation */
+/* For method calls we timeout at 25s, like in the D-Bus reference implementation */
#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
/* For the authentication phase we grant 90s, to provide extra room during boot, when RNGs and such are not filled up
assert_return(m, -EINVAL);
assert_return((size_t) n == size, -EINVAL);
+ if (m->bound)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "sd-device-monitor: Socket fd is already bound. "
+ "It may be dangerous to change buffer size. "
+ "Refusing to change buffer size.");
+
if (setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUF, n) < 0) {
r = setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUFFORCE, n);
if (r < 0)
return r;
}
- if (!m->bound) {
- r = device_monitor_enable_receiving(m);
- if (r < 0)
- return r;
- }
+ r = device_monitor_enable_receiving(m);
+ if (r < 0)
+ return r;
m->callback = callback;
m->userdata = userdata;
return log_debug_errno(r, "sd-device-monitor: Failed to update filter: %m");
if (!m->bound) {
+ /* enable receiving of sender credentials */
+ r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device-monitor: Failed to set socket option SO_PASSCRED: %m");
+
if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0)
return log_debug_errno(errno, "sd-device-monitor: Failed to bind monitoring socket: %m");
m->bound = true;
- }
-
- r = monitor_set_nl_address(m);
- if (r < 0)
- return log_debug_errno(r, "sd-device-monitor: Failed to set address: %m");
- /* enable receiving of sender credentials */
- r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true);
- if (r < 0)
- return log_debug_errno(r, "sd-device-monitor: Failed to set socket option SO_PASSCRED: %m");
+ r = monitor_set_nl_address(m);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device-monitor: Failed to set address: %m");
+ }
return 0;
}
[IFLA_BOND_AD_ACTOR_SYS_PRIO] = { .type = NETLINK_TYPE_U16 },
[IFLA_BOND_AD_USER_PORT_KEY] = { .type = NETLINK_TYPE_U16 },
[IFLA_BOND_AD_ACTOR_SYSTEM] = { .type = NETLINK_TYPE_ETHER_ADDR },
+ [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NETLINK_TYPE_U8 },
};
static const NLType rtnl_link_info_data_iptun_types[] = {
if (r == 0)
break;
if (r == -ETIMEDOUT) {
- /* Let's catch time-outs here, so that we can run safely in a CI that has no reliable DNS. Note
+ /* Let's catch timeouts here, so that we can run safely in a CI that has no reliable DNS. Note
* that we invoke exit() directly here, as the stuck NSS call will not allow us to exit
* cleanly. */
machine_save(m);
machine_send_signal(m, true);
+ (void) manager_enqueue_nscd_cache_flush(m->manager);
return 0;
}
m->stopping = true;
machine_save(m);
+ (void) manager_enqueue_nscd_cache_flush(m->manager);
return r;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "machined.h"
+#include "nscd-flush.h"
+#include "strv.h"
+
+static int on_nscd_cache_flush_event(sd_event_source *s, void *userdata) {
+ /* Let's ask glibc's nscd daemon to flush its caches. We request this for the three database machines may show
+ * up in: the hosts database (for resolvable machine names) and the user and group databases (for the user ns
+ * ranges). */
+
+ (void) nscd_flush_cache(STRV_MAKE("passwd", "group", "hosts"));
+ return 0;
+}
+
+int manager_enqueue_nscd_cache_flush(Manager *m) {
+ int r;
+
+ assert(m);
+
+ if (!m->nscd_cache_flush_event) {
+ r = sd_event_add_defer(m->event, &m->nscd_cache_flush_event, on_nscd_cache_flush_event, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate NSCD cache flush event: %m");
+
+ sd_event_source_set_description(m->nscd_cache_flush_event, "nscd-cache-flush");
+ }
+
+ r = sd_event_source_set_enabled(m->nscd_cache_flush_event, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ m->nscd_cache_flush_event = sd_event_source_unref(m->nscd_cache_flush_event);
+ return log_error_errno(r, "Failed to enable NSCD cache flush event: %m");
+ }
+
+ return 0;
+}
static Manager* manager_unref(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
-DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(machine_hash_ops, void, trivial_hash_func, trivial_compare_func,
- Machine, machine_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(machine_hash_ops, char, string_hash_func, string_compare_func, Machine, machine_free);
static int manager_new(Manager **ret) {
_cleanup_(manager_unrefp) Manager *m = NULL;
if (!m)
return -ENOMEM;
- m->machines = hashmap_new(&string_hash_ops);
+ m->machines = hashmap_new(&machine_hash_ops);
m->machine_units = hashmap_new(&string_hash_ops);
- m->machine_leaders = hashmap_new(&machine_hash_ops);
+ m->machine_leaders = hashmap_new(NULL);
if (!m->machines || !m->machine_units || !m->machine_leaders)
return -ENOMEM;
assert(m->n_operations == 0);
- hashmap_free(m->machines);
+ hashmap_free(m->machines); /* This will free all machines, so that the machine_units/machine_leaders is empty */
hashmap_free(m->machine_units);
hashmap_free(m->machine_leaders);
hashmap_free(m->image_cache);
sd_event_source_unref(m->image_cache_defer_event);
+ sd_event_source_unref(m->nscd_cache_flush_event);
bus_verify_polkit_async_registry_free(m->polkit_registry);
LIST_HEAD(Operation, operations);
unsigned n_operations;
+
+ sd_event_source *nscd_cache_flush_event;
};
int manager_add_machine(Manager *m, const char *name, Machine **_machine);
int manager_unref_unit(Manager *m, const char *unit, sd_bus_error *error);
int manager_unit_is_active(Manager *manager, const char *unit);
int manager_job_is_active(Manager *manager, const char *path);
+
+int manager_enqueue_nscd_cache_flush(Manager *m);
machine.c
machine.h
machined-dbus.c
+ machined-core.c
machine-dbus.c
machine-dbus.h
image-dbus.c
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ALL_SLAVES_ACTIVE attribute: %m");
+ if (b->tlb_dynamic_lb >= 0) {
+ r = sd_netlink_message_append_u8(m, IFLA_BOND_TLB_DYNAMIC_LB, b->tlb_dynamic_lb);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_TLB_DYNAMIC_LB attribute: %m");
+ }
+
if (b->arp_interval > 0) {
if (b->n_arp_ip_targets > 0) {
b->primary_reselect = _NETDEV_BOND_PRIMARY_RESELECT_INVALID;
b->all_slaves_active = false;
+ b->tlb_dynamic_lb = -1;
b->resend_igmp = RESEND_IGMP_DEFAULT;
b->packets_per_slave = PACKETS_PER_SLAVE_DEFAULT;
BondArpAllTargets arp_all_targets;
BondPrimaryReselect primary_reselect;
+ int tlb_dynamic_lb;
+
bool all_slaves_active;
unsigned resend_igmp;
Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
-Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
+Bond.AllSlavesActive, config_parse_bool, 0, offsetof(Bond, all_slaves_active)
+Bond.DynamicTransmitLoadBalancing, config_parse_tristate, 0, offsetof(Bond, tlb_dynamic_lb)
Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
# operation for /etc/resolv.conf.
nameserver 127.0.0.53
+options edns0
return 0;
}
-static int specifier_dnssd_host_name(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_dnssd_host_name(char specifier, const void *data, const void *userdata, char **ret) {
DnssdService *s = (DnssdService *) userdata;
char *n;
#include "resolved-dns-server.h"
#include "resolved-manager.h"
-#define LINK_SEARCH_DOMAINS_MAX 32
-#define LINK_DNS_SERVERS_MAX 32
+#define LINK_SEARCH_DOMAINS_MAX 256
+#define LINK_DNS_SERVERS_MAX 256
struct LinkAddress {
Link *link;
#include "resolved-dns-trust-anchor.h"
#include "resolved-link.h"
-#define MANAGER_SEARCH_DOMAINS_MAX 32
-#define MANAGER_DNS_SERVERS_MAX 32
+#define MANAGER_SEARCH_DOMAINS_MAX 256
+#define MANAGER_DNS_SERVERS_MAX 256
typedef struct EtcHosts {
Hashmap *by_address;
"# See man:systemd-resolved.service(8) for details about the supported modes of\n"
"# operation for /etc/resolv.conf.\n"
"\n"
- "nameserver 127.0.0.53\n", f);
+ "nameserver 127.0.0.53\n"
+ "options edns0\n", f);
if (!ordered_set_isempty(domains))
write_resolv_conf_search(domains, f);
#include "unit-name.h"
#include "user-util.h"
-static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_prefix_and_instance(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
_cleanup_free_ char *prefix = NULL;
int r;
return 0;
}
-static int specifier_name(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_name(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
char *ans;
return 0;
}
-static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_prefix(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
assert(i);
return unit_name_to_prefix(i->name, ret);
}
-static int specifier_instance(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_instance(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
char *instance;
int r;
return 0;
}
-static int specifier_last_component(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_last_component(char specifier, const void *data, const void *userdata, char **ret) {
_cleanup_free_ char *prefix = NULL;
char *dash;
int r;
return 0;
}
-int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) {
-
+int install_full_printf(const UnitFileInstallInfo *i, const char *format, char **ret) {
/* This is similar to unit_full_printf() but does not support
* anything path-related.
*
#include "install.h"
-int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret);
+int install_full_printf(const UnitFileInstallInfo *i, const char *format, char **ret);
size_t n_rules;
} Presets;
-static inline bool unit_file_install_info_has_rules(UnitFileInstallInfo *i) {
+static inline bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) {
assert(i);
return !strv_isempty(i->aliases) ||
!strv_isempty(i->required_by);
}
-static inline bool unit_file_install_info_has_also(UnitFileInstallInfo *i) {
+static inline bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) {
assert(i);
return !strv_isempty(i->also);
if (!dirname)
return -ENOMEM;
- if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path))
+ if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) {
+ log_debug("Symlink %s → %s already exists", new_path, dest);
return 1;
+ }
if (!force) {
unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest);
static int find_symlinks_fd(
const char *root_dir,
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
bool match_aliases,
+ bool ignore_same_name,
int fd,
const char *path,
const char *config_path,
}
/* This will close nfd, regardless whether it succeeds or not */
- q = find_symlinks_fd(root_dir, i, match_aliases, nfd,
+ q = find_symlinks_fd(root_dir, i, match_aliases, ignore_same_name, nfd,
p, config_path, same_name_link);
if (q > 0)
return 1;
} else if (de->d_type == DT_LNK) {
_cleanup_free_ char *p = NULL, *dest = NULL;
- bool found_path, found_dest, b = false;
+ bool found_path = false, found_dest, b = false;
int q;
/* Acquire symlink name */
if (!x)
return -ENOMEM;
- free(dest);
- dest = x;
+ free_and_replace(dest, x);
}
- /* Check if the symlink itself matches what we
- * are looking for */
- if (path_is_absolute(i->name))
- found_path = path_equal(p, i->name);
- else
- found_path = streq(de->d_name, i->name);
+ assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
+ if (!ignore_same_name)
+ /* Check if the symlink itself matches what we are looking for.
+ *
+ * If ignore_same_name is specified, we are in one of the directories which
+ * have lower priority than the unit file, and even if a file or symlink with
+ * this name was found, we should ignore it. */
+ found_path = streq(de->d_name, i->name);
- /* Check if what the symlink points to
- * matches what we are looking for */
- if (path_is_absolute(i->name))
- found_dest = path_equal(dest, i->name);
- else
- found_dest = streq(basename(dest), i->name);
+ /* Check if what the symlink points to matches what we are looking for */
+ found_dest = streq(basename(dest), i->name);
if (found_path && found_dest) {
_cleanup_free_ char *t = NULL;
static int find_symlinks(
const char *root_dir,
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
bool match_name,
+ bool ignore_same_name,
const char *config_path,
bool *same_name_link) {
}
/* This takes possession of fd and closes it */
- return find_symlinks_fd(root_dir, i, match_name, fd,
+ return find_symlinks_fd(root_dir, i, match_name, ignore_same_name, fd,
config_path, config_path, same_name_link);
}
static int find_symlinks_in_scope(
UnitFileScope scope,
const LookupPaths *paths,
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
bool match_name,
UnitFileState *state) {
bool same_name_link_runtime = false, same_name_link_config = false;
bool enabled_in_runtime = false, enabled_at_all = false;
+ bool ignore_same_name = false;
char **p;
int r;
assert(paths);
assert(i);
+ /* As we iterate over the list of search paths in paths->search_path, we may encounter "same name"
+ * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
+ * efectively masked, so we should ignore them. */
+
STRV_FOREACH(p, paths->search_path) {
bool same_name_link = false;
- r = find_symlinks(paths->root_dir, i, match_name, *p, &same_name_link);
+ r = find_symlinks(paths->root_dir, i, match_name, ignore_same_name, *p, &same_name_link);
if (r < 0)
return r;
if (r > 0) {
same_name_link_runtime = true;
}
}
+
+ /* Check if next iteration will be "below" the unit file (either a regular file
+ * or a symlink), and hence should be ignored */
+ if (!ignore_same_name && path_startswith(i->path, *p))
+ ignore_same_name = true;
}
if (enabled_in_runtime) {
}
static int install_info_may_process(
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
const LookupPaths *paths,
UnitFileChange **changes,
size_t *n_changes) {
r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
&i, NULL, NULL);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to discover unit %s: %m", name);
+
+ assert(IN_SET(i->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED));
+ log_debug("Found unit %s at %s (%s)", name, strna(i->path),
+ i->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask");
/* Shortcut things, if the caller just wants to know if this unit exists. */
if (!ret)
install-printf.h
install.c
install.h
+ ip-protocol-list.c
+ ip-protocol-list.h
journal-importer.c
journal-importer.h
journal-util.c
module-util.h
mount-util.c
mount-util.h
+ nscd-flush.c
+ nscd-flush.h
nsflags.c
nsflags.h
os-util.c
serialize.h
sleep-config.c
sleep-config.h
- ip-protocol-list.c
- ip-protocol-list.h
spawn-ask-password-agent.c
spawn-ask-password-agent.h
spawn-polkit-agent.c
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <sys/poll.h>
+
+#include "fd-util.h"
+#include "io-util.h"
+#include "nscd-flush.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+#define NSCD_FLUSH_CACHE_TIMEOUT_USEC (5*USEC_PER_SEC)
+
+struct nscdInvalidateRequest {
+ int32_t version;
+ int32_t type; /* in glibc this is an enum. We don't replicate this here 1:1. Also, wtf, how unportable is that
+ * even? */
+ int32_t key_len;
+ char dbname[];
+};
+
+static const union sockaddr_union nscd_sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/nscd/socket",
+};
+
+static int nscd_flush_cache_one(const char *database, usec_t end) {
+ size_t req_size, has_written = 0, has_read = 0, l;
+ struct nscdInvalidateRequest *req;
+ _cleanup_close_ int fd = -1;
+ int32_t resp;
+ int events;
+
+ assert(database);
+
+ l = strlen(database);
+ req_size = offsetof(struct nscdInvalidateRequest, dbname) + l + 1;
+
+ req = alloca(req_size);
+ *req = (struct nscdInvalidateRequest) {
+ .version = 2,
+ .type = 10,
+ .key_len = l + 1,
+ };
+
+ strcpy(req->dbname, database);
+
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return log_debug_errno(errno, "Failed to allocate nscd socket: %m");
+
+ /* Note: connect() returns EINPROGRESS if O_NONBLOCK is set and establishing a connection takes time. The
+ * kernel lets us know this way that the connection is now being established, and we should watch with poll()
+ * to learn when it is fully established. That said, AF_UNIX on Linux never triggers this IRL (connect() is
+ * always instant on AF_UNIX), hence handling this is mostly just an excercise in defensive, protocol-agnostic
+ * programming.
+ *
+ * connect() returns EAGAIN if the socket's backlog limit has been reached. When we see this we give up right
+ * away, after all this entire function here is written in a defensive style so that a non-responding nscd
+ * doesn't stall us for good. (Even if we wanted to handle this better: the Linux kernel doesn't really have a
+ * nice way to connect() to a server synchronously with a time limit that would also cover dealing with the
+ * backlog limit. After all SO_RCVTIMEO and SR_SNDTIMEO don't apply to connect(), and alarm() is frickin' ugly
+ * and not really reasonably usable from threads-aware code.) */
+ if (connect(fd, &nscd_sa.sa, SOCKADDR_UN_LEN(nscd_sa.un)) < 0) {
+ if (errno == EAGAIN)
+ return log_debug_errno(errno, "nscd is overloaded (backlog limit reached) and refuses to take further connections: %m");
+ if (errno != EINPROGRESS)
+ return log_debug_errno(errno, "Failed to connect to nscd socket: %m");
+
+ /* Continue in case of EINPROGRESS, but don't bother with send() or recv() until being notified that
+ * establishing the connection is complete. */
+ events = 0;
+ } else
+ events = POLLIN|POLLOUT; /* Let's assume initially that we can write and read to the fd, to suppress
+ * one poll() invocation */
+ for (;;) {
+ usec_t p;
+
+ if (events & POLLOUT) {
+ ssize_t m;
+
+ assert(has_written < req_size);
+
+ m = send(fd, (uint8_t*) req + has_written, req_size - has_written, MSG_NOSIGNAL);
+ if (m < 0) {
+ if (errno != EAGAIN) /* Note that EAGAIN is returned by the kernel whenever it can't
+ * take the data right now, and that includes if the connect() is
+ * asynchronous and we saw EINPROGRESS on it, and it hasn't
+ * completed yet. */
+ return log_debug_errno(errno, "Failed to write to nscd socket: %m");
+ } else
+ has_written += m;
+ }
+
+ if (events & (POLLIN|POLLERR|POLLHUP)) {
+ ssize_t m;
+
+ if (has_read >= sizeof(resp))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Response from nscd longer than expected: %m");
+
+ m = recv(fd, (uint8_t*) &resp + has_read, sizeof(resp) - has_read, 0);
+ if (m < 0) {
+ if (errno != EAGAIN)
+ return log_debug_errno(errno, "Failed to read from nscd socket: %m");
+ } else if (m == 0) { /* EOF */
+ if (has_read == 0 && has_written >= req_size) /* Older nscd immediately terminated the
+ * connection, accept that as OK */
+ return 1;
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "nscd prematurely ended connection.");
+ } else
+ has_read += m;
+ }
+
+ if (has_written >= req_size && has_read >= sizeof(resp)) { /* done? */
+ if (resp < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "nscd sent us a negative error numer: %i", resp);
+ if (resp > 0)
+ return log_debug_errno(resp, "nscd return failure code on invalidating '%s'.", database);
+ return 1;
+ }
+
+ p = now(CLOCK_MONOTONIC);
+ if (p >= end)
+ return -ETIMEDOUT;
+
+ events = fd_wait_for_event(fd, POLLIN | (has_written < req_size ? POLLOUT : 0), end - p);
+ if (events < 0)
+ return events;
+ }
+}
+
+int nscd_flush_cache(char **databases) {
+ usec_t end;
+ int r = 0;
+ char **i;
+
+ /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s time-out, so that we
+ * don't block indefinitely on another service. */
+
+ end = usec_add(now(CLOCK_MONOTONIC), NSCD_FLUSH_CACHE_TIMEOUT_USEC);
+
+ STRV_FOREACH(i, databases) {
+ int k;
+
+ k = nscd_flush_cache_one(*i, end);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+
+ return r;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+int nscd_flush_cache(char **databases);
* and "%" used for escaping. */
#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
-int specifier_printf(const char *text, const Specifier table[], void *userdata, char **_ret) {
+int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **_ret) {
size_t l, allocated = 0;
_cleanup_free_ char *ret = NULL;
char *t;
/* Generic handler for simple string replacements */
-int specifier_string(char specifier, void *data, void *userdata, char **ret) {
+int specifier_string(char specifier, const void *data, const void *userdata, char **ret) {
char *n;
n = strdup(strempty(data));
return 0;
}
-int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) {
+int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret) {
sd_id128_t id;
char *n;
int r;
return 0;
}
-int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) {
+int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret) {
sd_id128_t id;
char *n;
int r;
return 0;
}
-int specifier_host_name(char specifier, void *data, void *userdata, char **ret) {
+int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret) {
char *n;
n = gethostname_malloc();
return 0;
}
-int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret) {
+int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) {
struct utsname uts;
char *n;
int r;
return 0;
}
-int specifier_group_name(char specifier, void *data, void *userdata, char **ret) {
+int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) {
char *t;
t = gid_to_name(getgid());
return 0;
}
-int specifier_group_id(char specifier, void *data, void *userdata, char **ret) {
+int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret) {
if (asprintf(ret, UID_FMT, getgid()) < 0)
return -ENOMEM;
return 0;
}
-int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
+int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret) {
char *t;
/* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
return 0;
}
-int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
+int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret) {
if (asprintf(ret, UID_FMT, getuid()) < 0)
return -ENOMEM;
return 0;
}
-int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
+int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret) {
/* On PID 1 (which runs as root) this will not result in NSS,
* which is good. See above */
return get_home_dir(ret);
}
-int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
+int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret) {
/* On PID 1 (which runs as root) this will not result in NSS,
* which is good. See above */
return get_shell(ret);
}
-int specifier_tmp_dir(char specifier, void *data, void *userdata, char **ret) {
+int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
const char *p;
char *copy;
int r;
return 0;
}
-int specifier_var_tmp_dir(char specifier, void *data, void *userdata, char **ret) {
+int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
const char *p;
char *copy;
int r;
#include "string-util.h"
-typedef int (*SpecifierCallback)(char specifier, void *data, void *userdata, char **ret);
+typedef int (*SpecifierCallback)(char specifier, const void *data, const void *userdata, char **ret);
typedef struct Specifier {
const char specifier;
const SpecifierCallback lookup;
- void *data;
+ const void *data;
} Specifier;
-int specifier_printf(const char *text, const Specifier table[], void *userdata, char **ret);
+int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **ret);
-int specifier_string(char specifier, void *data, void *userdata, char **ret);
+int specifier_string(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_machine_id(char specifier, void *data, void *userdata, char **ret);
-int specifier_boot_id(char specifier, void *data, void *userdata, char **ret);
-int specifier_host_name(char specifier, void *data, void *userdata, char **ret);
-int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret);
+int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_group_name(char specifier, void *data, void *userdata, char **ret);
-int specifier_group_id(char specifier, void *data, void *userdata, char **ret);
-int specifier_user_name(char specifier, void *data, void *userdata, char **ret);
-int specifier_user_id(char specifier, void *data, void *userdata, char **ret);
-int specifier_user_home(char specifier, void *data, void *userdata, char **ret);
-int specifier_user_shell(char specifier, void *data, void *userdata, char **ret);
+int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_tmp_dir(char specifier, void *data, void *userdata, char **ret);
-int specifier_var_tmp_dir(char specifier, void *data, void *userdata, char **ret);
+int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret);
static inline char* specifier_escape(const char *string) {
return strreplace(string, "%", "%%");
value = strchr(p, '=');
if (!value) {
- log_error("Line is not an assignment at '%s:%u': %s", path, c, value);
+ log_error("Line is not an assignment at '%s:%u': %s", path, c, p);
if (r == 0)
r = -EINVAL;
int sd_id128_randomize(sd_id128_t *ret);
int sd_id128_get_machine(sd_id128_t *ret);
-int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
-int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
-#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
- ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
- 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }})
+int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
+int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
{ .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}
+#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
+ ((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15))
+
+
/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16
* times. It is hence not a good idea to call this macro with an
* expensive function as parameter or an expression with side
return a.qwords[0] == 0 && a.qwords[1] == 0;
}
+_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
+ return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
+}
+
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
+#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
_SD_END_DECLARATIONS;
libblkid],
'', 'manual'],
+ [['src/test/test-nscd-flush.c'],
+ [libcore,
+ libshared],
+ [threads,
+ librt,
+ libseccomp,
+ libselinux,
+ libmount,
+ libblkid],
+ '', 'manual'],
+
[['src/test/test-loopback.c'],
[libcore,
libshared],
assert_se(read_line(f, LINE_MAX, NULL) == 0);
}
+static void test_read_line4(void) {
+ static const struct {
+ size_t length;
+ const char *string;
+ } eof_endings[] = {
+ /* Each of these will be followed by EOF and should generate the one same single string */
+ { 3, "foo" },
+ { 4, "foo\n" },
+ { 4, "foo\r" },
+ { 4, "foo\0" },
+ { 5, "foo\n\0" },
+ { 5, "foo\r\0" },
+ { 5, "foo\r\n" },
+ { 5, "foo\n\r" },
+ { 6, "foo\r\n\0" },
+ { 6, "foo\n\r\0" },
+ };
+
+ size_t i;
+ int r;
+
+ for (i = 0; i < ELEMENTSOF(eof_endings); i++) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(f = fmemopen((void*) eof_endings[i].string, eof_endings[i].length, "r"));
+
+ r = read_line(f, (size_t) -1, &s);
+ assert_se((size_t) r == eof_endings[i].length);
+ assert_se(streq_ptr(s, "foo"));
+
+ assert_se(read_line(f, (size_t) -1, NULL) == 0); /* Ensure we hit EOF */
+ }
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_read_line();
test_read_line2();
test_read_line3();
+ test_read_line4();
return 0;
}
"%s"
"--- original end ---\n", data);
- assert_se(f = fmemopen((void*) data, sizeof(data), "r"));
+ assert_se(f = fmemopen((void*) data, strlen(data), "r"));
assert_se(json_parse_file(f, "waldo", &v, NULL, NULL) >= 0);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "main-func.h"
+#include "nscd-flush.h"
+#include "strv.h"
+#include "tests.h"
+
+static int run(int argc, char *argv[]) {
+ int r;
+
+ test_setup_logging(LOG_DEBUG);
+
+ r = nscd_flush_cache(STRV_MAKE("group", "passwd", "hosts"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to flush NSCD cache");
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
+ assert_se(write(fd, env_file_1, strlen(env_file_1)) == strlen(env_file_1));
r = load_env_file(NULL, name, &data);
assert_se(r == 0);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
+ assert_se(write(fd, env_file_2, strlen(env_file_2)) == strlen(env_file_2));
r = load_env_file(NULL, name, &data);
assert_se(r == 0);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
+ assert_se(write(fd, env_file_3, strlen(env_file_3)) == strlen(env_file_3));
r = load_env_file(NULL, name, &data);
assert_se(r == 0);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
+ assert_se(write(fd, env_file_4, strlen(env_file_4)) == strlen(env_file_4));
r = load_env_file(NULL, name, &data);
assert_se(r == 0);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5));
+ assert_se(write(fd, env_file_5, strlen(env_file_5)) == strlen(env_file_5));
r = load_env_file(NULL, name, &data);
assert_se(r == 0);
STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
-static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret);
-static int specifier_directory(char specifier, void *data, void *userdata, char **ret);
+static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret);
+static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret);
static const Specifier specifier_table[] = {
{ 'm', specifier_machine_id_safe, NULL },
{}
};
-static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret) {
int r;
/* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
return r;
}
-static int specifier_directory(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret) {
struct table_entry {
uint64_t type;
const char *suffix;
if (r < 0)
return log_error_errno(r, "Failed to initialize device monitor: %m");
- if (fd_uevent < 0)
- (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
+ (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
/* unnamed socket from workers to the main daemon */
r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
LACPTransmitRate=
ARPIntervalSec=
AllSlavesActive=
+DynamicTransmitLoadBalancing=
[FooOverUDP]
Protocol=
Port=
--- /dev/null
+[NetDev]
+Name=bond99
+Kind=bond
+
+[Bond]
+Mode=balance-tlb
+DynamicTransmitLoadBalancing=true
'21-vlan.network',
'25-6rd-tunnel.netdev',
'25-bond.netdev',
+ '25-bond-balanced-tlb.netdev',
'25-bridge.netdev',
'25-erspan-tunnel.netdev',
'25-geneve.netdev',
self.assertEqual('811', self.read_link_attr('bond99', 'bonding', 'ad_user_port_key'))
self.assertEqual('00:11:22:33:44:55', self.read_link_attr('bond99', 'bonding', 'ad_actor_system'))
+ def test_bond_balanced_tlb(self):
+ self.copy_unit_to_networkd_unit_path('25-bond-balanced-tlb.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('bond99'))
+
+ self.assertEqual('balance-tlb 5', self.read_link_attr('bond99', 'bonding', 'mode'))
+ self.assertEqual('1', self.read_link_attr('bond99', 'bonding', 'tlb_dynamic_lb'))
+
def test_vlan(self):
self.copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev', '21-vlan.network')
self.start_networkd()