Compare commits

...

51 Commits

Author SHA1 Message Date
Lennart Poettering 197db625a3 logind: fix merge issue
The two PRs #16664 and #16635 individually passed CI, but when combined
cannot build. Since both are merged now, let's fix that.
2020-09-09 20:13:57 +02:00
Robert Marko adbb2b6afe login: Add KEY_RESTART handling
KEY_RESTART is widely used in Linux to indicate device reboot.
So lets handle it in the same fashion as KEY_POWER.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
2020-09-09 18:40:13 +02:00
Franck Bui 6e220b4b88 logind.conf: document UserStopDelaySec in logind.conf 2020-09-09 17:46:00 +02:00
Lennart Poettering 12ce0f4173
Merge pull request #16635 from keszybz/do-not-for-each-word
Drop FOREACH_WORD
2020-09-09 17:43:38 +02:00
Lennart Poettering a6b3be1abf
Merge pull request #16972 from wusto/ambient-and-keep-caps-corrections
Ambient capabilities documenation and keep-caps usage corrections
2020-09-09 17:09:42 +02:00
Lennart Poettering 244d9793ee
Merge pull request #16984 from yuwata/make-log_xxx_error-void
Make log_xxx_error() or friends return void
2020-09-09 16:28:51 +02:00
Lennart Poettering 39c4e2c1a1
Merge pull request #16982 from yuwata/socket-buffer-size
Fixes for socket buffer size
2020-09-09 16:28:21 +02:00
Tobias Kaufmann 198dc17845 core: fix set keep caps for ambient capabilities
The securebit keep-caps retains the capabilities in the permitted set
over an UID change (ambient capabilities are cleared though).

Setting the keep-caps securebit after the uid change and before execve
doesn't make sense as it is cleared during execve and there is no
additional user ID change after this point.

Altough the documentation (man 7 capabilities) is ambigious, keep-caps
is reset during execve although keep-caps-locked is set. After execve
only keep-caps-locked is set and keep-caps is cleared.
2020-09-09 11:17:42 +02:00
Tobias Kaufmann 16fcb1918a core: fix comments on ambient capabilities
The comments on the code for ambient capabilities was wrong/outdated.
2020-09-09 11:17:42 +02:00
Zbigniew Jędrzejewski-Szmek 90e30d767a Rename strv_split_extract() to strv_split_full()
Now that _full() is gone, we can rename _extract() to have the usual suffix
we use for the more featureful version.
2020-09-09 09:34:55 +02:00
Zbigniew Jędrzejewski-Szmek 03b62851a9 Remove FOREACH_WORD and friends 2020-09-09 09:34:55 +02:00
Zbigniew Jędrzejewski-Szmek 0645b83a40 tree-wide: replace strv_split_full() with strv_split_extract() everywhere
Behaviour is not identical, as shown by the tests in test-strv.
The combination of EXTRACT_UNQUOTE without EXTRACT_RELAX only appears in
the test, so it doesn't seem particularly important. OTOH, the difference
in handling of squished parameters could make a difference. New behaviour
is what both bash and python do, so I think we can ignore this corner case.

This change has the following advantages:
- the duplication of code paths that do a very similar thing is removed
- extract_one_word() / strv_split_extract() return a proper error code.
2020-09-09 09:34:55 +02:00
Zbigniew Jędrzejewski-Szmek d59d954d7f test-string-util: stop testing FOREACH_WORD 2020-09-09 09:34:55 +02:00
Zbigniew Jędrzejewski-Szmek 45638a63c0 shared/fstab-util: replace FOREACH_WORD_SEPARATOR() with open-coded loop
The tricky part here is that the function is not allowed to fail in this code
path. Initially, I wanted to change the return value to allow it to fail, but
this cascades through all the places where fstab_test_option() and friends are
used; updating all those sites would be a lot of work. And since quoting is not
allowed here, a simple loop with strcspn() is easy to do.
2020-09-09 09:34:55 +02:00
Zbigniew Jędrzejewski-Szmek 0e8d185938 shared/fstab-util: use free_and_str[n]dup()
No functional change. I'm keeping this separate to make review easier.
2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek ecaf258eb4 Use extract_first_word() in generated conf parsers 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek 087908c140 nspawn: use extract_first_word() 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek 2417658d6a getty-generator: use extract_first_word() 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek da277e90a4 sd-journal: use extract_first_word() 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek cc24f0b872 delta: use extract_first_word() 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek dd2fff3a18 cryptsetup: use extract_first_word() 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek 7896ad8f66 core/load-fragment: use extract_first_word()
This is much nicer, and also fixes a potential overflow when we used
'word' in log_error() as if it was a NUL-terminated string.
2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek dd630d3cac Let sd_machine_get_ifindices() omit the output param too
Nowadays we do that almost everywhere, let's also do it here.
2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek 0ef14adc1c Rewrite sd_machine_get_ifindices() to avoid FOREACH_WORD()
If we fail to parse the index, the failure is propogated as -EUNCLEAN.
(-EINVAL would be confused with invalid args to the function itself.)
2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek aa3b40c3f9 Fix output value of sd_seat_get_sessions() and drop FOREACH_WORD use
sd_seat_get_sessions() would return 0 in the 'n_uids' (now 'ret_n_uids') output
parameter when 'uid' (now 'ret_uids') was passed as NULL.

While at it, drop FOREACH_WORD() use.

Also use any whitespace as separator. In practice this shouldn't matter, since
logind always uses spaces, but it seems nicer to not specify this explicitly,
and the default is more flexible.
2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek 87a4d416e5 sd-device: use extract_first_word() 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek ae7ef63f21 basic/cgroup-util: port over to string_contains_word() 2020-09-09 09:34:54 +02:00
Zbigniew Jędrzejewski-Szmek 46ed9f4ce1 logind: use extract_first_word() 2020-09-09 09:34:54 +02:00
Yu Watanabe e77f52e5f9 udev: warn if failed to set buffer size for device monitor 2020-09-09 06:46:54 +09:00
Yu Watanabe a725efb08b network: increase receive buffer size for device monitor
If networkd creates huge amount of netdevs, then the buffer of device
monitor becomes easily flowed.

Hopefully fixes #16865.
2020-09-09 06:39:05 +09:00
Yu Watanabe d31f33e3c9 network: do not start device monitor if /sys is read-only
Follow-up for bf331d8717.
2020-09-09 06:39:05 +09:00
Yu Watanabe e13af7bdb6 network: honor the buffer size specified in networkd.socket 2020-09-09 06:39:05 +09:00
Yu Watanabe ded71ab3bc core/socket: use fd_set_{rcv,snd}buf() 2020-09-09 06:39:05 +09:00
Yu Watanabe 2807a79424 sd-device-monitor: use fd_set_rcvbuf() 2020-09-09 06:39:05 +09:00
Yu Watanabe d9d9b2a0ae util: introduce fd_set_{snd,rcv}buf() 2020-09-09 06:39:05 +09:00
Yu Watanabe b92f350789 util: try to set with SO_{RCV,SND}BUFFORCE when requested size is larger than the kernel limit
The commit 10ce2e0681 inverts the order of
SO_{RCV,SND}BUFFORCE and SO_{RCV,SND}BUF. However, setting buffer size with
SO_{RCV,SND}BUF does not fail even if the requested size is larger than
the kernel limit. Hence, SO_{RCV,SND}BUFFORCE will not use anymore and
the buffer size is always limited by the kernel limit even if we have
the priviledge to ignore the limit.

This makes the buffer size is checked after configuring it with
SO_{RCV,SND}BUF, and if it is still not sufficient, then try to set it
with FORCE command. With this commit, if we have enough priviledge, the
requested buffer size is correctly set.

Hopefully fixes #14417.
2020-09-09 06:39:05 +09:00
Yu Watanabe 1263c85ef3 util: refuse to set too large value for socket buffer size 2020-09-09 06:39:05 +09:00
Yu Watanabe 8c63924c8d network: ignore error on increasing netlink receive buffer size 2020-09-09 06:39:05 +09:00
Yu Watanabe d157714b68 network: make log_link_error() or friends return void 2020-09-09 02:34:38 +09:00
Yu Watanabe 8ed6f81ba3 core: make log_unit_error() or friends return void 2020-09-09 02:34:38 +09:00
Yu Watanabe 93c5b90459 core/slice: explicitly specify return value 2020-09-09 02:34:38 +09:00
Yu Watanabe 38104ee2a5 udev: do not discard const qualifier 2020-09-09 02:34:38 +09:00
Yu Watanabe ab54f12b78 sd-device: make log_device_error() or friends return void 2020-09-09 02:34:38 +09:00
Yu Watanabe 09c69ecaeb udev: explicitly specify return value 2020-09-09 02:34:38 +09:00
Yu Watanabe a718b951ac udev: return negative errno for invalid EVDEV_ABS_XXX= property 2020-09-09 02:34:38 +09:00
Yu Watanabe 3b4e123173 udev: make log_rule_error() or friends return void 2020-09-09 02:34:38 +09:00
Zbigniew Jędrzejewski-Szmek 434fef6de3 shared/sleep-config: more logging and port to extract_first_word() 2020-09-04 13:53:47 +02:00
Zbigniew Jędrzejewski-Szmek 46bf625aca Add string_contains_word_strv()
I had to move STRV_MAKE to macro.h. There is a circular dependency between
extract-word.h, strv.h, and string-util.h that makes it hard to define the
inline function otherwise.
2020-09-04 12:59:26 +02:00
Zbigniew Jędrzejewski-Szmek 81823e6c12 sd-login: use string_contains_word() 2020-09-04 12:59:26 +02:00
Zbigniew Jędrzejewski-Szmek 53cd7f3374 basic: add string_contains_word()
This wraps the common pattern of using extract_first_word() in a loop
to look for a matching word.
2020-09-04 12:59:25 +02:00
Zbigniew Jędrzejewski-Szmek 0176728a73 test-string-util,test-extract-word: add log headers 2020-09-04 12:59:25 +02:00
69 changed files with 978 additions and 841 deletions

View File

@ -74,9 +74,6 @@ ForEachMacros:
- FOREACH_INOTIFY_EVENT
- FOREACH_STRING
- FOREACH_SUBSYSTEM
- _FOREACH_WORD
- FOREACH_WORD
- FOREACH_WORD_SEPARATOR
- HASHMAP_FOREACH
- HASHMAP_FOREACH_IDX
- HASHMAP_FOREACH_KEY

View File

@ -199,10 +199,11 @@
<term><varname>HandleLidSwitch=</varname></term>
<term><varname>HandleLidSwitchExternalPower=</varname></term>
<term><varname>HandleLidSwitchDocked=</varname></term>
<term><varname>HandleRebootKey=</varname></term>
<listitem><para>Controls how logind shall handle the
system power and sleep keys and the lid switch to trigger
actions such as system power-off or suspend. Can be one of
system power, reboot and sleep keys and the lid switch to trigger
actions such as system power-off, reboot or suspend. Can be one of
<literal>ignore</literal>,
<literal>poweroff</literal>,
<literal>reboot</literal>,
@ -219,7 +220,8 @@
in the respective event. Only input devices with the
<literal>power-switch</literal> udev tag will be watched for
key/lid switch events. <varname>HandlePowerKey=</varname>
defaults to <literal>poweroff</literal>.
defaults to <literal>poweroff</literal>, <varname>HandleRebootKey=</varname>
defaults to <literal>reboot</literal>.
<varname>HandleSuspendKey=</varname> and
<varname>HandleLidSwitch=</varname> default to
<literal>suspend</literal>.
@ -240,7 +242,8 @@
<para>A different application may disable logind's handling of system power and
sleep keys and the lid switch by taking a low-level inhibitor lock
(<literal>handle-power-key</literal>, <literal>handle-suspend-key</literal>,
<literal>handle-hibernate-key</literal>, <literal>handle-lid-switch</literal>).
<literal>handle-hibernate-key</literal>, <literal>handle-lid-switch</literal>,
<literal>handle-reboot-switch</literal>).
This is most commonly used by graphical desktop environments
to take over suspend and hibernation handling, and to use their own configuration
mechanisms. If a low-level inhibitor lock is taken, logind will not take any
@ -253,20 +256,23 @@
<term><varname>SuspendKeyIgnoreInhibited=</varname></term>
<term><varname>HibernateKeyIgnoreInhibited=</varname></term>
<term><varname>LidSwitchIgnoreInhibited=</varname></term>
<term><varname>RebootKeyIgnoreInhibited=</varname></term>
<listitem><para>Controls whether actions that <command>systemd-logind</command>
takes when the power and sleep keys and the lid switch are triggered are subject
to high-level inhibitor locks ("shutdown", "sleep", "idle"). Low level inhibitor
takes when the power, reboot and sleep keys and the lid switch are triggered are subject
to high-level inhibitor locks ("shutdown", "reboot", "sleep", "idle"). Low level inhibitor
locks (<literal>handle-power-key</literal>, <literal>handle-suspend-key</literal>,
<literal>handle-hibernate-key</literal>, <literal>handle-lid-switch</literal>),
<literal>handle-hibernate-key</literal>, <literal>handle-lid-switch</literal>,
<literal>handle-reboot-key</literal>),
are always honored, irrespective of this setting.</para>
<para>These settings take boolean arguments. If <literal>no</literal>, the
inhibitor locks taken by applications are respected. If <literal>yes</literal>,
"shutdown", "sleep", and "idle" inhibitor locks are ignored.
"shutdown", "reboot" "sleep", and "idle" inhibitor locks are ignored.
<varname>PowerKeyIgnoreInhibited=</varname>,
<varname>SuspendKeyIgnoreInhibited=</varname>, and
<varname>HibernateKeyIgnoreInhibited=</varname> default to <literal>no</literal>.
<varname>SuspendKeyIgnoreInhibited=</varname>,
<varname>HibernateKeyIgnoreInhibited=</varname> and
<varname>RebootKeyIgnoreInhibited=</varname> default to <literal>no</literal>.
<varname>LidSwitchIgnoreInhibited=</varname> defaults to <literal>yes</literal>.
This means that when <command>systemd-logind</command> is handling events by
itself (no low level inhibitor locks are taken by another application), the lid

View File

@ -35,7 +35,7 @@
<funcprototype>
<funcdef>int <function>sd_machine_get_ifindices</function></funcdef>
<paramdef>const char* <parameter>machine</parameter></paramdef>
<paramdef>int **<parameter>ifindices</parameter></paramdef>
<paramdef>int **<parameter>ret_ifindices</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@ -53,21 +53,22 @@
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
<para><function>sd_machine_get_ifindices()</function> may be used
to determine the numeric indices of the network interfaces on the
host that are pointing towards the specified locally running
virtual machine or container that is registered with
<para><function>sd_machine_get_ifindices()</function> may be used to determine the numeric indices of the
network interfaces on the host that are pointing towards the specified locally running virtual machine or
container. The vm or container must be registered with
<citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
The returned array needs to be freed with the libc <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
The output parameter <parameter>ret_ifindices</parameter> may be passed as <constant>NULL</constant> when
the output value is not needed. The returned array needs to be freed with the libc <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after
use.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, these calls return 0 or a positive integer. On failure, these calls return a negative
errno-style error code.</para>
<para>On success, these functions return a non-negative integer.
<function>sd_machine_get_ifindices()</function> returns the number of the relevant network interfaces.
On failure, these calls return a negative errno-style error code.</para>
<refsect2>
<title>Errors</title>

View File

@ -38,9 +38,9 @@
<funcprototype>
<funcdef>int <function>sd_seat_get_sessions</function></funcdef>
<paramdef>const char *<parameter>seat</parameter></paramdef>
<paramdef>char ***<parameter>sessions</parameter></paramdef>
<paramdef>uid_t **<parameter>uid</parameter></paramdef>
<paramdef>unsigned int *<parameter>n_uids</parameter></paramdef>
<paramdef>char ***<parameter>ret_sessions</parameter></paramdef>
<paramdef>uid_t **<parameter>ret_uids</parameter></paramdef>
<paramdef>unsigned int *<parameter>ret_n_uids</parameter></paramdef>
</funcprototype>
<funcprototype>
@ -68,21 +68,16 @@
<citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
<para><function>sd_seat_get_sessions()</function> may be used to
determine all sessions on the specified seat. Returns two arrays,
one (<constant>NULL</constant> terminated) with the session
identifiers of the sessions and one with the user identifiers of
the Unix users the sessions belong to. An additional parameter may
be used to return the number of entries in the latter array. This
value is the same the return value, if the latter is nonnegative.
The two arrays and the last parameter may be passed as
<constant>NULL</constant> in case these values need not to be
determined. The arrays and the strings referenced by them need to
be freed with the libc
<citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use. Note that instead of an empty array
<constant>NULL</constant> may be returned and should be considered
equivalent to an empty array.</para>
<para><function>sd_seat_get_sessions()</function> may be used to determine all sessions on the specified
seat. Returns two arrays, one (<constant>NULL</constant> terminated) with the session identifiers of the
sessions and one with the user identifiers of the Unix users the sessions belong to. An additional
parameter may be used to return the number of entries in the latter array. This value is the same as the
return value if the return value is nonnegative. The output parameters may be passed as
<constant>NULL</constant> in case these output values are not needed. The arrays and the strings
referenced by them need to be freed with the libc <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after
use. Note that instead of an empty array <constant>NULL</constant> may be returned and should be
considered equivalent to an empty array.</para>
<para><function>sd_seat_can_tty()</function> may be used to
determine whether a specific seat provides TTY functionality, i.e.

View File

@ -652,14 +652,13 @@ int cg_remove_xattr(const char *controller, const char *path, const char *name)
return 0;
}
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
int cg_pid_get_path(const char *controller, pid_t pid, char **ret_path) {
_cleanup_fclose_ FILE *f = NULL;
const char *fs, *controller_str;
int unified, r;
size_t cs = 0;
assert(path);
assert(pid >= 0);
assert(ret_path);
if (controller) {
if (!cg_controller_is_valid(controller))
@ -675,8 +674,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
else
controller_str = controller;
cs = strlen(controller_str);
}
fs = procfs_file_alloca(pid, "cgroup");
@ -688,13 +685,13 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
for (;;) {
_cleanup_free_ char *line = NULL;
char *e, *p;
char *e;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0)
break;
return -ENODATA;
if (unified) {
e = startswith(line, "0:");
@ -706,9 +703,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
continue;
} else {
char *l;
size_t k;
const char *word, *state;
bool found = false;
l = strchr(line, ':');
if (!l)
@ -718,31 +712,27 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
e = strchr(l, ':');
if (!e)
continue;
*e = 0;
FOREACH_WORD_SEPARATOR(word, k, l, ",", state)
if (k == cs && memcmp(word, controller_str, cs) == 0) {
found = true;
break;
}
if (!found)
r = string_contains_word(l, ",", controller_str);
if (r < 0)
return r;
if (r == 0)
continue;
}
p = strdup(e + 1);
if (!p)
char *path = strdup(e + 1);
if (!path)
return -ENOMEM;
/* Truncate suffix indicating the process is a zombie */
e = endswith(p, " (deleted)");
e = endswith(path, " (deleted)");
if (e)
*e = 0;
*path = p;
*ret_path = path;
return 0;
}
return -ENODATA;
}
int cg_install_release_agent(const char *controller, const char *agent) {

View File

@ -687,7 +687,7 @@ char **replace_env_argv(char **argv, char **env) {
if (e) {
int r;
r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);

View File

@ -14,6 +14,7 @@
#include "log.h"
#include "macro.h"
#include "string-util.h"
#include "strv.h"
#include "utf8.h"
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {

View File

@ -538,6 +538,9 @@ static inline int __coverity_check_and_return__(int condition) {
(y) = (_t); \
} while (false)
#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */
#define FOREACH_POINTER(p, x, ...) \
for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \

View File

@ -617,40 +617,62 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b
return false;
}
int fd_inc_sndbuf(int fd, size_t n) {
int fd_set_sndbuf(int fd, size_t n, bool increase) {
int r, value;
socklen_t l = sizeof(value);
if (n > INT_MAX)
return -ERANGE;
r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
return 0;
/* If we have the privileges we will ignore the kernel limit. */
/* First, try to set the buffer size with SO_SNDBUF. */
r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n);
if (r < 0)
return r;
if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n) < 0) {
r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n);
if (r < 0)
return r;
}
/* SO_SNDBUF above may set to the kernel limit, instead of the requested size.
* So, we need to check the actual buffer size here. */
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;
/* If we have the privileges we will ignore the kernel limit. */
r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n);
if (r < 0)
return r;
return 1;
}
int fd_inc_rcvbuf(int fd, size_t n) {
int fd_set_rcvbuf(int fd, size_t n, bool increase) {
int r, value;
socklen_t l = sizeof(value);
if (n > INT_MAX)
return -ERANGE;
r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
return 0;
/* If we have the privileges we will ignore the kernel limit. */
/* First, try to set the buffer size with SO_RCVBUF. */
r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n);
if (r < 0)
return r;
if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n) < 0) {
r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n);
if (r < 0)
return r;
}
/* SO_RCVBUF above may set to the kernel limit, instead of the requested size.
* So, we need to check the actual buffer size here. */
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;
/* If we have the privileges we will ignore the kernel limit. */
r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n);
if (r < 0)
return r;
return 1;
}

View File

@ -118,8 +118,14 @@ int netlink_family_from_string(const char *s) _pure_;
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b);
int fd_inc_sndbuf(int fd, size_t n);
int fd_inc_rcvbuf(int fd, size_t n);
int fd_set_sndbuf(int fd, size_t n, bool increase);
static inline int fd_inc_sndbuf(int fd, size_t n) {
return fd_set_sndbuf(fd, n, true);
}
int fd_set_rcvbuf(int fd, size_t n, bool increase);
static inline int fd_inc_rcvbuf(int fd, size_t n) {
return fd_set_rcvbuf(fd, n, true);
}
int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);

View File

@ -8,12 +8,14 @@
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
#include "fileio.h"
#include "gunicode.h"
#include "locale-util.h"
#include "macro.h"
#include "memory-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "utf8.h"
#include "util.h"
@ -110,83 +112,6 @@ char* first_word(const char *s, const char *word) {
return (char*) p;
}
static size_t strcspn_escaped(const char *s, const char *reject) {
bool escaped = false;
int n;
for (n = 0; s[n] != '\0'; n++) {
if (escaped)
escaped = false;
else if (s[n] == '\\')
escaped = true;
else if (strchr(reject, s[n]))
break;
}
return n;
}
/* Split a string into words. */
const char* split(
const char **state,
size_t *l,
const char *separator,
SplitFlags flags) {
const char *current;
assert(state);
assert(l);
if (!separator)
separator = WHITESPACE;
current = *state;
if (*current == '\0') /* already at the end? */
return NULL;
current += strspn(current, separator); /* skip leading separators */
if (*current == '\0') { /* at the end now? */
*state = current;
return NULL;
}
if (FLAGS_SET(flags, SPLIT_QUOTES)) {
if (strchr(QUOTES, *current)) {
/* We are looking at a quote */
*l = strcspn_escaped(current + 1, CHAR_TO_STR(*current));
if (current[*l + 1] != *current ||
(current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) {
/* right quote missing or garbage at the end */
if (FLAGS_SET(flags, SPLIT_RELAX)) {
*state = current + *l + 1 + (current[*l + 1] != '\0');
return current + 1;
}
*state = current;
return NULL;
}
*state = current++ + *l + 2;
} else {
/* We are looking at a something that is not a quote */
*l = strcspn_escaped(current, separator);
if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) {
/* unfinished escape */
*state = current;
return NULL;
}
*state = current + *l;
}
} else {
*l = strcspn(current, separator);
*state = current + *l;
}
return current;
}
char *strnappend(const char *s, const char *suffix, size_t b) {
size_t a;
char *r;
@ -1207,3 +1132,30 @@ int string_extract_line(const char *s, size_t i, char **ret) {
c++;
}
}
int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) {
/* In the default mode with no separators specified, we split on whitespace and
* don't coalesce separators. */
const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
const char *found = NULL;
for (const char *p = string;;) {
_cleanup_free_ char *w = NULL;
int r;
r = extract_first_word(&p, &w, separators, flags);
if (r < 0)
return r;
if (r == 0)
break;
found = strv_find(words, w);
if (found)
break;
}
if (ret_word)
*ret_word = found;
return !!found;
}

View File

@ -108,24 +108,6 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
typedef enum SplitFlags {
SPLIT_QUOTES = 0x01 << 0,
SPLIT_RELAX = 0x01 << 1,
} SplitFlags;
/* Smelly. Do not use this anymore. Use extract_first_word() instead! */
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
/* Similar, don't use this anymore */
#define FOREACH_WORD(word, length, s, state) \
_FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
_FOREACH_WORD(word, length, s, separator, 0, state)
#define _FOREACH_WORD(word, length, s, separator, flags, state) \
for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
char *strnappend(const char *s, const char *suffix, size_t length);
char *strjoin_real(const char *x, ...) _sentinel_;
@ -280,3 +262,8 @@ char* string_erase(char *x);
int string_truncate_lines(const char *s, size_t n_lines, char **ret);
int string_extract_line(const char *s, size_t i, char **ret);
int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word);
static inline int string_contains_word(const char *string, const char *separators, const char *word) {
return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
}

View File

@ -256,44 +256,6 @@ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
return 0;
}
char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
const char *word, *state;
size_t l;
size_t n, i;
char **r;
assert(s);
if (!separator)
separator = WHITESPACE;
s += strspn(s, separator);
if (isempty(s))
return new0(char*, 1);
n = 0;
_FOREACH_WORD(word, l, s, separator, flags, state)
n++;
r = new(char*, n+1);
if (!r)
return NULL;
i = 0;
_FOREACH_WORD(word, l, s, separator, flags, state) {
r[i] = strndup(word, l);
if (!r[i]) {
strv_free(r);
return NULL;
}
i++;
}
r[i] = NULL;
return r;
}
char **strv_split_newlines(const char *s) {
char **l;
size_t n;
@ -317,7 +279,7 @@ char **strv_split_newlines(const char *s) {
return l;
}
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
_cleanup_strv_free_ char **l = NULL;
size_t n = 0, allocated = 0;
int r;

View File

@ -72,13 +72,19 @@ static inline bool strv_isempty(char * const *l) {
return !l || !*l;
}
char **strv_split_full(const char *s, const char *separator, SplitFlags flags);
static inline char **strv_split(const char *s, const char *separator) {
return strv_split_full(s, separator, 0);
}
char **strv_split_newlines(const char *s);
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags);
static inline char **strv_split(const char *s, const char *separators) {
char **ret;
int r;
r = strv_split_full(&ret, s, separators, 0);
if (r < 0)
return NULL;
return ret;
}
/* Given a string containing white-space separated tuples of words themselves separated by ':',
* returns a vector of strings. If the second element in a tuple is missing, the corresponding
@ -123,10 +129,6 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
char **strv_sort(char **l);
void strv_print(char * const *l);
#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
#define strv_from_stdarg_alloca(first) \
({ \
char **_l; \

View File

@ -899,11 +899,11 @@ void emit_bpf_firewall_warning(Unit *u) {
if (!warned) {
bool quiet = bpf_firewall_unsupported_reason == -EPERM && detect_container();
log_unit_full(u, quiet ? LOG_DEBUG : LOG_WARNING, bpf_firewall_unsupported_reason,
"unit configures an IP firewall, but %s.\n"
"(This warning is only shown for the first unit using IP firewalling.)",
getuid() != 0 ? "not running as root" :
"the local system does not support BPF/cgroup firewalling");
log_unit_full_errno(u, quiet ? LOG_DEBUG : LOG_WARNING, bpf_firewall_unsupported_reason,
"unit configures an IP firewall, but %s.\n"
"(This warning is only shown for the first unit using IP firewalling.)",
getuid() != 0 ? "not running as root" :
"the local system does not support BPF/cgroup firewalling");
warned = true;
}
}

View File

@ -78,8 +78,8 @@ static int set_attribute_and_warn(Unit *u, const char *controller, const char *a
r = cg_set_attribute(controller, u->cgroup_path, attribute, value);
if (r < 0)
log_unit_full(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%.*s': %m",
strna(attribute), isempty(u->cgroup_path) ? "/" : u->cgroup_path, (int) strcspn(value, NEWLINE), value);
log_unit_full_errno(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%.*s': %m",
strna(attribute), isempty(u->cgroup_path) ? "/" : u->cgroup_path, (int) strcspn(value, NEWLINE), value);
return r;
}
@ -724,7 +724,7 @@ static usec_t cgroup_cpu_adjust_period_and_log(Unit *u, usec_t period, usec_t qu
if (new_period != period) {
char v[FORMAT_TIMESPAN_MAX];
log_unit_full(u, u->warned_clamping_cpu_quota_period ? LOG_DEBUG : LOG_WARNING, 0,
log_unit_full(u, u->warned_clamping_cpu_quota_period ? LOG_DEBUG : LOG_WARNING,
"Clamping CPU interval for cpu.max: period is now %s",
format_timespan(v, sizeof(v), new_period, 1));
u->warned_clamping_cpu_quota_period = true;
@ -986,8 +986,8 @@ static int cgroup_apply_devices(Unit *u) {
else
r = cg_set_attribute("devices", path, "devices.allow", "a");
if (r < 0)
log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to reset devices.allow/devices.deny: %m");
log_unit_full_errno(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to reset devices.allow/devices.deny: %m");
}
bool allow_list_static = policy == CGROUP_DEVICE_POLICY_CLOSED ||
@ -1351,8 +1351,8 @@ static void cgroup_context_apply(
else
r = 0;
if (r < 0)
log_unit_full(u, LOG_LEVEL_CGROUP_WRITE(r), r,
"Failed to write to tasks limit sysctls: %m");
log_unit_full_errno(u, LOG_LEVEL_CGROUP_WRITE(r), r,
"Failed to write to tasks limit sysctls: %m");
}
/* The attribute itself is not available on the host root cgroup, and in the container case we want to
@ -2435,7 +2435,7 @@ void unit_prune_cgroup(Unit *u) {
* the containing slice is stopped. So even if we failed now, this unit shouldn't assume
* that the cgroup is still realized the next time it is started. Do not return early
* on error, continue cleanup. */
log_unit_full(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
log_unit_full_errno(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
if (is_root_slice)
return;

View File

@ -4255,8 +4255,16 @@ static int exec_child(
}
}
/* This is done before enforce_user, but ambient set
* does not survive over setresuid() if keep_caps is not set. */
/* Ambient capabilities are cleared during setresuid() (in enforce_user()) even with
* keep-caps set.
* To be able to raise the ambient capabilities after setresuid() they have to be
* added to the inherited set and keep caps has to be set (done in enforce_user()).
* After setresuid() the ambient capabilities can be raised as they are present in
* the permitted and inhertiable set. However it is possible that someone wants to
* set ambient capabilities without changing the user, so we also set the ambient
* capabilities here.
* The requested ambient capabilities are raised in the inheritable set if the
* second argument is true. */
if (!needs_ambient_hack) {
r = capability_ambient_set_apply(context->capability_ambient_set, true);
if (r < 0) {
@ -4282,21 +4290,12 @@ static int exec_child(
if (!needs_ambient_hack &&
context->capability_ambient_set != 0) {
/* Fix the ambient capabilities after user change. */
/* Raise the ambient capabilities after user change. */
r = capability_ambient_set_apply(context->capability_ambient_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return log_unit_error_errno(unit, r, "Failed to apply ambient capabilities (after UID change): %m");
}
/* If we were asked to change user and ambient capabilities
* were requested, we had to add keep-caps to the securebits
* so that we would maintain the inherited capability set
* through the setresuid(). Make sure that the bit is added
* also to the context secure_bits so that we don't try to
* drop the bit away next. */
secure_bits |= 1<<SECURE_KEEP_CAPS;
}
}
}

View File

@ -4438,15 +4438,13 @@ int config_parse_set_status(
void *data,
void *userdata) {
size_t l;
const char *word, *state;
int r;
ExitStatusSet *status_set = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
assert(status_set);
/* Empty assignment resets the list */
if (isempty(rvalue)) {
@ -4454,25 +4452,26 @@ int config_parse_set_status(
return 0;
}
FOREACH_WORD(word, l, rvalue, state) {
_cleanup_free_ char *temp;
for (const char *p = rvalue;;) {
_cleanup_free_ char *word = NULL;
Bitmap *bitmap;
temp = strndup(word, l);
if (!temp)
return log_oom();
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse %s: %m", lvalue);
if (r == 0)
return 0;
/* We need to call exit_status_from_string() first, because we want
* to parse numbers as exit statuses, not signals. */
r = exit_status_from_string(temp);
r = exit_status_from_string(word);
if (r >= 0) {
assert(r >= 0 && r < 256);
bitmap = &status_set->status;
} else {
r = signal_from_string(temp);
if (r <= 0) {
r = signal_from_string(word);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Failed to parse value, ignoring: %s", word);
continue;
@ -4484,10 +4483,6 @@ int config_parse_set_status(
if (r < 0)
return log_error_errno(r, "Failed to set signal or status %s: %m", word);
}
if (!isempty(state))
log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
int config_parse_namespace_path_strv(

View File

@ -918,17 +918,17 @@ static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) {
* good */
if (pid == getpid_cached() || pid == 1) {
log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" is the manager, refusing.", pid);
log_unit_full(UNIT(s), prio, "New main PID "PID_FMT" is the manager, refusing.", pid);
return -EPERM;
}
if (pid == s->control_pid) {
log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" is the control process, refusing.", pid);
log_unit_full(UNIT(s), prio, "New main PID "PID_FMT" is the control process, refusing.", pid);
return -EPERM;
}
if (!pid_is_alive(pid)) {
log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" does not exist or is a zombie.", pid);
log_unit_full(UNIT(s), prio, "New main PID "PID_FMT" does not exist or is a zombie.", pid);
return -ESRCH;
}
@ -958,16 +958,16 @@ static int service_load_pid_file(Service *s, bool may_warn) {
r = chase_symlinks(s->pid_file, NULL, CHASE_SAFE, NULL, &fd);
if (r == -ENOLINK) {
log_unit_full(UNIT(s), LOG_DEBUG, r,
"Potentially unsafe symlink chain, will now retry with relaxed checks: %s", s->pid_file);
log_unit_debug_errno(UNIT(s), r,
"Potentially unsafe symlink chain, will now retry with relaxed checks: %s", s->pid_file);
questionable_pid_file = true;
r = chase_symlinks(s->pid_file, NULL, 0, NULL, &fd);
}
if (r < 0)
return log_unit_full(UNIT(s), prio, fd,
"Can't open PID file %s (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state));
return log_unit_full_errno(UNIT(s), prio, fd,
"Can't open PID file %s (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state));
/* Let's read the PID file now that we chased it down. But we need to convert the O_PATH fd
* chase_symlinks() returned us into a proper fd first. */
@ -980,7 +980,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
r = parse_pid(k, &pid);
if (r < 0)
return log_unit_full(UNIT(s), prio, r, "Failed to parse PID from file %s: %m", s->pid_file);
return log_unit_full_errno(UNIT(s), prio, r, "Failed to parse PID from file %s: %m", s->pid_file);
if (s->main_pid_known && pid == s->main_pid)
return 0;

View File

@ -381,8 +381,10 @@ static int slice_freezer_action(Unit *s, FreezerAction action) {
assert(s);
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
if (!slice_freezer_action_supported_by_children(s))
return log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
if (!slice_freezer_action_supported_by_children(s)) {
log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
return 0;
}
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE]) {
if (UNIT_DEREF(member->slice) != s)

View File

@ -1058,20 +1058,15 @@ static void socket_apply_socket_options(Socket *s, int fd) {
}
if (s->receive_buffer > 0) {
/* We first try with SO_RCVBUFFORCE, in case we have the perms for that */
if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, s->receive_buffer) < 0) {
r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, s->receive_buffer);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "SO_RCVBUF failed: %m");
}
r = fd_set_rcvbuf(fd, s->receive_buffer, false);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "SO_RCVBUF/SO_RCVBUFFORCE failed: %m");
}
if (s->send_buffer > 0) {
if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, s->send_buffer) < 0) {
r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, s->send_buffer);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "SO_SNDBUF failed: %m");
}
r = fd_set_sndbuf(fd, s->receive_buffer, false);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "SO_SNDBUF/SO_SNDBUFFORCE failed: %m");
}
if (s->mark >= 0) {

View File

@ -284,8 +284,8 @@ static int swap_load_devnode(Swap *s) {
r = device_new_from_stat_rdev(&d, &st);
if (r < 0) {
log_unit_full(UNIT(s), r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to allocate device for swap %s: %m", s->what);
log_unit_full_errno(UNIT(s), r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to allocate device for swap %s: %m", s->what);
return 0;
}

View File

@ -998,10 +998,9 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, following) {
r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_full(dep,
r == -ERFKILL ? LOG_INFO : LOG_WARNING,
r, "Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
log_unit_full_errno(dep, r == -ERFKILL ? LOG_INFO : LOG_WARNING, r,
"Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
sd_bus_error_free(e);
}
}
@ -1035,10 +1034,10 @@ int transaction_add_job_and_dependencies(
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
/* unit masked, job type not applicable and unit not found are not considered as errors. */
log_unit_full(dep,
IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
r, "Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
log_unit_full_errno(dep,
IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
r, "Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
sd_bus_error_free(e);
}
}

View File

@ -896,7 +896,7 @@ int unit_thaw_vtable_common(Unit *u);
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
#define log_unit_full_errno(unit, level, error, ...) \
({ \
const Unit *_u = (unit); \
(log_get_max_level() < LOG_PRI(level)) ? -ERRNO_VALUE(error) : \
@ -904,17 +904,19 @@ int unit_thaw_vtable_common(Unit *u);
log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
})
#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, 0, ##__VA_ARGS__)
#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, 0, ##__VA_ARGS__)
#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, 0, ##__VA_ARGS__)
#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, 0, ##__VA_ARGS__)
#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, 0, ##__VA_ARGS__)
#define log_unit_full(unit, level, ...) (void) log_unit_full_errno(unit, level, 0, __VA_ARGS__)
#define log_unit_debug_errno(unit, error, ...) log_unit_full(unit, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_unit_info_errno(unit, error, ...) log_unit_full(unit, LOG_INFO, error, ##__VA_ARGS__)
#define log_unit_notice_errno(unit, error, ...) log_unit_full(unit, LOG_NOTICE, error, ##__VA_ARGS__)
#define log_unit_warning_errno(unit, error, ...) log_unit_full(unit, LOG_WARNING, error, ##__VA_ARGS__)
#define log_unit_error_errno(unit, error, ...) log_unit_full(unit, LOG_ERR, error, ##__VA_ARGS__)
#define log_unit_debug(unit, ...) log_unit_full_errno(unit, LOG_DEBUG, 0, __VA_ARGS__)
#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, __VA_ARGS__)
#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, __VA_ARGS__)
#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, __VA_ARGS__)
#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, __VA_ARGS__)
#define log_unit_debug_errno(unit, error, ...) log_unit_full_errno(unit, LOG_DEBUG, error, __VA_ARGS__)
#define log_unit_info_errno(unit, error, ...) log_unit_full_errno(unit, LOG_INFO, error, __VA_ARGS__)
#define log_unit_notice_errno(unit, error, ...) log_unit_full_errno(unit, LOG_NOTICE, error, __VA_ARGS__)
#define log_unit_warning_errno(unit, error, ...) log_unit_full_errno(unit, LOG_WARNING, error, __VA_ARGS__)
#define log_unit_error_errno(unit, error, ...) log_unit_full_errno(unit, LOG_ERR, error, __VA_ARGS__)
#define LOG_UNIT_MESSAGE(unit, fmt, ...) "MESSAGE=%s: " fmt, (unit)->id, ##__VA_ARGS__
#define LOG_UNIT_ID(unit) (unit)->manager->unit_log_format_string, (unit)->id

View File

@ -288,19 +288,19 @@ static int parse_one_option(const char *option) {
}
static int parse_options(const char *options) {
const char *word, *state;
size_t l;
int r;
assert(options);
FOREACH_WORD_SEPARATOR(word, l, options, ",", state) {
_cleanup_free_ char *o;
for (;;) {
_cleanup_free_ char *word = NULL;
int r;
o = strndup(word, l);
if (!o)
return -ENOMEM;
r = parse_one_option(o);
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_debug_errno(r, "Failed to parse options: %m");
if (r == 0)
break;
r = parse_one_option(word);
if (r < 0)
return r;
}

View File

@ -541,28 +541,33 @@ static int help(void) {
}
static int parse_flags(const char *flag_str, int flags) {
const char *word, *state;
size_t l;
for (;;) {
_cleanup_free_ char *word = NULL;
int r;
FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) {
if (strneq("masked", word, l))
r = extract_first_word(&flag_str, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
return flags;
if (streq(word, "masked"))
flags |= SHOW_MASKED;
else if (strneq ("equivalent", word, l))
else if (streq(word, "equivalent"))
flags |= SHOW_EQUIVALENT;
else if (strneq("redirected", word, l))
else if (streq(word, "redirected"))
flags |= SHOW_REDIRECTED;
else if (strneq("overridden", word, l))
else if (streq(word, "overridden"))
flags |= SHOW_OVERRIDDEN;
else if (strneq("unchanged", word, l))
else if (streq(word, "unchanged"))
flags |= SHOW_UNCHANGED;
else if (strneq("extended", word, l))
else if (streq(word, "extended"))
flags |= SHOW_EXTENDED;
else if (strneq("default", word, l))
else if (streq(word, "default"))
flags |= SHOW_DEFAULTS;
else
return -EINVAL;
}
return flags;
}
static int parse_argv(int argc, char *argv[]) {

View File

@ -99,89 +99,85 @@ static int verify_tty(const char *name) {
return 0;
}
static int run_container(void) {
_cleanup_free_ char *container_ttys = NULL;
int r;
log_debug("Automatically adding console shell.");
r = add_symlink("console-getty.service", "console-getty.service");
if (r < 0)
return r;
/* When $container_ttys is set for PID 1, spawn gettys on all ptys named therein.
* Note that despite the variable name we only support ptys here. */
(void) getenv_for_pid(1, "container_ttys", &container_ttys);
for (const char *p = container_ttys;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse $container_ttys: %m");
if (r == 0)
return 0;
const char *tty = word;
/* First strip off /dev/ if it is specified */
tty = path_startswith(tty, "/dev/") ?: tty;
/* Then, make sure it's actually a pty */
tty = path_startswith(tty, "pts/");
if (!tty)
continue;
r = add_container_getty(tty);
if (r < 0)
return r;
}
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
_cleanup_free_ char *active = NULL;
const char *j;
int r;
assert_se(arg_dest = dest);
if (detect_container() > 0) {
_cleanup_free_ char *container_ttys = NULL;
if (detect_container() > 0)
/* Add console shell and look at $container_ttys, but don't do add any
* further magic if we are in a container. */
return run_container();
log_debug("Automatically adding console shell.");
/* Automatically add in a serial getty on all active kernel consoles */
_cleanup_free_ char *active = NULL;
(void) read_one_line_file("/sys/class/tty/console/active", &active);
for (const char *p = active;;) {
_cleanup_free_ char *tty = NULL;
r = add_symlink("console-getty.service", "console-getty.service");
r = extract_first_word(&p, &tty, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse /sys/class/tty/console/active: %m");
if (r == 0)
break;
/* We assume that gettys on virtual terminals are started via manual configuration and do
* this magic only for non-VC terminals. */
if (isempty(tty) || tty_is_vc(tty))
continue;
if (verify_tty(tty) < 0)
continue;
r = add_serial_getty(tty);
if (r < 0)
return r;
/* When $container_ttys is set for PID 1, spawn
* gettys on all ptys named therein. Note that despite
* the variable name we only support ptys here. */
r = getenv_for_pid(1, "container_ttys", &container_ttys);
if (r > 0) {
const char *word, *state;
size_t l;
FOREACH_WORD(word, l, container_ttys, state) {
const char *t;
char tty[l + 1];
memcpy(tty, word, l);
tty[l] = 0;
/* First strip off /dev/ if it is specified */
t = path_startswith(tty, "/dev/");
if (!t)
t = tty;
/* Then, make sure it's actually a pty */
t = path_startswith(t, "pts/");
if (!t)
continue;
r = add_container_getty(t);
if (r < 0)
return r;
}
}
/* Don't add any further magic if we are in a container */
return 0;
}
if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
const char *word, *state;
size_t l;
/* Automatically add in a serial getty on all active
* kernel consoles */
FOREACH_WORD(word, l, active, state) {
_cleanup_free_ char *tty = NULL;
tty = strndup(word, l);
if (!tty)
return log_oom();
/* We assume that gettys on virtual terminals are
* started via manual configuration and do this magic
* only for non-VC terminals. */
if (isempty(tty) || tty_is_vc(tty))
continue;
if (verify_tty(tty) < 0)
continue;
r = add_serial_getty(tty);
if (r < 0)
return r;
}
}
/* Automatically add in a serial getty on the first
* virtualizer console */
const char *j;
FOREACH_STRING(j,
"hvc0",
"xvc0",

View File

@ -124,7 +124,7 @@ static int spawn_getter(const char *getter) {
_cleanup_strv_free_ char **words = NULL;
assert(getter);
r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&words, getter, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to split getter option: %m");

View File

@ -950,74 +950,69 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
}
_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
const char *word, *state;
size_t l;
unsigned long long seqnum, monotonic, realtime, xor_hash;
bool
seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
bool seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
sd_id128_t seqnum_id, boot_id;
int r;
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
assert_return(!isempty(cursor), -EINVAL);
FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
char *item;
int k = 0;
for (const char *p = cursor;;) {
_cleanup_free_ char *word = NULL;
if (l < 2 || word[1] != '=')
r = extract_first_word(&p, &word, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
break;
if (word[0] == '\0' || word[1] != '=')
return -EINVAL;
item = strndup(word, l);
if (!item)
return -ENOMEM;
switch (word[0]) {
case 's':
seqnum_id_set = true;
k = sd_id128_from_string(item+2, &seqnum_id);
r = sd_id128_from_string(word + 2, &seqnum_id);
if (r < 0)
return r;
break;
case 'i':
seqnum_set = true;
if (sscanf(item+2, "%llx", &seqnum) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &seqnum) != 1)
return -EINVAL;
break;
case 'b':
boot_id_set = true;
k = sd_id128_from_string(item+2, &boot_id);
r = sd_id128_from_string(word + 2, &boot_id);
break;
case 'm':
monotonic_set = true;
if (sscanf(item+2, "%llx", &monotonic) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &monotonic) != 1)
return -EINVAL;
break;
case 't':
realtime_set = true;
if (sscanf(item+2, "%llx", &realtime) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &realtime) != 1)
return -EINVAL;
break;
case 'x':
xor_hash_set = true;
if (sscanf(item+2, "%llx", &xor_hash) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &xor_hash) != 1)
return -EINVAL;
break;
}
free(item);
if (k < 0)
return k;
}
if ((!seqnum_set || !seqnum_id_set) &&

View File

@ -101,7 +101,7 @@ _public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
e = getenv("LISTEN_FDNAMES");
if (e) {
n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
n_names = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (n_names < 0) {
unsetenv_all(unset_environment);
return n_names;

View File

@ -91,18 +91,9 @@ int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor
}
_public_ int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size) {
int r, n = (int) size;
assert_return(m, -EINVAL);
assert_return((size_t) n == size, -EINVAL);
if (setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUFFORCE, n) < 0) {
r = setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUF, n);
if (r < 0)
return r;
}
return 0;
return fd_set_rcvbuf(m->sock, size, false);
}
int device_monitor_disconnect(sd_device_monitor *m) {

View File

@ -316,34 +316,33 @@ static int device_amend(sd_device *device, const char *key, const char *value) {
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to set SEQNUM to '%s': %m", value);
} else if (streq(key, "DEVLINKS")) {
const char *word, *state;
size_t l;
for (const char *p = value;;) {
_cleanup_free_ char *word = NULL;
FOREACH_WORD(word, l, value, state) {
char devlink[l + 1];
strncpy(devlink, word, l);
devlink[l] = '\0';
r = device_add_devlink(device, devlink);
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink);
return r;
if (r == 0)
break;
r = device_add_devlink(device, word);
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", word);
}
} else if (STR_IN_SET(key, "TAGS", "CURRENT_TAGS")) {
const char *word, *state;
size_t l;
for (const char *p = value;;) {
_cleanup_free_ char *word = NULL;
FOREACH_WORD_SEPARATOR(word, l, value, ":", state) {
char tag[l + 1];
(void) strncpy(tag, word, l);
tag[l] = '\0';
r = device_add_tag(device, tag, streq(key, "CURRENT_TAGS"));
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag);
}
return r;
if (r == 0)
break;
r = device_add_tag(device, word, streq(key, "CURRENT_TAGS"));
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", word);
}
} else {
r = device_add_property_internal(device, key, value);
if (r < 0)

View File

@ -36,7 +36,7 @@
device; \
device = sd_device_enumerator_get_subsystem_next(enumerator))
#define log_device_full(device, level, error, ...) \
#define log_device_full_errno(device, level, error, ...) \
({ \
const char *_sysname = NULL; \
sd_device *_d = (device); \
@ -46,17 +46,19 @@
(void) sd_device_get_sysname(_d, &_sysname); \
log_object_internal(_level, _error, PROJECT_FILE, __LINE__, __func__, \
_sysname ? "DEVICE=" : NULL, _sysname, \
NULL, NULL, ##__VA_ARGS__); \
NULL, NULL, __VA_ARGS__); \
})
#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, 0, ##__VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, 0, ##__VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, 0, ##__VA_ARGS__)
#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, 0, ##__VA_ARGS__)
#define log_device_error(device, ...) log_device_full(device, LOG_ERR, 0, ##__VA_ARGS__)
#define log_device_full(device, level, ...) (void) log_device_full_errno(device, level, 0, __VA_ARGS__)
#define log_device_debug_errno(device, error, ...) log_device_full(device, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_device_info_errno(device, error, ...) log_device_full(device, LOG_INFO, error, ##__VA_ARGS__)
#define log_device_notice_errno(device, error, ...) log_device_full(device, LOG_NOTICE, error, ##__VA_ARGS__)
#define log_device_warning_errno(device, error, ...) log_device_full(device, LOG_WARNING, error, ##__VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full(device, LOG_ERR, error, ##__VA_ARGS__)
#define log_device_debug(device, ...) log_device_full_errno(device, LOG_DEBUG, 0, __VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__)
#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)

View File

@ -12,6 +12,7 @@
#include "dirent-util.h"
#include "env-file.h"
#include "escape.h"
#include "extract-word.h"
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
@ -22,6 +23,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@ -331,35 +333,29 @@ static int file_of_seat(const char *seat, char **_p) {
}
_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
_cleanup_free_ char *t = NULL, *s = NULL, *p = NULL;
size_t l;
_cleanup_free_ char *filename = NULL, *content = NULL;
int r;
const char *word, *variable, *state;
assert_return(uid_is_valid(uid), -EINVAL);
r = file_of_seat(seat, &p);
r = file_of_seat(seat, &filename);
if (r < 0)
return r;
variable = require_active ? "ACTIVE_UID" : "UIDS";
r = parse_env_file(NULL, p, variable, &s);
r = parse_env_file(NULL, filename,
require_active ? "ACTIVE_UID" : "UIDS",
&content);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
if (isempty(s))
if (isempty(content))
return 0;
if (asprintf(&t, UID_FMT, uid) < 0)
return -ENOMEM;
char t[DECIMAL_STR_MAX(uid_t)];
xsprintf(t, UID_FMT, uid);
FOREACH_WORD(word, l, s, state)
if (strneq(t, word, l))
return 1;
return 0;
return string_contains_word(content, ",", t);
}
static int uid_get_array(uid_t uid, const char *variable, char ***array) {
@ -382,7 +378,7 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) {
if (r < 0)
return r;
a = strv_split(s, " ");
a = strv_split(s, NULL);
if (!a)
return -ENOMEM;
@ -654,73 +650,70 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
return 0;
}
_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
_cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
_cleanup_strv_free_ char **a = NULL;
_cleanup_free_ uid_t *b = NULL;
unsigned n = 0;
_public_ int sd_seat_get_sessions(
const char *seat,
char ***ret_sessions,
uid_t **ret_uids,
unsigned *ret_n_uids) {
_cleanup_free_ char *fname = NULL, *session_line = NULL, *uid_line = NULL;
_cleanup_strv_free_ char **sessions = NULL;
_cleanup_free_ uid_t *uids = NULL;
unsigned n_sessions = 0;
int r;
r = file_of_seat(seat, &p);
r = file_of_seat(seat, &fname);
if (r < 0)
return r;
r = parse_env_file(NULL, p,
"SESSIONS", &s,
"UIDS", &t);
r = parse_env_file(NULL, fname,
"SESSIONS", &session_line,
"UIDS", &uid_line);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
return r;
if (s) {
a = strv_split(s, " ");
if (!a)
if (session_line) {
sessions = strv_split(session_line, NULL);
if (!sessions)
return -ENOMEM;
}
if (uids && t) {
const char *word, *state;
size_t l;
n_sessions = strv_length(sessions);
};
FOREACH_WORD(word, l, t, state)
n++;
if (ret_uids && uid_line) {
uids = new(uid_t, n_sessions);
if (!uids)
return -ENOMEM;
if (n > 0) {
unsigned i = 0;
size_t n = 0;
for (const char *p = uid_line;;) {
_cleanup_free_ char *word = NULL;
b = new(uid_t, n);
if (!b)
return -ENOMEM;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return r;
if (r == 0)
break;
FOREACH_WORD(word, l, t, state) {
_cleanup_free_ char *k = NULL;
k = strndup(word, l);
if (!k)
return -ENOMEM;
r = parse_uid(k, b + i);
if (r < 0)
return r;
i++;
}
r = parse_uid(word, &uids[n++]);
if (r < 0)
return r;
}
if (n != n_sessions)
return -EUCLEAN;
}
r = (int) strv_length(a);
if (ret_sessions)
*ret_sessions = TAKE_PTR(sessions);
if (ret_uids)
*ret_uids = TAKE_PTR(uids);
if (ret_n_uids)
*ret_n_uids = n_sessions;
if (sessions)
*sessions = TAKE_PTR(a);
if (uids)
*uids = TAKE_PTR(b);
if (n_uids)
*n_uids = n;
return r;
return n_sessions;
}
static int seat_get_can(const char *seat, const char *variable) {
@ -901,47 +894,52 @@ _public_ int sd_machine_get_class(const char *machine, char **class) {
return 0;
}
_public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) {
_cleanup_free_ char *netif = NULL;
size_t l, allocated = 0, nr = 0;
int *ni = NULL;
const char *p, *word, *state;
_public_ int sd_machine_get_ifindices(const char *machine, int **ret_ifindices) {
_cleanup_free_ char *netif_line = NULL;
const char *p;
int r;
assert_return(machine_name_is_valid(machine), -EINVAL);
assert_return(ifindices, -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(NULL, p, "NETIF", &netif);
r = parse_env_file(NULL, p, "NETIF", &netif_line);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
return r;
if (!netif) {
*ifindices = NULL;
if (!netif_line) {
*ret_ifindices = NULL;
return 0;
}
FOREACH_WORD(word, l, netif, state) {
char buf[l+1];
int ifi;
_cleanup_strv_free_ char **tt = strv_split(netif_line, NULL);
if (!tt)
return -ENOMEM;
*(char*) (mempcpy(buf, word, l)) = 0;
ifi = parse_ifindex(buf);
if (ifi < 0)
continue;
if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
free(ni);
size_t n = 0;
int *ifindices;
if (ret_ifindices) {
ifindices = new(int, strv_length(tt));
if (!ifindices)
return -ENOMEM;
}
ni[nr++] = ifi;
}
*ifindices = ni;
return nr;
for (size_t i = 0; tt[i]; i++) {
int ind;
ind = parse_ifindex(tt[i]);
if (ind < 0)
/* Return -EUCLEAN to distinguish from -EINVAL for invalid args */
return ind == -EINVAL ? -EUCLEAN : ind;
if (ret_ifindices)
ifindices[n] = ind;
n++;
}
if (ret_ifindices)
*ret_ifindices = ifindices;
return n;
}
static int MONITOR_TO_FD(sd_login_monitor *m) {

View File

@ -251,7 +251,7 @@ int x11_read_data(Context *c, sd_bus_message *m) {
if (in_section && first_word(l, "Option")) {
_cleanup_strv_free_ char **a = NULL;
r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return r;
@ -274,7 +274,7 @@ int x11_read_data(Context *c, sd_bus_message *m) {
} else if (!in_section && first_word(l, "Section")) {
_cleanup_strv_free_ char **a = NULL;
r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return -ENOMEM;
@ -489,7 +489,7 @@ static int read_next_mapping(const char* filename,
if (IN_SET(l[0], 0, '#'))
continue;
r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&b, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return r;

View File

@ -14,7 +14,7 @@
#include "string-util.h"
#include "util.h"
#define CONST_MAX4(a, b, c, d) CONST_MAX(CONST_MAX(a, b), CONST_MAX(c, d))
#define CONST_MAX5(a, b, c, d, e) CONST_MAX(CONST_MAX(a, b), CONST_MAX(CONST_MAX(c, d), e))
#define ULONG_BITS (sizeof(unsigned long)*8)
@ -158,7 +158,20 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
break;
/* The kernel is a bit confused here:
/* The kernel naming is a bit confusing here:
KEY_RESTART was probably introduced for media playback purposes, but
is now being predominantly used to indicate device reboot.
*/
case KEY_RESTART:
log_struct(LOG_INFO,
LOG_MESSAGE("Reboot key pressed."),
"MESSAGE_ID=" SD_MESSAGE_REBOOT_KEY_STR);
manager_handle_action(b->manager, INHIBIT_HANDLE_REBOOT_KEY, b->manager->handle_reboot_key, b->manager->reboot_key_ignore_inhibited, true);
break;
/* The kernel naming is a bit confusing here:
KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend"
KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
@ -231,7 +244,7 @@ static int button_suitable(int fd) {
return -errno;
if (bitset_get(types, EV_KEY)) {
unsigned long keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1];
unsigned long keys[CONST_MAX5(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND, KEY_RESTART)/ULONG_BITS+1];
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof keys), keys) < 0)
return -errno;
@ -239,7 +252,8 @@ static int button_suitable(int fd) {
if (bitset_get(keys, KEY_POWER) ||
bitset_get(keys, KEY_POWER2) ||
bitset_get(keys, KEY_SLEEP) ||
bitset_get(keys, KEY_SUSPEND))
bitset_get(keys, KEY_SUSPEND) ||
bitset_get(keys, KEY_RESTART))
return true;
}
@ -260,7 +274,7 @@ static int button_suitable(int fd) {
static int button_set_mask(const char *name, int fd) {
unsigned long
types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1] = {},
keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1] = {},
keys[CONST_MAX5(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND, KEY_RESTART)/ULONG_BITS+1] = {},
switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {};
struct input_mask mask;
@ -285,6 +299,7 @@ static int button_set_mask(const char *name, int fd) {
bitset_put(keys, KEY_POWER2);
bitset_put(keys, KEY_SLEEP);
bitset_put(keys, KEY_SUSPEND);
bitset_put(keys, KEY_RESTART);
mask = (struct input_mask) {
.type = EV_KEY,

View File

@ -43,10 +43,12 @@ void manager_reset_config(Manager *m) {
m->handle_lid_switch = HANDLE_SUSPEND;
m->handle_lid_switch_ep = _HANDLE_ACTION_INVALID;
m->handle_lid_switch_docked = HANDLE_IGNORE;
m->handle_reboot_key = HANDLE_REBOOT;
m->power_key_ignore_inhibited = false;
m->suspend_key_ignore_inhibited = false;
m->hibernate_key_ignore_inhibited = false;
m->lid_switch_ignore_inhibited = true;
m->reboot_key_ignore_inhibited = false;
m->holdoff_timeout_usec = 30 * USEC_PER_SEC;
@ -675,6 +677,8 @@ bool manager_all_buttons_ignored(Manager *m) {
return false;
if (m->handle_lid_switch_docked != HANDLE_IGNORE)
return false;
if (m->handle_reboot_key != HANDLE_IGNORE)
return false;
return true;
}

View File

@ -3240,6 +3240,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" :
w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
w == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" :
w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
"org.freedesktop.login1.inhibit-handle-lid-switch",
NULL,

View File

@ -30,10 +30,12 @@ Login.HandleHibernateKey, config_parse_handle_action, 0, offse
Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch)
Login.HandleLidSwitchExternalPower, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_ep)
Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked)
Login.HandleRebootKey, config_parse_handle_action, 0, offsetof(Manager, handle_reboot_key)
Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited)
Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited)
Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited)
Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited)
Login.RebootKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, reboot_key_ignore_inhibited)
Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec)
Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action)
Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec)

View File

@ -8,6 +8,7 @@
#include "alloc-util.h"
#include "env-file.h"
#include "errno-list.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@ -451,7 +452,15 @@ bool manager_is_inhibited(
}
const char *inhibit_what_to_string(InhibitWhat w) {
static thread_local char buffer[97];
static thread_local char buffer[STRLEN(
"shutdown:"
"sleep:"
"idle:"
"handle-power-key:"
"handle-suspend-key:"
"handle-hibernate-key:"
"handle-lid-switch:"
"handle-reboot-key")+1];
char *p;
if (w < 0 || w >= _INHIBIT_WHAT_MAX)
@ -472,6 +481,8 @@ const char *inhibit_what_to_string(InhibitWhat w) {
p = stpcpy(p, "handle-hibernate-key:");
if (w & INHIBIT_HANDLE_LID_SWITCH)
p = stpcpy(p, "handle-lid-switch:");
if (w & INHIBIT_HANDLE_REBOOT_KEY)
p = stpcpy(p, "handle-reboot-key:");
if (p > buffer)
*(p-1) = 0;
@ -481,31 +492,41 @@ const char *inhibit_what_to_string(InhibitWhat w) {
return buffer;
}
InhibitWhat inhibit_what_from_string(const char *s) {
int inhibit_what_from_string(const char *s) {
InhibitWhat what = 0;
const char *word, *state;
size_t l;
FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
if (l == 8 && strneq(word, "shutdown", l))
for (const char *p = s;;) {
_cleanup_free_ char *word = NULL;
int r;
/* A sanity check that our return values fit in an int */
assert_cc((int) _INHIBIT_WHAT_MAX == _INHIBIT_WHAT_MAX);
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
return what;
if (streq(word, "shutdown"))
what |= INHIBIT_SHUTDOWN;
else if (l == 5 && strneq(word, "sleep", l))
else if (streq(word, "sleep"))
what |= INHIBIT_SLEEP;
else if (l == 4 && strneq(word, "idle", l))
else if (streq(word, "idle"))
what |= INHIBIT_IDLE;
else if (l == 16 && strneq(word, "handle-power-key", l))
else if (streq(word, "handle-power-key"))
what |= INHIBIT_HANDLE_POWER_KEY;
else if (l == 18 && strneq(word, "handle-suspend-key", l))
else if (streq(word, "handle-suspend-key"))
what |= INHIBIT_HANDLE_SUSPEND_KEY;
else if (l == 20 && strneq(word, "handle-hibernate-key", l))
else if (streq(word, "handle-hibernate-key"))
what |= INHIBIT_HANDLE_HIBERNATE_KEY;
else if (l == 17 && strneq(word, "handle-lid-switch", l))
else if (streq(word, "handle-lid-switch"))
what |= INHIBIT_HANDLE_LID_SWITCH;
else if (streq(word, "handle-reboot-key"))
what |= INHIBIT_HANDLE_REBOOT_KEY;
else
return _INHIBIT_WHAT_INVALID;
}
return what;
}
static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {

View File

@ -11,7 +11,8 @@ typedef enum InhibitWhat {
INHIBIT_HANDLE_SUSPEND_KEY = 1 << 4,
INHIBIT_HANDLE_HIBERNATE_KEY = 1 << 5,
INHIBIT_HANDLE_LID_SWITCH = 1 << 6,
_INHIBIT_WHAT_MAX = 1 << 7,
INHIBIT_HANDLE_REBOOT_KEY = 1 << 7,
_INHIBIT_WHAT_MAX = 1 << 8,
_INHIBIT_WHAT_INVALID = -1
} InhibitWhat;
@ -66,7 +67,7 @@ InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm);
bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending);
const char *inhibit_what_to_string(InhibitWhat k);
InhibitWhat inhibit_what_from_string(const char *s);
int inhibit_what_from_string(const char *s);
const char *inhibit_mode_to_string(InhibitMode k);
InhibitMode inhibit_mode_from_string(const char *s);

View File

@ -18,16 +18,19 @@
#KillOnlyUsers=
#KillExcludeUsers=root
#InhibitDelayMaxSec=5
#UserStopDelaySec=10
#HandlePowerKey=poweroff
#HandleSuspendKey=suspend
#HandleHibernateKey=hibernate
#HandleLidSwitch=suspend
#HandleLidSwitchExternalPower=suspend
#HandleLidSwitchDocked=ignore
#HandleRebootKey=reboot
#PowerKeyIgnoreInhibited=no
#SuspendKeyIgnoreInhibited=no
#HibernateKeyIgnoreInhibited=no
#LidSwitchIgnoreInhibited=yes
#RebootKeyIgnoreInhibited=no
#HoldoffTimeoutSec=30s
#IdleAction=ignore
#IdleActionSec=30min

View File

@ -107,11 +107,13 @@ struct Manager {
HandleAction handle_lid_switch;
HandleAction handle_lid_switch_ep;
HandleAction handle_lid_switch_docked;
HandleAction handle_reboot_key;
bool power_key_ignore_inhibited;
bool suspend_key_ignore_inhibited;
bool hibernate_key_ignore_inhibited;
bool lid_switch_ignore_inhibited;
bool reboot_key_ignore_inhibited;
bool remove_ipc;

View File

@ -113,6 +113,17 @@
</defaults>
</action>
<action id="org.freedesktop.login1.inhibit-handle-reboot-key">
<description gettext-domain="systemd">Allow applications to inhibit system handling of the reboot key</description>
<message gettext-domain="systemd">Authentication is required for an application to inhibit system handling of the reboot key.</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>yes</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.inhibit-handle-suspend-key org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch</annotate>
</action>
<action id="org.freedesktop.login1.set-self-linger">
<description gettext-domain="systemd">Allow non-logged-in user to run programs</description>
<message gettext-domain="systemd">Explicit request is required to run programs as a non-logged-in user.</message>

View File

@ -983,7 +983,7 @@ static int dhcp6_update_address(
addr->cinfo.ifa_valid = lifetime_valid;
(void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
log_link_full(link, set_contains(link->dhcp6_addresses, addr) ? LOG_DEBUG : LOG_INFO, 0,
log_link_full(link, set_contains(link->dhcp6_addresses, addr) ? LOG_DEBUG : LOG_INFO,
"DHCPv6 address %s/%u timeout preferred %d valid %d",
strna(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);

View File

@ -4660,10 +4660,10 @@ int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, in
const char *err_msg = NULL;
(void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg);
return log_link_full(link, level, err,
"%s: %s%s%s%m",
msg,
strempty(err_msg),
err_msg && !endswith(err_msg, ".") ? "." : "",
err_msg ? " " : "");
return log_link_full_errno(link, level, err,
"%s: %s%s%s%m",
msg,
strempty(err_msg),
err_msg && !endswith(err_msg, ".") ? "." : "",
err_msg ? " " : "");
}

View File

@ -36,11 +36,11 @@
#include "path-util.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
#include "strv.h"
#include "sysctl-util.h"
#include "tmpfile-util.h"
#include "udev-util.h"
#include "virt.h"
/* use 128 MB for receive socket kernel queue. */
#define RCVBUF_SIZE (128*1024*1024)
@ -261,16 +261,19 @@ static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *devi
static int manager_connect_udev(Manager *m) {
int r;
/* udev does not initialize devices inside containers,
* so we rely on them being already initialized before
* entering the container */
if (detect_container() > 0)
/* udev does not initialize devices inside containers, so we rely on them being already
* initialized before entering the container. */
if (path_is_read_only_fs("/sys") > 0)
return 0;
r = sd_device_monitor_new(&m->device_monitor);
if (r < 0)
return log_error_errno(r, "Failed to initialize device monitor: %m");
r = sd_device_monitor_set_receive_buffer_size(m->device_monitor, RCVBUF_SIZE);
if (r < 0)
log_warning_errno(r, "Failed to increase buffer size for device monitor, ignoring: %m");
r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL);
if (r < 0)
return log_error_errno(r, "Could not add device monitor filter: %m");
@ -1347,7 +1350,7 @@ static int manager_connect_genl(Manager *m) {
r = sd_netlink_inc_rcvbuf(m->genl, RCVBUF_SIZE);
if (r < 0)
return r;
log_warning_errno(r, "Failed to increase receive buffer size for general netlink socket, ignoring: %m");
r = sd_netlink_attach_event(m->genl, m->event, 0);
if (r < 0)
@ -1369,9 +1372,14 @@ static int manager_connect_rtnl(Manager *m) {
if (r < 0)
return r;
r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE);
if (r < 0)
return r;
/* Bump receiver buffer, but only if we are not called via socket activation, as in that
* case systemd sets the receive buffer size for us, and the value in the .socket unit
* should take full effect. */
if (fd < 0) {
r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE);
if (r < 0)
log_warning_errno(r, "Failed to increase receive buffer size for rtnl socket, ignoring: %m");
}
r = sd_netlink_attach_event(m->rtnl, m->event, 0);
if (r < 0)

View File

@ -301,7 +301,7 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
CustomMount *m;
int k;
k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
k = strv_split_full(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (k < 0)
return k;
if (k < 2)

View File

@ -88,13 +88,12 @@ int change_uid_gid_raw(
int change_uid_gid(const char *user, char **_home) {
char *x, *u, *g, *h;
const char *word, *state;
_cleanup_free_ gid_t *gids = NULL;
_cleanup_free_ char *home = NULL, *line = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
unsigned n_gids = 0;
size_t sz = 0, l;
size_t sz = 0;
uid_t uid;
gid_t gid;
pid_t pid;
@ -208,16 +207,19 @@ int change_uid_gid(const char *user, char **_home) {
x += strcspn(x, WHITESPACE);
x += strspn(x, WHITESPACE);
FOREACH_WORD(word, l, x, state) {
char c[l+1];
for (const char *p = x;;) {
_cleanup_free_ char *word = NULL;
memcpy(c, word, l);
c[l] = 0;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse group data from getent: %m");
if (r == 0)
break;
if (!GREEDY_REALLOC(gids, sz, n_gids+1))
return log_oom();
r = parse_gid(c, &gids[n_gids++]);
r = parse_gid(word, &gids[n_gids++]);
if (r < 0)
return log_error_errno(r, "Failed to parse group data from getent: %m");
}

View File

@ -340,7 +340,7 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
return log_error_errno(r, "Failed to parse path: %m");
}
r = strv_split_extract(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (r < 0)
return log_error_errno(r, "Failed to parse command line: %m");

View File

@ -239,10 +239,10 @@ typedef enum Disabled {
#define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \
CONFIG_PARSER_PROTOTYPE(function) { \
type **enums = data, x, *ys; \
type **enums = data; \
_cleanup_free_ type *xs = NULL; \
const char *word, *state; \
size_t l, i = 0; \
size_t i = 0; \
int r; \
\
assert(filename); \
assert(lvalue); \
@ -255,29 +255,32 @@ typedef enum Disabled {
\
*xs = invalid; \
\
FOREACH_WORD(word, l, rvalue, state) { \
for (const char *p = rvalue;;) { \
_cleanup_free_ char *en = NULL; \
type *new_xs; \
type x, *new_xs; \
\
en = strndup(word, l); \
if (!en) \
r = extract_first_word(&p, &en, NULL, 0); \
if (r == -ENOMEM) \
return log_oom(); \
if (r < 0) \
return log_syntax(unit, LOG_ERR, filename, line, 0, \
msg ": %s", en); \
if (r == 0) \
break; \
\
if ((x = name##_from_string(en)) < 0) { \
log_syntax(unit, LOG_WARNING, filename, line, 0, \
log_syntax(unit, LOG_WARNING, filename, line, 0, \
msg ", ignoring: %s", en); \
continue; \
} \
\
for (ys = xs; x != invalid && *ys != invalid; ys++) { \
if (*ys == x) { \
log_syntax(unit, LOG_NOTICE, filename, \
line, 0, \
"Duplicate entry, ignoring: %s", \
for (type *ys = xs; x != invalid && *ys != invalid; ys++) \
if (*ys == x) { \
log_syntax(unit, LOG_NOTICE, filename, line, 0, \
"Duplicate entry, ignoring: %s", \
en); \
x = invalid; \
} \
} \
\
if (x == invalid) \
continue; \
@ -292,6 +295,5 @@ typedef enum Disabled {
*(xs + i) = invalid; \
} \
\
free_and_replace(*enums, xs); \
return 0; \
return free_and_replace(*enums, xs); \
}

View File

@ -81,50 +81,53 @@ int fstab_is_mount_point(const char *mount) {
int fstab_filter_options(const char *opts, const char *names,
const char **ret_namefound, char **ret_value, char **ret_filtered) {
const char *name, *n = NULL, *x;
const char *name, *namefound = NULL, *x;
_cleanup_strv_free_ char **stor = NULL;
_cleanup_free_ char *v = NULL, **strv = NULL;
int r;
assert(names && *names);
if (!opts)
goto answer;
/* If !value and !filtered, this function is not allowed to fail. */
/* If !ret_value and !ret_filtered, this function is not allowed to fail. */
if (!ret_filtered) {
const char *word, *state;
size_t l;
for (const char *word = opts;;) {
const char *end = word + strcspn(word, ",");
FOREACH_WORD_SEPARATOR(word, l, opts, ",", state)
NULSTR_FOREACH(name, names) {
if (l < strlen(name))
if (end < word + strlen(name))
continue;
if (!strneq(word, name, strlen(name)))
continue;
/* we know that the string is NUL
* terminated, so *x is valid */
/* We know that the string is NUL terminated, so *x is valid */
x = word + strlen(name);
if (IN_SET(*x, '\0', '=', ',')) {
n = name;
namefound = name;
if (ret_value) {
free(v);
if (IN_SET(*x, '\0', ','))
v = NULL;
else {
assert(*x == '=');
x++;
v = strndup(x, l - strlen(name) - 1);
if (!v)
return -ENOMEM;
}
bool eq = *x == '=';
assert(eq || IN_SET(*x, ',', '\0'));
r = free_and_strndup(&v,
eq ? x + 1 : NULL,
eq ? end - x - 1 : 0);
if (r < 0)
return r;
}
break;
}
}
} else {
char **t, **s;
if (*end)
word = end + 1;
else
break;
}
} else {
stor = strv_split(opts, ",");
if (!stor)
return -ENOMEM;
@ -132,7 +135,8 @@ int fstab_filter_options(const char *opts, const char *names,
if (!strv)
return -ENOMEM;
for (s = t = strv; *s; s++) {
char **t = strv;
for (char **s = strv; *s; s++) {
NULSTR_FOREACH(name, names) {
x = startswith(*s, name);
if (x && IN_SET(*x, '\0', '='))
@ -144,18 +148,12 @@ int fstab_filter_options(const char *opts, const char *names,
continue;
found:
/* Keep the last occurrence found */
n = name;
namefound = name;
if (ret_value) {
free(v);
if (*x == '\0')
v = NULL;
else {
assert(*x == '=');
x++;
v = strdup(x);
if (!v)
return -ENOMEM;
}
assert(IN_SET(*x, '=', '\0'));
r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL);
if (r < 0)
return r;
}
}
*t = NULL;
@ -163,7 +161,7 @@ int fstab_filter_options(const char *opts, const char *names,
answer:
if (ret_namefound)
*ret_namefound = n;
*ret_namefound = namefound;
if (ret_filtered) {
char *f;
@ -176,7 +174,7 @@ answer:
if (ret_value)
*ret_value = TAKE_PTR(v);
return !!n;
return !!namefound;
}
int fstab_extract_values(const char *opts, const char *name, char ***values) {

View File

@ -14,24 +14,26 @@
* See, network/networkd-link.h for example.
*/
#define log_link_full(link, level, error, ...) \
#define log_link_full_errno(link, level, error, ...) \
({ \
const Link *_l = (link); \
(_l && _l->ifname) ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _l->ifname, NULL, NULL, ##__VA_ARGS__) : \
log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
}) \
#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__)
#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__)
#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__)
#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__)
#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__)
#define log_link_full(link, level, ...) (void) log_link_full_errno(link, level, 0, __VA_ARGS__)
#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__)
#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__)
#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__)
#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__)
#define log_link_debug(link, ...) log_link_full_errno(link, LOG_DEBUG, 0, __VA_ARGS__)
#define log_link_info(link, ...) log_link_full(link, LOG_INFO, __VA_ARGS__)
#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, __VA_ARGS__)
#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, __VA_ARGS__)
#define log_link_error(link, ...) log_link_full(link, LOG_ERR, __VA_ARGS__)
#define log_link_debug_errno(link, error, ...) log_link_full_errno(link, LOG_DEBUG, error, __VA_ARGS__)
#define log_link_info_errno(link, error, ...) log_link_full_errno(link, LOG_INFO, error, __VA_ARGS__)
#define log_link_notice_errno(link, error, ...) log_link_full_errno(link, LOG_NOTICE, error, __VA_ARGS__)
#define log_link_warning_errno(link, error, ...) log_link_full_errno(link, LOG_WARNING, error, __VA_ARGS__)
#define log_link_error_errno(link, error, ...) log_link_full_errno(link, LOG_ERR, error, __VA_ARGS__)
#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__
#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname

View File

@ -100,9 +100,8 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
}
int can_sleep_state(char **types) {
char **type;
_cleanup_free_ char *text = NULL;
int r;
_cleanup_free_ char *p = NULL;
if (strv_isempty(types))
return true;
@ -113,34 +112,27 @@ int can_sleep_state(char **types) {
return false;
}
r = read_one_line_file("/sys/power/state", &p);
r = read_one_line_file("/sys/power/state", &text);
if (r < 0) {
log_debug_errno(r, "Failed to read /sys/power/state, cannot sleep: %m");
return false;
}
STRV_FOREACH(type, types) {
const char *word, *state;
size_t l, k;
k = strlen(*type);
FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
if (l == k && memcmp(word, *type, l) == 0) {
log_debug("Sleep mode \"%s\" is supported by the kernel.", *type);
return true;
}
}
if (DEBUG_LOGGING) {
const char *found;
r = string_contains_word_strv(text, NULL, types, &found);
if (r < 0)
return log_debug_errno(r, "Failed to parse /sys/power/state: %m");
if (r > 0)
log_debug("Sleep mode \"%s\" is supported by the kernel.", found);
else if (DEBUG_LOGGING) {
_cleanup_free_ char *t = strv_join(types, "/");
log_debug("Sleep mode %s not supported by the kernel, sorry.", strnull(t));
}
return false;
return r;
}
int can_sleep_disk(char **types) {
_cleanup_free_ char *p = NULL;
char **type;
_cleanup_free_ char *text = NULL;
int r;
if (strv_isempty(types))
@ -152,29 +144,38 @@ int can_sleep_disk(char **types) {
return false;
}
r = read_one_line_file("/sys/power/disk", &p);
r = read_one_line_file("/sys/power/disk", &text);
if (r < 0) {
log_debug_errno(r, "Couldn't read /sys/power/disk: %m");
return false;
}
STRV_FOREACH(type, types) {
const char *word, *state;
size_t l, k;
for (const char *p = text;;) {
_cleanup_free_ char *word = NULL;
k = strlen(*type);
FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
if (l == k && memcmp(word, *type, l) == 0)
return true;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_debug_errno(r, "Failed to parse /sys/power/disk: %m");
if (r == 0)
break;
if (l == k + 2 &&
word[0] == '[' &&
memcmp(word + 1, *type, l - 2) == 0 &&
word[l-1] == ']')
return true;
char *s = word;
size_t l = strlen(s);
if (s[0] == '[' && s[l-1] == ']') {
s[l-1] = '\0';
s++;
}
if (strv_contains(types, s)) {
log_debug("Disk sleep mode \"%s\" is supported by the kernel.", s);
return true;
}
}
if (DEBUG_LOGGING) {
_cleanup_free_ char *t = strv_join(types, "/");
log_debug("Disk sleep mode %s not supported by the kernel, sorry.", strnull(t));
}
return false;
}

View File

@ -180,7 +180,11 @@ int sd_seat_get_active(const char *seat, char **session, uid_t *uid);
/* Return sessions and users on seat. Returns number of sessions.
* If sessions is NULL, this returns only the number of sessions. */
int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids);
int sd_seat_get_sessions(
const char *seat,
char ***ret_sessions,
uid_t **ret_uids,
unsigned *ret_n_uids);
/* Return whether the seat is multi-session capable */
int sd_seat_can_multi_session(const char *seat) _sd_deprecated_;
@ -195,7 +199,7 @@ int sd_seat_can_graphical(const char *seat);
int sd_machine_get_class(const char *machine, char **clazz);
/* Return the list if host-side network interface indices of a machine */
int sd_machine_get_ifindices(const char *machine, int **ifindices);
int sd_machine_get_ifindices(const char *machine, int **ret_ifindices);
/* Get all seats, store in *seats. Returns the number of seats. If
* seats is NULL, this only returns the number of seats. */

View File

@ -140,6 +140,8 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_SYSTEM_UNDOCKED_STR SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
#define SD_MESSAGE_POWER_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
#define SD_MESSAGE_REBOOT_KEY SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
#define SD_MESSAGE_REBOOT_KEY_STR SD_ID128_MAKE_STR(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
#define SD_MESSAGE_SUSPEND_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)

View File

@ -11,6 +11,8 @@ static void test_extract_first_word(void) {
const char *p, *original;
char *t;
log_info("/* %s */", __func__);
p = original = "foobar waldo";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
assert_se(streq(t, "foobar"));
@ -387,6 +389,8 @@ static void test_extract_first_word_and_warn(void) {
const char *p, *original;
char *t;
log_info("/* %s */", __func__);
p = original = "foobar waldo";
assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foobar"));
@ -531,6 +535,8 @@ static void test_extract_many_words(void) {
const char *p, *original;
char *a, *b, *c, *d, *e, *f;
log_info("/* %s */", __func__);
p = original = "foobar waldi piep";
assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 3);
assert_se(isempty(p));

View File

@ -10,8 +10,9 @@
#include "util.h"
static void test_string_erase(void) {
char *x;
log_info("/* %s */", __func__);
char *x;
x = strdupa("");
assert_se(streq(string_erase(x), ""));
@ -33,17 +34,17 @@ static void test_string_erase(void) {
}
static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) {
int r;
log_debug("%s: \"%s\", \"%s\", %zd (expect \"%s\", %s)",
__func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change));
r = free_and_strndup(t, src, l);
int r = free_and_strndup(t, src, l);
assert_se(streq_ptr(*t, expected));
assert_se(r == change); /* check that change occurs only when necessary */
}
static void test_free_and_strndup(void) {
log_info("/* %s */", __func__);
static const struct test_case {
const char *src;
size_t len;
@ -91,6 +92,7 @@ static void test_free_and_strndup(void) {
}
static void test_ascii_strcasecmp_n(void) {
log_info("/* %s */", __func__);
assert_se(ascii_strcasecmp_n("", "", 0) == 0);
assert_se(ascii_strcasecmp_n("", "", 1) == 0);
@ -118,6 +120,8 @@ static void test_ascii_strcasecmp_n(void) {
}
static void test_ascii_strcasecmp_nn(void) {
log_info("/* %s */", __func__);
assert_se(ascii_strcasecmp_nn("", 0, "", 0) == 0);
assert_se(ascii_strcasecmp_nn("", 0, "", 1) < 0);
assert_se(ascii_strcasecmp_nn("", 1, "", 0) > 0);
@ -137,6 +141,8 @@ static void test_ascii_strcasecmp_nn(void) {
static void test_cellescape(void) {
char buf[40];
log_info("/* %s */", __func__);
assert_se(streq(cellescape(buf, 1, ""), ""));
assert_se(streq(cellescape(buf, 1, "1"), ""));
assert_se(streq(cellescape(buf, 1, "12"), ""));
@ -216,19 +222,24 @@ static void test_cellescape(void) {
}
static void test_streq_ptr(void) {
log_info("/* %s */", __func__);
assert_se(streq_ptr(NULL, NULL));
assert_se(!streq_ptr("abc", "cdef"));
}
static void test_strstrip(void) {
char *r;
char input[] = " hello, waldo. ";
log_info("/* %s */", __func__);
r = strstrip(input);
assert_se(streq(r, "hello, waldo."));
char *ret, input[] = " hello, waldo. ";
ret = strstrip(input);
assert_se(streq(ret, "hello, waldo."));
}
static void test_strextend(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *str = NULL;
assert_se(strextend(&str, NULL));
@ -240,6 +251,8 @@ static void test_strextend(void) {
}
static void test_strextend_with_separator(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *str = NULL;
assert_se(strextend_with_separator(&str, NULL, NULL));
@ -263,6 +276,8 @@ static void test_strextend_with_separator(void) {
}
static void test_strrep(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *one, *three, *zero;
one = strrep("waldo", 1);
three = strrep("waldo", 3);
@ -288,11 +303,15 @@ static void test_string_has_cc(void) {
}
static void test_ascii_strlower(void) {
log_info("/* %s */", __func__);
char a[] = "AabBcC Jk Ii Od LKJJJ kkd LK";
assert_se(streq(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk"));
}
static void test_strshorten(void) {
log_info("/* %s */", __func__);
char s[] = "foobar";
assert_se(strlen(strshorten(s, 6)) == 6);
@ -302,6 +321,8 @@ static void test_strshorten(void) {
}
static void test_strjoina(void) {
log_info("/* %s */", __func__);
char *actual;
actual = strjoina("", "foo", "bar");
@ -359,6 +380,8 @@ static void test_strjoin(void) {
}
static void test_strcmp_ptr(void) {
log_info("/* %s */", __func__);
assert_se(strcmp_ptr(NULL, NULL) == 0);
assert_se(strcmp_ptr("", NULL) > 0);
assert_se(strcmp_ptr("foo", NULL) > 0);
@ -371,26 +394,36 @@ static void test_strcmp_ptr(void) {
}
static void test_foreach_word(void) {
const char *word, *state;
size_t l;
int i = 0;
const char test[] = "test abc d\te f ";
log_info("/* %s */", __func__);
const char *test = "test abc d\te f ";
const char * const expected[] = {
"test",
"abc",
"d",
"e",
"f",
"",
NULL
};
FOREACH_WORD(word, l, test, state)
assert_se(strneq(expected[i++], word, l));
size_t i = 0;
int r;
for (const char *p = test;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r == 0) {
assert_se(i == ELEMENTSOF(expected));
break;
}
assert_se(r > 0);
assert_se(streq(expected[i++], word));
}
}
static void check(const char *test, char** expected, bool trailing) {
int i = 0, r;
size_t i = 0;
int r;
printf("<<<%s>>>\n", test);
for (;;) {
@ -412,6 +445,8 @@ static void check(const char *test, char** expected, bool trailing) {
}
static void test_foreach_word_quoted(void) {
log_info("/* %s */", __func__);
check("test a b c 'd' e '' '' hhh '' '' \"a b c\"",
STRV_MAKE("test",
"a",
@ -437,6 +472,8 @@ static void test_foreach_word_quoted(void) {
}
static void test_endswith(void) {
log_info("/* %s */", __func__);
assert_se(endswith("foobar", "bar"));
assert_se(endswith("foobar", ""));
assert_se(endswith("foobar", "foobar"));
@ -447,6 +484,8 @@ static void test_endswith(void) {
}
static void test_endswith_no_case(void) {
log_info("/* %s */", __func__);
assert_se(endswith_no_case("fooBAR", "bar"));
assert_se(endswith_no_case("foobar", ""));
assert_se(endswith_no_case("foobar", "FOOBAR"));
@ -457,6 +496,8 @@ static void test_endswith_no_case(void) {
}
static void test_delete_chars(void) {
log_info("/* %s */", __func__);
char *s, input[] = " hello, waldo. abc";
s = delete_chars(input, WHITESPACE);
@ -465,6 +506,7 @@ static void test_delete_chars(void) {
}
static void test_delete_trailing_chars(void) {
log_info("/* %s */", __func__);
char *s,
input1[] = " \n \r k \n \r ",
@ -489,6 +531,8 @@ static void test_delete_trailing_chars(void) {
}
static void test_delete_trailing_slashes(void) {
log_info("/* %s */", __func__);
char s1[] = "foobar//",
s2[] = "foobar/",
s3[] = "foobar",
@ -502,6 +546,8 @@ static void test_delete_trailing_slashes(void) {
}
static void test_skip_leading_chars(void) {
log_info("/* %s */", __func__);
char input1[] = " \n \r k \n \r ",
input2[] = "kkkkthiskkkiskkkaktestkkk",
input3[] = "abcdef";
@ -514,11 +560,15 @@ static void test_skip_leading_chars(void) {
}
static void test_in_charset(void) {
log_info("/* %s */", __func__);
assert_se(in_charset("dddaaabbbcccc", "abcd"));
assert_se(!in_charset("dddaaabbbcccc", "abc f"));
}
static void test_split_pair(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *a = NULL, *b = NULL;
assert_se(split_pair("", "", &a, &b) == -EINVAL);
@ -541,6 +591,8 @@ static void test_split_pair(void) {
}
static void test_first_word(void) {
log_info("/* %s */", __func__);
assert_se(first_word("Hello", ""));
assert_se(first_word("Hello", "Hello"));
assert_se(first_word("Hello world", "Hello"));
@ -555,12 +607,16 @@ static void test_first_word(void) {
}
static void test_strlen_ptr(void) {
log_info("/* %s */", __func__);
assert_se(strlen_ptr("foo") == 3);
assert_se(strlen_ptr("") == 0);
assert_se(strlen_ptr(NULL) == 0);
}
static void test_memory_startswith(void) {
log_info("/* %s */", __func__);
assert_se(streq(memory_startswith("", 0, ""), ""));
assert_se(streq(memory_startswith("", 1, ""), ""));
assert_se(streq(memory_startswith("x", 2, ""), "x"));
@ -573,6 +629,8 @@ static void test_memory_startswith(void) {
}
static void test_memory_startswith_no_case(void) {
log_info("/* %s */", __func__);
assert_se(streq(memory_startswith_no_case("", 0, ""), ""));
assert_se(streq(memory_startswith_no_case("", 1, ""), ""));
assert_se(streq(memory_startswith_no_case("x", 2, ""), "x"));
@ -605,6 +663,8 @@ static void test_string_truncate_lines_one(const char *input, size_t n_lines, co
}
static void test_string_truncate_lines(void) {
log_info("/* %s */", __func__);
test_string_truncate_lines_one("", 0, "", false);
test_string_truncate_lines_one("", 1, "", false);
test_string_truncate_lines_one("", 2, "", false);
@ -676,6 +736,8 @@ static void test_string_extract_lines_one(const char *input, size_t i, const cha
}
static void test_string_extract_line(void) {
log_info("/* %s */", __func__);
test_string_extract_lines_one("", 0, "", false);
test_string_extract_lines_one("", 1, "", false);
test_string_extract_lines_one("", 2, "", false);
@ -742,6 +804,88 @@ static void test_string_extract_line(void) {
test_string_extract_lines_one("\n\n\nx\n", 3, "x", false);
}
static void test_string_contains_word_strv(void) {
log_info("/* %s */", __func__);
const char *w;
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), NULL));
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), &w));
assert_se(streq(w, "a"));
assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE("d"), &w));
assert_se(w == NULL);
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", "a"), &w));
assert_se(streq(w, "a"));
assert_se(string_contains_word_strv("b a b cc", NULL, STRV_MAKE("b", "a", "b"), &w));
assert_se(streq(w, "b"));
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", ""), &w));
assert_se(streq(w, "b"));
assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE(""), &w));
assert_se(w == NULL);
assert_se(string_contains_word_strv("a b cc", " ", STRV_MAKE(""), &w));
assert_se(streq(w, ""));
}
static void test_string_contains_word(void) {
log_info("/* %s */", __func__);
assert_se( string_contains_word("a b cc", NULL, "a"));
assert_se( string_contains_word("a b cc", NULL, "b"));
assert_se(!string_contains_word("a b cc", NULL, "c"));
assert_se( string_contains_word("a b cc", NULL, "cc"));
assert_se(!string_contains_word("a b cc", NULL, "d"));
assert_se(!string_contains_word("a b cc", NULL, "a b"));
assert_se(!string_contains_word("a b cc", NULL, "a b c"));
assert_se(!string_contains_word("a b cc", NULL, "b c"));
assert_se(!string_contains_word("a b cc", NULL, "b cc"));
assert_se(!string_contains_word("a b cc", NULL, "a "));
assert_se(!string_contains_word("a b cc", NULL, " b "));
assert_se(!string_contains_word("a b cc", NULL, " cc"));
assert_se( string_contains_word(" a b\t\tcc", NULL, "a"));
assert_se( string_contains_word(" a b\t\tcc", NULL, "b"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "c"));
assert_se( string_contains_word(" a b\t\tcc", NULL, "cc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "d"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "a b"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "a b\t\tc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "b\t\tc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "b\t\tcc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "a "));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " b "));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " cc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, ""));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " "));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " "));
assert_se( string_contains_word(" a b\t\tcc", " ", ""));
assert_se( string_contains_word(" a b\t\tcc", "\t", ""));
assert_se( string_contains_word(" a b\t\tcc", WHITESPACE, ""));
assert_se( string_contains_word("a:b:cc", ":#", "a"));
assert_se( string_contains_word("a:b:cc", ":#", "b"));
assert_se(!string_contains_word("a:b:cc", ":#", "c"));
assert_se( string_contains_word("a:b:cc", ":#", "cc"));
assert_se(!string_contains_word("a:b:cc", ":#", "d"));
assert_se(!string_contains_word("a:b:cc", ":#", "a:b"));
assert_se(!string_contains_word("a:b:cc", ":#", "a:b:c"));
assert_se(!string_contains_word("a:b:cc", ":#", "b:c"));
assert_se(!string_contains_word("a#b#cc", ":#", "b:cc"));
assert_se( string_contains_word("a#b#cc", ":#", "b"));
assert_se( string_contains_word("a#b#cc", ":#", "cc"));
assert_se(!string_contains_word("a:b:cc", ":#", "a:"));
assert_se(!string_contains_word("a:b cc", ":#", "b"));
assert_se( string_contains_word("a:b cc", ":#", "b cc"));
assert_se(!string_contains_word("a:b:cc", ":#", ":cc"));
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
@ -777,6 +921,8 @@ int main(int argc, char *argv[]) {
test_memory_startswith_no_case();
test_string_truncate_lines();
test_string_extract_line();
test_string_contains_word_strv();
test_string_contains_word();
return 0;
}

View File

@ -100,6 +100,12 @@ static const char* const input_table_quoted[] = {
NULL,
};
static const char* const input_table_quoted_joined[] = {
"one",
" two\t three " " four five",
NULL,
};
static const char* const input_table_one[] = {
"one",
NULL,
@ -232,7 +238,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
log_info("/* %s */", __func__);
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
assert_se(r == (int) strv_length(list));
assert_se(s);
j = strv_join(s, " | ");
@ -251,7 +257,7 @@ static void test_invalid_unquote(const char *quoted) {
log_info("/* %s */", __func__);
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
assert_se(s == NULL);
assert_se(r == -EINVAL);
}
@ -281,47 +287,39 @@ static void test_strv_split(void) {
strv_free_erase(l);
l = strv_split_full(" one two\t three", NULL, 0);
assert_se(l);
assert_se(strv_split_full(&l, " one two\t three", NULL, 0) == 3);
assert_se(strv_equal(l, (char**) input_table_multiple));
strv_free_erase(l);
l = strv_split_full(" 'one' \" two\t three \" ' four five'", NULL, SPLIT_QUOTES);
assert_se(l);
assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five'", NULL, EXTRACT_UNQUOTE) == 3);
assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_free_erase(l);
/* missing last quote ignores the last element. */
l = strv_split_full(" 'one' \" two\t three \" ' four five' ' ignored element ", NULL, SPLIT_QUOTES);
assert_se(l);
/* missing last quote causes extraction to fail. */
assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five", NULL, EXTRACT_UNQUOTE) == -EINVAL);
assert_se(!l);
/* missing last quote, but the last element is _not_ ignored with EXTRACT_RELAX. */
assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 3);
assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_free_erase(l);
/* missing last quote, but the last element is _not_ ignored with SPLIT_RELAX. */
l = strv_split_full(" 'one' \" two\t three \" ' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
/* missing separator between items */
assert_se(strv_split_full(&l, " 'one' \" two\t three \"' four five'", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 2);
assert_se(strv_equal(l, (char**) input_table_quoted_joined));
strv_free_erase(l);
l = strv_free_erase(l);
/* missing separator between */
l = strv_split_full(" 'one' \" two\t three \"' four five'", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
assert_se(strv_split_full(&l, " 'one' \" two\t three \"' four five", NULL,
EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 2);
assert_se(strv_equal(l, (char**) input_table_quoted_joined));
strv_free_erase(l);
l = strv_free_erase(l);
l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_split_full("\\", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_split_full(&l, "\\", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 1);
assert_se(strv_equal(l, STRV_MAKE("\\")));
}
@ -333,71 +331,70 @@ static void test_strv_split_empty(void) {
l = strv_split("", WHITESPACE);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split("", NULL);
assert_se(l = strv_split("", NULL));
assert_se(strv_isempty(l));
l = strv_free(l);
assert_se(strv_split_full(&l, "", NULL, 0) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full("", NULL, 0);
assert_se(strv_split_full(&l, "", NULL, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full("", NULL, SPLIT_QUOTES);
assert_se(strv_split_full(&l, "", WHITESPACE, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full("", WHITESPACE, SPLIT_QUOTES);
assert_se(strv_split_full(&l, "", WHITESPACE, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 0);
assert_se(l);
assert_se(strv_isempty(l));
strv_free(l);
l = strv_split_full("", WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_isempty(l));
strv_free(l);
l = strv_split(" ", WHITESPACE);
assert_se(l);
assert_se(strv_isempty(l));
strv_free(l);
l = strv_split(" ", NULL);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", NULL, 0);
assert_se(strv_split_full(&l, " ", NULL, 0) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", WHITESPACE, SPLIT_QUOTES);
assert_se(strv_split_full(&l, " ", WHITESPACE, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", NULL, SPLIT_QUOTES);
assert_se(strv_split_full(&l, " ", NULL, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(strv_split_full(&l, " ", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 0);
assert_se(l);
assert_se(strv_isempty(l));
}
static void test_strv_split_extract(void) {
static void test_strv_split_full(void) {
_cleanup_strv_free_ char **l = NULL;
const char *str = ":foo\\:bar::waldo:";
int r;
log_info("/* %s */", __func__);
r = strv_split_extract(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
r = strv_split_full(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
assert_se(r == (int) strv_length(l));
assert_se(streq_ptr(l[0], ""));
assert_se(streq_ptr(l[1], "foo:bar"));
@ -1026,7 +1023,7 @@ int main(int argc, char *argv[]) {
test_strv_split();
test_strv_split_empty();
test_strv_split_extract();
test_strv_split_full();
test_strv_split_colon_pairs();
test_strv_split_newlines();
test_strv_split_nulstr();

View File

@ -320,7 +320,8 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
case NET_ADDR_PERM:
break;
default:
return log_device_warning(device, "Unknown addr_assign_type %u, ignoring", addr_type);
log_device_warning(device, "Unknown addr_assign_type %u, ignoring", addr_type);
return 0;
}
if (want_random == (addr_type == NET_ADDR_RANDOM))

View File

@ -87,7 +87,7 @@ static int map_keycode(sd_device *dev, int fd, int scancode, const char *keycode
return 0;
}
static char* parse_token(const char *current, int32_t *val_out) {
static const char* parse_token(const char *current, int32_t *val_out) {
char *next;
int32_t val;
@ -109,7 +109,7 @@ static char* parse_token(const char *current, int32_t *val_out) {
static int override_abs(sd_device *dev, int fd, unsigned evcode, const char *value) {
struct input_absinfo absinfo;
char *next;
const char *next;
int r;
r = ioctl(fd, EVIOCGABS(evcode), &absinfo);
@ -122,7 +122,7 @@ static int override_abs(sd_device *dev, int fd, unsigned evcode, const char *val
next = parse_token(next, &absinfo.fuzz);
next = parse_token(next, &absinfo.flat);
if (!next)
return log_device_error(dev, "Failed to parse EV_ABS override '%s'", value);
return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Failed to parse EV_ABS override '%s'", value);
log_device_debug(dev, "keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32,
evcode, absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat);

View File

@ -50,7 +50,7 @@ static int builtin_uaccess(sd_device *dev, int argc, char *argv[], bool test) {
r = devnode_acl(path, true, false, 0, true, uid);
if (r < 0) {
log_device_full(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL: %m");
log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL: %m");
goto finish;
}
@ -64,7 +64,7 @@ finish:
/* Better be safe than sorry and reset ACL */
k = devnode_acl(path, true, false, 0, false, 0);
if (k < 0) {
log_device_full(dev, k == -ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL: %m");
log_device_full_errno(dev, k == -ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL: %m");
if (r >= 0)
r = k;
}

View File

@ -109,6 +109,7 @@ UdevBuiltinCommand udev_builtin_lookup(const char *command) {
int udev_builtin_run(sd_device *dev, UdevBuiltinCommand cmd, const char *command, bool test) {
_cleanup_strv_free_ char **argv = NULL;
int r;
assert(dev);
assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
@ -117,9 +118,10 @@ int udev_builtin_run(sd_device *dev, UdevBuiltinCommand cmd, const char *command
if (!builtins[cmd])
return -EOPNOTSUPP;
argv = strv_split_full(command, NULL, SPLIT_QUOTES | SPLIT_RELAX);
if (!argv)
return -ENOMEM;
r = strv_split_full(&argv, command, NULL,
EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
/* we need '0' here to reset the internal state */
optind = 0;

View File

@ -633,7 +633,7 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd
if (si->si_status == 0)
log_device_debug(spawn->device, "Process '%s' succeeded.", spawn->cmd);
else
log_device_full(spawn->device, spawn->accept_failure ? LOG_DEBUG : LOG_WARNING, 0,
log_device_full(spawn->device, spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
"Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
ret = si->si_status;
break;
@ -747,9 +747,9 @@ int udev_event_spawn(UdevEvent *event,
return log_device_error_errno(event->dev, errno,
"Failed to create pipe for command '%s': %m", cmd);
argv = strv_split_full(cmd, NULL, SPLIT_QUOTES|SPLIT_RELAX);
if (!argv)
return log_oom();
r = strv_split_full(&argv, cmd, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return log_device_error_errno(event->dev, r, "Failed to split command: %m");
if (isempty(argv[0]))
return log_device_error_errno(event->dev, SYNTHETIC_ERRNO(EINVAL),

View File

@ -324,13 +324,13 @@ static int node_permissions_apply(sd_device *dev, bool apply_mac,
r = chmod_and_chown(devnode, mode, uid, gid);
if (r < 0)
log_device_full(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
"Failed to set owner/mode of %s to uid=" UID_FMT
", gid=" GID_FMT ", mode=%#o: %m",
devnode,
uid_is_valid(uid) ? uid : stats.st_uid,
gid_is_valid(gid) ? gid : stats.st_gid,
mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
"Failed to set owner/mode of %s to uid=" UID_FMT
", gid=" GID_FMT ", mode=%#o: %m",
devnode,
uid_is_valid(uid) ? uid : stats.st_uid,
gid_is_valid(gid) ? gid : stats.st_gid,
mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
} else
log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
devnode,
@ -347,8 +347,8 @@ static int node_permissions_apply(sd_device *dev, bool apply_mac,
q = mac_selinux_apply(devnode, label);
if (q < 0)
log_device_full(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
"SECLABEL: failed to set SELinux label '%s': %m", label);
log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
"SECLABEL: failed to set SELinux label '%s': %m", label);
else
log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
@ -357,8 +357,8 @@ static int node_permissions_apply(sd_device *dev, bool apply_mac,
q = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label);
if (q < 0)
log_device_full(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
"SECLABEL: failed to set SMACK label '%s': %m", label);
log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
"SECLABEL: failed to set SMACK label '%s': %m", label);
else
log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);

View File

@ -182,43 +182,46 @@ struct UdevRules {
/*** Logging helpers ***/
#define log_rule_full(device, rules, level, error, fmt, ...) \
#define log_rule_full_errno(device, rules, level, error, fmt, ...) \
({ \
UdevRules *_r = (rules); \
UdevRuleFile *_f = _r ? _r->current_file : NULL; \
UdevRuleLine *_l = _f ? _f->current_line : NULL; \
const char *_n = _f ? _f->filename : NULL; \
\
log_device_full(device, level, error, "%s:%u " fmt, \
strna(_n), _l ? _l->line_number : 0, \
##__VA_ARGS__); \
log_device_full_errno(device, level, error, "%s:%u " fmt, \
strna(_n), _l ? _l->line_number : 0, \
##__VA_ARGS__); \
})
#define log_rule_debug(device, rules, ...) log_rule_full(device, rules, LOG_DEBUG, 0, ##__VA_ARGS__)
#define log_rule_info(device, rules, ...) log_rule_full(device, rules, LOG_INFO, 0, ##__VA_ARGS__)
#define log_rule_notice(device, rules, ...) log_rule_full(device, rules, LOG_NOTICE, 0, ##__VA_ARGS__)
#define log_rule_warning(device, rules, ...) log_rule_full(device, rules, LOG_WARNING, 0, ##__VA_ARGS__)
#define log_rule_error(device, rules, ...) log_rule_full(device, rules, LOG_ERR, 0, ##__VA_ARGS__)
#define log_rule_full(device, rules, level, ...) (void) log_rule_full_errno(device, rules, level, 0, __VA_ARGS__)
#define log_rule_debug_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_rule_info_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_INFO, error, ##__VA_ARGS__)
#define log_rule_notice_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_NOTICE, error, ##__VA_ARGS__)
#define log_rule_warning_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_WARNING, error, ##__VA_ARGS__)
#define log_rule_error_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_ERR, error, ##__VA_ARGS__)
#define log_rule_debug(device, rules, ...) log_rule_full_errno(device, rules, LOG_DEBUG, 0, __VA_ARGS__)
#define log_rule_info(device, rules, ...) log_rule_full(device, rules, LOG_INFO, __VA_ARGS__)
#define log_rule_notice(device, rules, ...) log_rule_full(device, rules, LOG_NOTICE, __VA_ARGS__)
#define log_rule_warning(device, rules, ...) log_rule_full(device, rules, LOG_WARNING, __VA_ARGS__)
#define log_rule_error(device, rules, ...) log_rule_full(device, rules, LOG_ERR, __VA_ARGS__)
#define log_token_full(rules, ...) log_rule_full(NULL, rules, ##__VA_ARGS__)
#define log_rule_debug_errno(device, rules, error, ...) log_rule_full_errno(device, rules, LOG_DEBUG, error, __VA_ARGS__)
#define log_rule_info_errno(device, rules, error, ...) log_rule_full_errno(device, rules, LOG_INFO, error, __VA_ARGS__)
#define log_rule_notice_errno(device, rules, error, ...) log_rule_full_errno(device, rules, LOG_NOTICE, error, __VA_ARGS__)
#define log_rule_warning_errno(device, rules, error, ...) log_rule_full_errno(device, rules, LOG_WARNING, error, __VA_ARGS__)
#define log_rule_error_errno(device, rules, error, ...) log_rule_full_errno(device, rules, LOG_ERR, error, __VA_ARGS__)
#define log_token_debug(rules, ...) log_token_full(rules, LOG_DEBUG, 0, ##__VA_ARGS__)
#define log_token_info(rules, ...) log_token_full(rules, LOG_INFO, 0, ##__VA_ARGS__)
#define log_token_notice(rules, ...) log_token_full(rules, LOG_NOTICE, 0, ##__VA_ARGS__)
#define log_token_warning(rules, ...) log_token_full(rules, LOG_WARNING, 0, ##__VA_ARGS__)
#define log_token_error(rules, ...) log_token_full(rules, LOG_ERR, 0, ##__VA_ARGS__)
#define log_token_full_errno(rules, level, error, ...) log_rule_full_errno(NULL, rules, level, error, __VA_ARGS__)
#define log_token_full(rules, level, ...) (void) log_token_full_errno(rules, level, 0, __VA_ARGS__)
#define log_token_debug_errno(rules, error, ...) log_token_full(rules, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_token_info_errno(rules, error, ...) log_token_full(rules, LOG_INFO, error, ##__VA_ARGS__)
#define log_token_notice_errno(rules, error, ...) log_token_full(rules, LOG_NOTICE, error, ##__VA_ARGS__)
#define log_token_warning_errno(rules, error, ...) log_token_full(rules, LOG_WARNING, error, ##__VA_ARGS__)
#define log_token_error_errno(rules, error, ...) log_token_full(rules, LOG_ERR, error, ##__VA_ARGS__)
#define log_token_debug(rules, ...) log_token_full_errno(rules, LOG_DEBUG, 0, __VA_ARGS__)
#define log_token_info(rules, ...) log_token_full(rules, LOG_INFO, __VA_ARGS__)
#define log_token_notice(rules, ...) log_token_full(rules, LOG_NOTICE, __VA_ARGS__)
#define log_token_warning(rules, ...) log_token_full(rules, LOG_WARNING, __VA_ARGS__)
#define log_token_error(rules, ...) log_token_full(rules, LOG_ERR, __VA_ARGS__)
#define log_token_debug_errno(rules, error, ...) log_token_full_errno(rules, LOG_DEBUG, error, __VA_ARGS__)
#define log_token_info_errno(rules, error, ...) log_token_full_errno(rules, LOG_INFO, error, __VA_ARGS__)
#define log_token_notice_errno(rules, error, ...) log_token_full_errno(rules, LOG_NOTICE, error, __VA_ARGS__)
#define log_token_warning_errno(rules, error, ...) log_token_full_errno(rules, LOG_WARNING, error, __VA_ARGS__)
#define log_token_error_errno(rules, error, ...) log_token_full_errno(rules, LOG_ERR, error, __VA_ARGS__)
#define _log_token_invalid(rules, key, type) \
log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), \

View File

@ -97,10 +97,8 @@ int udev_watch_begin(sd_device *dev) {
log_device_debug(dev, "Adding watch on '%s'", devnode);
wd = inotify_add_watch(inotify_fd, devnode, IN_CLOSE_WRITE);
if (wd < 0)
return log_device_full(dev,
errno == ENOENT ? LOG_DEBUG : LOG_ERR,
errno,
"Failed to add device '%s' to watch: %m", devnode);
return log_device_full_errno(dev, errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
"Failed to add device '%s' to watch: %m", devnode);
device_set_watch_handle(dev, wd);

View File

@ -1686,8 +1686,11 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
/* Bump receiver buffer, but only if we are not called via socket activation, as in that
* case systemd sets the receive buffer size for us, and the value in the .socket unit
* should take full effect. */
if (fd_uevent < 0)
(void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
if (fd_uevent < 0) {
r = sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
if (r < 0)
log_warning_errno(r, "Failed to set receive buffer size for device monitor, ignoring: %m");
}
r = device_monitor_enable_receiving(manager->monitor);
if (r < 0)

View File

@ -385,9 +385,9 @@ int xdg_autostart_format_exec_start(
* NOTE: Technically, XDG only specifies " as quotes, while this also
* accepts '.
*/
exec_split = strv_split_full(exec, WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX);
if (!exec_split)
return -ENOMEM;
r = strv_split_full(&exec_split, exec, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX);
if (r < 0)
return r;
if (strv_isempty(exec_split))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Exec line is empty");