Compare commits
57 Commits
aaf6cb2914
...
adf5023062
Author | SHA1 | Date |
---|---|---|
Mike Yuan | adf5023062 | |
Yu Watanabe | a3c2a9ee5d | |
Daan De Meyer | 062332f3db | |
Mike Yuan | b3c41c9849 | |
Mike Yuan | 97cdb64101 | |
Mike Yuan | 8606e63208 | |
Mike Yuan | a05de0a7fa | |
Mike Yuan | 77e528bda1 | |
Mike Yuan | dd3672539a | |
Mike Yuan | 1f62c885d7 | |
Mike Yuan | da9ff07fa4 | |
Mike Yuan | 394068f136 | |
Mike Yuan | 79495b0904 | |
Mike Yuan | 727bb81a99 | |
Mike Yuan | 97f00da913 | |
Mike Yuan | 8dd6249e0d | |
Mike Yuan | 3cb72f0fe2 | |
Mike Yuan | b9503dfa80 | |
Mike Yuan | c0e8dcfe1a | |
Mike Yuan | 395f57bbce | |
Mike Yuan | bd8316cb29 | |
Mike Yuan | b7d5b0d33b | |
Daan De Meyer | bc9a9177b2 | |
Daan De Meyer | e5c6dcac87 | |
Daan De Meyer | 34a7ca6db2 | |
Daan De Meyer | 397820961d | |
Yu Watanabe | 3b16e9f419 | |
Yu Watanabe | d5a7f3b7d4 | |
Yu Watanabe | f1c16ca6d6 | |
Yu Watanabe | b094398b0f | |
Yu Watanabe | 1ee6570843 | |
Yu Watanabe | b7f051c91d | |
Yu Watanabe | a13ead6814 | |
Yu Watanabe | f901a7b39f | |
Yu Watanabe | 9b01cf0406 | |
Yu Watanabe | d5aae0713d | |
Daan De Meyer | 1d8a81eb4e | |
Daan De Meyer | 86c1317270 | |
Daan De Meyer | f4faac2073 | |
Yu Watanabe | 2bcc2a89f3 | |
Yu Watanabe | 07e6a111c0 | |
Yu Watanabe | c2648f6e23 | |
Daan De Meyer | 1d5b4317cd | |
Yu Watanabe | 8d6eedd8a3 | |
Yu Watanabe | 91eaa90b81 | |
Yu Watanabe | 3b5c5da73a | |
Yu Watanabe | 1775654e2c | |
Yu Watanabe | 0ea6d55a4b | |
Yu Watanabe | 26d35019de | |
Yu Watanabe | b962338104 | |
Yu Watanabe | fae0b00434 | |
Yu Watanabe | f7923ef318 | |
Yu Watanabe | 36df48d863 | |
Yu Watanabe | 53c638db16 | |
Yu Watanabe | 751a247794 | |
Yu Watanabe | 07dbbda0fc | |
Yu Watanabe | ed4a6c476e |
|
@ -962,7 +962,9 @@ default ignore - -</programlisting>
|
||||||
discovered/supported/used, prints <literal>no</literal>. Otherwise prints
|
discovered/supported/used, prints <literal>no</literal>. Otherwise prints
|
||||||
<literal>partial</literal>. In either of these two cases exits with non-zero exit status. It also shows
|
<literal>partial</literal>. In either of these two cases exits with non-zero exit status. It also shows
|
||||||
five lines indicating separately whether firmware, drivers, the system, the kernel and libraries
|
five lines indicating separately whether firmware, drivers, the system, the kernel and libraries
|
||||||
discovered/support/use TPM2.</para>
|
discovered/support/use TPM2. Currently, required libraries are <filename>libtss2-esys.so.0</filename>,
|
||||||
|
<filename>libtss2-rc.so.0</filename>, and <filename>libtss2-mu.so.0</filename>. The requirement may be
|
||||||
|
changed in the future release.</para>
|
||||||
|
|
||||||
<para>Note, this checks for TPM 2.0 devices only, and does not consider TPM 1.2 at all.</para>
|
<para>Note, this checks for TPM 2.0 devices only, and does not consider TPM 1.2 at all.</para>
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Description</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para><command>systemd-nsresourced</command> is a system service that permits transient delegation of a a
|
<para><command>systemd-nsresourced</command> is a system service that permits transient delegation of a
|
||||||
UID/GID range to a user namespace (see <citerefentry
|
UID/GID range to a user namespace (see <citerefentry
|
||||||
project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>)
|
project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>)
|
||||||
allocated by a client, via a Varlink IPC API.</para>
|
allocated by a client, via a Varlink IPC API.</para>
|
||||||
|
|
|
@ -2084,6 +2084,7 @@ libsystemd_includes = [basic_includes, include_directories(
|
||||||
'src/libsystemd/sd-json',
|
'src/libsystemd/sd-json',
|
||||||
'src/libsystemd/sd-netlink',
|
'src/libsystemd/sd-netlink',
|
||||||
'src/libsystemd/sd-network',
|
'src/libsystemd/sd-network',
|
||||||
|
'src/libsystemd/sd-path',
|
||||||
'src/libsystemd/sd-resolve',
|
'src/libsystemd/sd-resolve',
|
||||||
'src/libsystemd/sd-varlink')]
|
'src/libsystemd/sd-varlink')]
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,11 @@
|
||||||
set -e
|
set -e
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
if [[ "$DISTRIBUTION" =~ ubuntu|debian ]]; then
|
|
||||||
SUDO_GROUP=sudo
|
|
||||||
else
|
|
||||||
SUDO_GROUP=wheel
|
|
||||||
fi
|
|
||||||
|
|
||||||
useradd \
|
useradd \
|
||||||
--uid 4711 \
|
--uid 4711 \
|
||||||
--user-group \
|
--user-group \
|
||||||
--create-home \
|
--create-home \
|
||||||
--password "$(openssl passwd -1 testuser)" \
|
--password "$(openssl passwd -1 testuser)" \
|
||||||
--groups "$SUDO_GROUP",systemd-journal \
|
|
||||||
--shell /bin/bash \
|
--shell /bin/bash \
|
||||||
testuser
|
testuser
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ _systemd_analyze() {
|
||||||
)
|
)
|
||||||
|
|
||||||
local -A VERBS=(
|
local -A VERBS=(
|
||||||
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk'
|
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2'
|
||||||
[CRITICAL_CHAIN]='critical-chain'
|
[CRITICAL_CHAIN]='critical-chain'
|
||||||
[DOT]='dot'
|
[DOT]='dot'
|
||||||
[DUMP]='dump'
|
[DUMP]='dump'
|
||||||
|
|
|
@ -73,6 +73,7 @@ JSON or table format'
|
||||||
'timespan:Parse a systemd syntax timespan'
|
'timespan:Parse a systemd syntax timespan'
|
||||||
'security:Analyze security settings of a service'
|
'security:Analyze security settings of a service'
|
||||||
'inspect-elf:Parse and print ELF package metadata'
|
'inspect-elf:Parse and print ELF package metadata'
|
||||||
|
'has-tpm2:Report whether TPM2 support is available'
|
||||||
# log-level, log-target, service-watchdogs have been deprecated
|
# log-level, log-target, service-watchdogs have been deprecated
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ int verb_pcrs(int argc, char *argv[], void *userdata) {
|
||||||
const char *alg = NULL;
|
const char *alg = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (tpm2_support() != TPM2_SUPPORT_FULL)
|
if (!tpm2_is_fully_supported())
|
||||||
log_notice("System lacks full TPM2 support, not showing PCR state.");
|
log_notice("System lacks full TPM2 support, not showing PCR state.");
|
||||||
else {
|
else {
|
||||||
r = get_pcr_alg(&alg);
|
r = get_pcr_alg(&alg);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "bus-map-properties.h"
|
#include "bus-map-properties.h"
|
||||||
#include "bus-unit-util.h"
|
#include "bus-unit-util.h"
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
|
#include "conf-files.h"
|
||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
#include "env-util.h"
|
#include "env-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
|
|
|
@ -469,3 +469,30 @@ int conf_file_read(
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int find_portable_profile(const char *name, const char *unit, char **ret_path) {
|
||||||
|
const char *dot;
|
||||||
|
|
||||||
|
assert(name);
|
||||||
|
assert(ret_path);
|
||||||
|
|
||||||
|
assert_se(dot = strrchr(unit, '.'));
|
||||||
|
|
||||||
|
NULSTR_FOREACH(p, PORTABLE_PROFILE_DIRS) {
|
||||||
|
_cleanup_free_ char *joined = NULL;
|
||||||
|
|
||||||
|
joined = strjoin(p, "/", name, "/", dot + 1, ".conf");
|
||||||
|
if (!joined)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (laccess(joined, F_OK) >= 0) {
|
||||||
|
*ret_path = TAKE_PTR(joined);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno != ENOENT)
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
|
@ -47,3 +47,9 @@ int conf_file_read(
|
||||||
void *userdata,
|
void *userdata,
|
||||||
bool ignore_enoent,
|
bool ignore_enoent,
|
||||||
bool *invalid_config);
|
bool *invalid_config);
|
||||||
|
|
||||||
|
#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
|
||||||
|
#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network")
|
||||||
|
|
||||||
|
#define PORTABLE_PROFILE_DIRS CONF_PATHS_NULSTR("systemd/portable/profile")
|
||||||
|
int find_portable_profile(const char *name, const char *unit, char **ret_path);
|
||||||
|
|
|
@ -70,7 +70,6 @@ basic_sources = files(
|
||||||
'ordered-set.c',
|
'ordered-set.c',
|
||||||
'os-util.c',
|
'os-util.c',
|
||||||
'parse-util.c',
|
'parse-util.c',
|
||||||
'path-lookup.c',
|
|
||||||
'path-util.c',
|
'path-util.c',
|
||||||
'percent-util.c',
|
'percent-util.c',
|
||||||
'pidref.c',
|
'pidref.c',
|
||||||
|
@ -106,7 +105,6 @@ basic_sources = files(
|
||||||
'uid-classification.c',
|
'uid-classification.c',
|
||||||
'uid-range.c',
|
'uid-range.c',
|
||||||
'unit-def.c',
|
'unit-def.c',
|
||||||
'unit-file.c',
|
|
||||||
'unit-name.c',
|
'unit-name.c',
|
||||||
'user-util.c',
|
'user-util.c',
|
||||||
'utf8.c',
|
'utf8.c',
|
||||||
|
|
|
@ -1,930 +0,0 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "alloc-util.h"
|
|
||||||
#include "fs-util.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "macro.h"
|
|
||||||
#include "nulstr-util.h"
|
|
||||||
#include "path-lookup.h"
|
|
||||||
#include "path-util.h"
|
|
||||||
#include "stat-util.h"
|
|
||||||
#include "string-util.h"
|
|
||||||
#include "strv.h"
|
|
||||||
#include "tmpfile-util.h"
|
|
||||||
#include "user-util.h"
|
|
||||||
|
|
||||||
int xdg_user_runtime_dir(char **ret, const char *suffix) {
|
|
||||||
const char *e;
|
|
||||||
char *j;
|
|
||||||
|
|
||||||
assert(ret);
|
|
||||||
assert(suffix);
|
|
||||||
|
|
||||||
e = getenv("XDG_RUNTIME_DIR");
|
|
||||||
if (!e)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
j = path_join(e, suffix);
|
|
||||||
if (!j)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret = j;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdg_user_config_dir(char **ret, const char *suffix) {
|
|
||||||
_cleanup_free_ char *j = NULL;
|
|
||||||
const char *e;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
e = getenv("XDG_CONFIG_HOME");
|
|
||||||
if (e) {
|
|
||||||
j = path_join(e, suffix);
|
|
||||||
if (!j)
|
|
||||||
return -ENOMEM;
|
|
||||||
} else {
|
|
||||||
r = get_home_dir(&j);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (!path_extend(&j, "/.config", suffix))
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(j);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int xdg_user_data_dir(char **ret, const char *suffix) {
|
|
||||||
_cleanup_free_ char *j = NULL;
|
|
||||||
const char *e;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(ret);
|
|
||||||
assert(suffix);
|
|
||||||
|
|
||||||
/* We don't treat /etc/xdg/systemd here as the spec
|
|
||||||
* suggests because we assume that is a link to
|
|
||||||
* /etc/systemd/ anyway. */
|
|
||||||
|
|
||||||
e = getenv("XDG_DATA_HOME");
|
|
||||||
if (e) {
|
|
||||||
j = path_join(e, suffix);
|
|
||||||
if (!j)
|
|
||||||
return -ENOMEM;
|
|
||||||
} else {
|
|
||||||
r = get_home_dir(&j);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (!path_extend(&j, "/.local/share", suffix))
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(j);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int runtime_directory(char **ret, RuntimeScope scope, const char *suffix) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(ret);
|
|
||||||
assert(suffix);
|
|
||||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
|
||||||
|
|
||||||
/* Accept $RUNTIME_DIRECTORY as authoritative
|
|
||||||
* If its missing apply the suffix to /run or $XDG_RUNTIME_DIR
|
|
||||||
* if we are in a user runtime scope.
|
|
||||||
*
|
|
||||||
* Return value indicates whether the suffix was applied or not */
|
|
||||||
|
|
||||||
const char *e = secure_getenv("RUNTIME_DIRECTORY");
|
|
||||||
if (e)
|
|
||||||
return strdup_to(ret, e);
|
|
||||||
|
|
||||||
if (scope == RUNTIME_SCOPE_USER) {
|
|
||||||
r = xdg_user_runtime_dir(ret, suffix);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
} else {
|
|
||||||
char *d = path_join("/run", suffix);
|
|
||||||
if (!d)
|
|
||||||
return -ENOMEM;
|
|
||||||
*ret = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* const user_data_unit_paths[] = {
|
|
||||||
"/usr/local/lib/systemd/user",
|
|
||||||
"/usr/local/share/systemd/user",
|
|
||||||
USER_DATA_UNIT_DIR,
|
|
||||||
"/usr/lib/systemd/user",
|
|
||||||
"/usr/share/systemd/user",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* const user_config_unit_paths[] = {
|
|
||||||
USER_CONFIG_UNIT_DIR,
|
|
||||||
"/etc/systemd/user",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
|
|
||||||
/* Implement the mechanisms defined in
|
|
||||||
*
|
|
||||||
* https://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
|
|
||||||
*
|
|
||||||
* We look in both the config and the data dirs because we
|
|
||||||
* want to encourage that distributors ship their unit files
|
|
||||||
* as data, and allow overriding as configuration.
|
|
||||||
*/
|
|
||||||
const char *e;
|
|
||||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
|
||||||
|
|
||||||
e = getenv("XDG_CONFIG_DIRS");
|
|
||||||
if (e)
|
|
||||||
config_dirs = strv_split(e, ":");
|
|
||||||
else
|
|
||||||
config_dirs = strv_new("/etc/xdg");
|
|
||||||
if (!config_dirs)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
e = getenv("XDG_DATA_DIRS");
|
|
||||||
if (e)
|
|
||||||
data_dirs = strv_split(e, ":");
|
|
||||||
else
|
|
||||||
data_dirs = strv_new("/usr/local/share",
|
|
||||||
"/usr/share");
|
|
||||||
if (!data_dirs)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret_config_dirs = TAKE_PTR(config_dirs);
|
|
||||||
*ret_data_dirs = TAKE_PTR(data_dirs);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char** user_dirs(
|
|
||||||
const char *persistent_config,
|
|
||||||
const char *runtime_config,
|
|
||||||
const char *global_persistent_config,
|
|
||||||
const char *global_runtime_config,
|
|
||||||
const char *generator,
|
|
||||||
const char *generator_early,
|
|
||||||
const char *generator_late,
|
|
||||||
const char *transient,
|
|
||||||
const char *persistent_control,
|
|
||||||
const char *runtime_control) {
|
|
||||||
|
|
||||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
|
||||||
_cleanup_free_ char *data_home = NULL;
|
|
||||||
_cleanup_strv_free_ char **res = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = xdg_user_dirs(&config_dirs, &data_dirs);
|
|
||||||
if (r < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
r = xdg_user_data_dir(&data_home, "/systemd/user");
|
|
||||||
if (r < 0 && r != -ENXIO)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Now merge everything we found. */
|
|
||||||
if (strv_extend_many(
|
|
||||||
&res,
|
|
||||||
persistent_control,
|
|
||||||
runtime_control,
|
|
||||||
transient,
|
|
||||||
generator_early,
|
|
||||||
persistent_config) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/systemd/user") < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* global config has lower priority than the user config of the same type */
|
|
||||||
if (strv_extend(&res, global_persistent_config) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strv_extend_many(
|
|
||||||
&res,
|
|
||||||
runtime_config,
|
|
||||||
global_runtime_config,
|
|
||||||
generator,
|
|
||||||
data_home) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/systemd/user") < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strv_extend(&res, generator_late) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (path_strv_make_absolute_cwd(res) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return TAKE_PTR(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool path_is_user_data_dir(const char *path) {
|
|
||||||
assert(path);
|
|
||||||
|
|
||||||
return strv_contains((char**) user_data_unit_paths, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool path_is_user_config_dir(const char *path) {
|
|
||||||
assert(path);
|
|
||||||
|
|
||||||
return strv_contains((char**) user_config_unit_paths, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acquire_generator_dirs(
|
|
||||||
RuntimeScope scope,
|
|
||||||
const char *tempdir,
|
|
||||||
char **generator,
|
|
||||||
char **generator_early,
|
|
||||||
char **generator_late) {
|
|
||||||
|
|
||||||
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *p = NULL;
|
|
||||||
const char *prefix;
|
|
||||||
|
|
||||||
assert(generator);
|
|
||||||
assert(generator_early);
|
|
||||||
assert(generator_late);
|
|
||||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
|
||||||
|
|
||||||
if (scope == RUNTIME_SCOPE_GLOBAL)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
if (tempdir)
|
|
||||||
prefix = tempdir;
|
|
||||||
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
|
||||||
prefix = "/run/systemd";
|
|
||||||
else {
|
|
||||||
/* RUNTIME_SCOPE_USER */
|
|
||||||
const char *e;
|
|
||||||
|
|
||||||
e = getenv("XDG_RUNTIME_DIR");
|
|
||||||
if (!e)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
p = path_join(e, "/systemd");
|
|
||||||
if (!p)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
prefix = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = path_join(prefix, "generator");
|
|
||||||
if (!x)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
y = path_join(prefix, "generator.early");
|
|
||||||
if (!y)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
z = path_join(prefix, "generator.late");
|
|
||||||
if (!z)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*generator = TAKE_PTR(x);
|
|
||||||
*generator_early = TAKE_PTR(y);
|
|
||||||
*generator_late = TAKE_PTR(z);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acquire_transient_dir(
|
|
||||||
RuntimeScope scope,
|
|
||||||
const char *tempdir,
|
|
||||||
char **ret) {
|
|
||||||
|
|
||||||
char *transient;
|
|
||||||
|
|
||||||
assert(ret);
|
|
||||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
|
||||||
|
|
||||||
if (scope == RUNTIME_SCOPE_GLOBAL)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
if (tempdir)
|
|
||||||
transient = path_join(tempdir, "transient");
|
|
||||||
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
|
||||||
transient = strdup("/run/systemd/transient");
|
|
||||||
else
|
|
||||||
return xdg_user_runtime_dir(ret, "/systemd/transient");
|
|
||||||
|
|
||||||
if (!transient)
|
|
||||||
return -ENOMEM;
|
|
||||||
*ret = transient;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acquire_config_dirs(RuntimeScope scope, char **persistent, char **runtime) {
|
|
||||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(persistent);
|
|
||||||
assert(runtime);
|
|
||||||
|
|
||||||
switch (scope) {
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_SYSTEM:
|
|
||||||
a = strdup(SYSTEM_CONFIG_UNIT_DIR);
|
|
||||||
b = strdup("/run/systemd/system");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_GLOBAL:
|
|
||||||
a = strdup(USER_CONFIG_UNIT_DIR);
|
|
||||||
b = strdup("/run/systemd/user");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_USER:
|
|
||||||
r = xdg_user_config_dir(&a, "/systemd/user");
|
|
||||||
if (r < 0 && r != -ENXIO)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = xdg_user_runtime_dir(runtime, "/systemd/user");
|
|
||||||
if (r < 0) {
|
|
||||||
if (r != -ENXIO)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
|
|
||||||
* directory to NULL */
|
|
||||||
*runtime = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*persistent = TAKE_PTR(a);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!a || !b)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*persistent = TAKE_PTR(a);
|
|
||||||
*runtime = TAKE_PTR(b);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acquire_control_dirs(RuntimeScope scope, char **persistent, char **runtime) {
|
|
||||||
_cleanup_free_ char *a = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(persistent);
|
|
||||||
assert(runtime);
|
|
||||||
|
|
||||||
switch (scope) {
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_SYSTEM: {
|
|
||||||
_cleanup_free_ char *b = NULL;
|
|
||||||
|
|
||||||
a = strdup("/etc/systemd/system.control");
|
|
||||||
if (!a)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
b = strdup("/run/systemd/system.control");
|
|
||||||
if (!b)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*runtime = TAKE_PTR(b);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_USER:
|
|
||||||
r = xdg_user_config_dir(&a, "/systemd/user.control");
|
|
||||||
if (r < 0 && r != -ENXIO)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = xdg_user_runtime_dir(runtime, "/systemd/user.control");
|
|
||||||
if (r < 0) {
|
|
||||||
if (r != -ENXIO)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
|
|
||||||
* NULL */
|
|
||||||
*runtime = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_GLOBAL:
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
*persistent = TAKE_PTR(a);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acquire_attached_dirs(
|
|
||||||
RuntimeScope scope,
|
|
||||||
char **ret_persistent,
|
|
||||||
char **ret_runtime) {
|
|
||||||
|
|
||||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
|
||||||
|
|
||||||
assert(ret_persistent);
|
|
||||||
assert(ret_runtime);
|
|
||||||
|
|
||||||
/* Portable services are not available to regular users for now. */
|
|
||||||
if (scope != RUNTIME_SCOPE_SYSTEM)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
a = strdup("/etc/systemd/system.attached");
|
|
||||||
if (!a)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
b = strdup("/run/systemd/system.attached");
|
|
||||||
if (!b)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret_persistent = TAKE_PTR(a);
|
|
||||||
*ret_runtime = TAKE_PTR(b);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int patch_root_prefix(char **p, const char *root_dir) {
|
|
||||||
char *c;
|
|
||||||
|
|
||||||
assert(p);
|
|
||||||
|
|
||||||
if (!*p)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
c = path_join(root_dir, *p);
|
|
||||||
if (!c)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
free_and_replace(*p, c);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int patch_root_prefix_strv(char **l, const char *root_dir) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (!root_dir)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
STRV_FOREACH(i, l) {
|
|
||||||
r = patch_root_prefix(i, root_dir);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_paths_from_environ(const char *var, char ***paths, bool *append) {
|
|
||||||
const char *e;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(var);
|
|
||||||
assert(paths);
|
|
||||||
assert(append);
|
|
||||||
|
|
||||||
*append = false;
|
|
||||||
|
|
||||||
e = getenv(var);
|
|
||||||
if (e) {
|
|
||||||
const char *k;
|
|
||||||
|
|
||||||
k = endswith(e, ":");
|
|
||||||
if (k) {
|
|
||||||
e = strndupa_safe(e, k - e);
|
|
||||||
*append = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: empty components in other places should be rejected. */
|
|
||||||
|
|
||||||
r = path_split_and_make_absolute(e, paths);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lookup_paths_init(
|
|
||||||
LookupPaths *lp,
|
|
||||||
RuntimeScope scope,
|
|
||||||
LookupPathsFlags flags,
|
|
||||||
const char *root_dir) {
|
|
||||||
|
|
||||||
_cleanup_(rmdir_and_freep) char *tempdir = NULL;
|
|
||||||
_cleanup_free_ char
|
|
||||||
*root = NULL,
|
|
||||||
*persistent_config = NULL, *runtime_config = NULL,
|
|
||||||
*global_persistent_config = NULL, *global_runtime_config = NULL,
|
|
||||||
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
|
|
||||||
*transient = NULL,
|
|
||||||
*persistent_control = NULL, *runtime_control = NULL,
|
|
||||||
*persistent_attached = NULL, *runtime_attached = NULL;
|
|
||||||
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
|
|
||||||
_cleanup_strv_free_ char **paths = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(lp);
|
|
||||||
assert(scope >= 0);
|
|
||||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
|
||||||
|
|
||||||
if (!empty_or_root(root_dir)) {
|
|
||||||
if (scope == RUNTIME_SCOPE_USER)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
r = is_dir(root_dir, true);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
if (r == 0)
|
|
||||||
return -ENOTDIR;
|
|
||||||
|
|
||||||
root = strdup(root_dir);
|
|
||||||
if (!root)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
|
|
||||||
r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
|
|
||||||
if (r < 0)
|
|
||||||
return log_debug_errno(r, "Failed to create temporary directory: %m");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
|
|
||||||
r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (scope == RUNTIME_SCOPE_USER) {
|
|
||||||
r = acquire_config_dirs(RUNTIME_SCOPE_GLOBAL, &global_persistent_config, &global_runtime_config);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
|
|
||||||
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
|
||||||
r = acquire_generator_dirs(scope, tempdir,
|
|
||||||
&generator, &generator_early, &generator_late);
|
|
||||||
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
|
||||||
r = acquire_transient_dir(scope, tempdir, &transient);
|
|
||||||
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
|
|
||||||
r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
|
|
||||||
if (r < 0 && r != -EOPNOTSUPP)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = acquire_attached_dirs(scope, &persistent_attached, &runtime_attached);
|
|
||||||
if (r < 0 && r != -EOPNOTSUPP)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* First priority is whatever has been passed to us via env vars */
|
|
||||||
r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (!paths || append) {
|
|
||||||
/* Let's figure something out. */
|
|
||||||
|
|
||||||
_cleanup_strv_free_ char **add = NULL;
|
|
||||||
|
|
||||||
/* For the user units we include share/ in the search
|
|
||||||
* path in order to comply with the XDG basedir spec.
|
|
||||||
* For the system stuff we avoid such nonsense. OTOH
|
|
||||||
* we include /lib in the search path for the system
|
|
||||||
* stuff but avoid it for user stuff. */
|
|
||||||
|
|
||||||
switch (scope) {
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_SYSTEM:
|
|
||||||
add = strv_new(
|
|
||||||
/* If you modify this you also want to modify
|
|
||||||
* systemdsystemunitpath= in systemd.pc.in! */
|
|
||||||
STRV_IFNOTNULL(persistent_control),
|
|
||||||
STRV_IFNOTNULL(runtime_control),
|
|
||||||
STRV_IFNOTNULL(transient),
|
|
||||||
STRV_IFNOTNULL(generator_early),
|
|
||||||
persistent_config,
|
|
||||||
SYSTEM_CONFIG_UNIT_DIR,
|
|
||||||
"/etc/systemd/system",
|
|
||||||
STRV_IFNOTNULL(persistent_attached),
|
|
||||||
runtime_config,
|
|
||||||
"/run/systemd/system",
|
|
||||||
STRV_IFNOTNULL(runtime_attached),
|
|
||||||
STRV_IFNOTNULL(generator),
|
|
||||||
"/usr/local/lib/systemd/system",
|
|
||||||
SYSTEM_DATA_UNIT_DIR,
|
|
||||||
"/usr/lib/systemd/system",
|
|
||||||
/* To be used ONLY for images which might be legacy split-usr */
|
|
||||||
STRV_IFNOTNULL(flags & LOOKUP_PATHS_SPLIT_USR ? "/lib/systemd/system" : NULL),
|
|
||||||
STRV_IFNOTNULL(generator_late));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_GLOBAL:
|
|
||||||
add = strv_new(
|
|
||||||
/* If you modify this you also want to modify
|
|
||||||
* systemduserunitpath= in systemd.pc.in, and
|
|
||||||
* the arrays in user_dirs() above! */
|
|
||||||
STRV_IFNOTNULL(persistent_control),
|
|
||||||
STRV_IFNOTNULL(runtime_control),
|
|
||||||
STRV_IFNOTNULL(transient),
|
|
||||||
STRV_IFNOTNULL(generator_early),
|
|
||||||
persistent_config,
|
|
||||||
USER_CONFIG_UNIT_DIR,
|
|
||||||
"/etc/systemd/user",
|
|
||||||
runtime_config,
|
|
||||||
"/run/systemd/user",
|
|
||||||
STRV_IFNOTNULL(generator),
|
|
||||||
"/usr/local/share/systemd/user",
|
|
||||||
"/usr/share/systemd/user",
|
|
||||||
"/usr/local/lib/systemd/user",
|
|
||||||
USER_DATA_UNIT_DIR,
|
|
||||||
"/usr/lib/systemd/user",
|
|
||||||
STRV_IFNOTNULL(generator_late));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_USER:
|
|
||||||
add = user_dirs(persistent_config, runtime_config,
|
|
||||||
global_persistent_config, global_runtime_config,
|
|
||||||
generator, generator_early, generator_late,
|
|
||||||
transient,
|
|
||||||
persistent_control, runtime_control);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!add)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (paths) {
|
|
||||||
r = strv_extend_strv(&paths, add, true);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
} else
|
|
||||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
|
||||||
* and don't have to copy anything */
|
|
||||||
paths = TAKE_PTR(add);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = patch_root_prefix(&persistent_config, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = patch_root_prefix(&runtime_config, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = patch_root_prefix(&generator, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = patch_root_prefix(&generator_early, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = patch_root_prefix(&generator_late, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = patch_root_prefix(&transient, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = patch_root_prefix(&persistent_control, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = patch_root_prefix(&runtime_control, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = patch_root_prefix(&persistent_attached, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = patch_root_prefix(&runtime_attached, root);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = patch_root_prefix_strv(paths, root);
|
|
||||||
if (r < 0)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*lp = (LookupPaths) {
|
|
||||||
.search_path = strv_uniq(TAKE_PTR(paths)),
|
|
||||||
|
|
||||||
.persistent_config = TAKE_PTR(persistent_config),
|
|
||||||
.runtime_config = TAKE_PTR(runtime_config),
|
|
||||||
|
|
||||||
.generator = TAKE_PTR(generator),
|
|
||||||
.generator_early = TAKE_PTR(generator_early),
|
|
||||||
.generator_late = TAKE_PTR(generator_late),
|
|
||||||
|
|
||||||
.transient = TAKE_PTR(transient),
|
|
||||||
|
|
||||||
.persistent_control = TAKE_PTR(persistent_control),
|
|
||||||
.runtime_control = TAKE_PTR(runtime_control),
|
|
||||||
|
|
||||||
.persistent_attached = TAKE_PTR(persistent_attached),
|
|
||||||
.runtime_attached = TAKE_PTR(runtime_attached),
|
|
||||||
|
|
||||||
.root_dir = TAKE_PTR(root),
|
|
||||||
.temporary_dir = TAKE_PTR(tempdir),
|
|
||||||
};
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = lookup_paths_init(lp, scope, flags, root_dir);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to initialize unit search paths%s%s: %m",
|
|
||||||
isempty(root_dir) ? "" : " for root directory ", strempty(root_dir));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lookup_paths_done(LookupPaths *lp) {
|
|
||||||
assert(lp);
|
|
||||||
|
|
||||||
lp->search_path = strv_free(lp->search_path);
|
|
||||||
|
|
||||||
lp->persistent_config = mfree(lp->persistent_config);
|
|
||||||
lp->runtime_config = mfree(lp->runtime_config);
|
|
||||||
|
|
||||||
lp->persistent_attached = mfree(lp->persistent_attached);
|
|
||||||
lp->runtime_attached = mfree(lp->runtime_attached);
|
|
||||||
|
|
||||||
lp->generator = mfree(lp->generator);
|
|
||||||
lp->generator_early = mfree(lp->generator_early);
|
|
||||||
lp->generator_late = mfree(lp->generator_late);
|
|
||||||
|
|
||||||
lp->transient = mfree(lp->transient);
|
|
||||||
|
|
||||||
lp->persistent_control = mfree(lp->persistent_control);
|
|
||||||
lp->runtime_control = mfree(lp->runtime_control);
|
|
||||||
|
|
||||||
lp->root_dir = mfree(lp->root_dir);
|
|
||||||
lp->temporary_dir = mfree(lp->temporary_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
void lookup_paths_log(LookupPaths *lp) {
|
|
||||||
assert(lp);
|
|
||||||
|
|
||||||
if (strv_isempty(lp->search_path)) {
|
|
||||||
log_debug("Ignoring unit files.");
|
|
||||||
lp->search_path = strv_free(lp->search_path);
|
|
||||||
} else {
|
|
||||||
_cleanup_free_ char *t = NULL;
|
|
||||||
|
|
||||||
t = strv_join(lp->search_path, "\n\t");
|
|
||||||
log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char **generator_binary_paths(RuntimeScope scope) {
|
|
||||||
bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
|
|
||||||
_cleanup_strv_free_ char **paths = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* First priority is whatever has been passed to us via env vars */
|
|
||||||
r = get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths, &append);
|
|
||||||
if (r < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!paths || append) {
|
|
||||||
_cleanup_strv_free_ char **add = NULL;
|
|
||||||
|
|
||||||
switch (scope) {
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_SYSTEM:
|
|
||||||
add = strv_new("/run/systemd/system-generators",
|
|
||||||
"/etc/systemd/system-generators",
|
|
||||||
"/usr/local/lib/systemd/system-generators",
|
|
||||||
SYSTEM_GENERATOR_DIR);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_GLOBAL:
|
|
||||||
case RUNTIME_SCOPE_USER:
|
|
||||||
add = strv_new("/run/systemd/user-generators",
|
|
||||||
"/etc/systemd/user-generators",
|
|
||||||
"/usr/local/lib/systemd/user-generators",
|
|
||||||
USER_GENERATOR_DIR);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
if (!add)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (paths) {
|
|
||||||
r = strv_extend_strv(&paths, add, true);
|
|
||||||
if (r < 0)
|
|
||||||
return NULL;
|
|
||||||
} else
|
|
||||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
|
||||||
* and don't have to copy anything */
|
|
||||||
paths = TAKE_PTR(add);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TAKE_PTR(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
char **env_generator_binary_paths(RuntimeScope runtime_scope) {
|
|
||||||
_cleanup_strv_free_ char **paths = NULL, **add = NULL;
|
|
||||||
bool append = false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* First priority is whatever has been passed to us via env vars */
|
|
||||||
r = get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths, &append);
|
|
||||||
if (r < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!paths || append) {
|
|
||||||
switch (runtime_scope) {
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_SYSTEM:
|
|
||||||
add = strv_new("/run/systemd/system-environment-generators",
|
|
||||||
"/etc/systemd/system-environment-generators",
|
|
||||||
"/usr/local/lib/systemd/system-environment-generators",
|
|
||||||
SYSTEM_ENV_GENERATOR_DIR);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RUNTIME_SCOPE_USER:
|
|
||||||
add = strv_new("/run/systemd/user-environment-generators",
|
|
||||||
"/etc/systemd/user-environment-generators",
|
|
||||||
"/usr/local/lib/systemd/user-environment-generators",
|
|
||||||
USER_ENV_GENERATOR_DIR);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
if (!add)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paths) {
|
|
||||||
r = strv_extend_strv(&paths, add, true);
|
|
||||||
if (r < 0)
|
|
||||||
return NULL;
|
|
||||||
} else
|
|
||||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
|
||||||
* and don't have to copy anything */
|
|
||||||
paths = TAKE_PTR(add);
|
|
||||||
|
|
||||||
return TAKE_PTR(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_portable_profile(const char *name, const char *unit, char **ret_path) {
|
|
||||||
const char *dot;
|
|
||||||
|
|
||||||
assert(name);
|
|
||||||
assert(ret_path);
|
|
||||||
|
|
||||||
assert_se(dot = strrchr(unit, '.'));
|
|
||||||
|
|
||||||
NULSTR_FOREACH(p, PORTABLE_PROFILE_DIRS) {
|
|
||||||
_cleanup_free_ char *joined = NULL;
|
|
||||||
|
|
||||||
joined = strjoin(p, "/", name, "/", dot + 1, ".conf");
|
|
||||||
if (!joined)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (laccess(joined, F_OK) >= 0) {
|
|
||||||
*ret_path = TAKE_PTR(joined);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errno != ENOENT)
|
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
|
@ -411,7 +411,6 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||||
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
|
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
|
||||||
*current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL;
|
*current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL;
|
||||||
uint64_t loader_features = 0, stub_features = 0;
|
uint64_t loader_features = 0, stub_features = 0;
|
||||||
Tpm2Support s;
|
|
||||||
int have;
|
int have;
|
||||||
|
|
||||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
|
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
|
||||||
|
@ -440,7 +439,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||||
else
|
else
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
s = tpm2_support();
|
Tpm2Support s = tpm2_support_full(TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER);
|
||||||
printf(" TPM2 Support: %s%s%s\n",
|
printf(" TPM2 Support: %s%s%s\n",
|
||||||
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
|
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
|
||||||
(s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
|
(s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
|
||||||
|
|
|
@ -450,7 +450,7 @@ static size_t pe_section_table_find_profile_length(
|
||||||
assert(start >= section_table);
|
assert(start >= section_table);
|
||||||
assert(start < section_table + n_section_table);
|
assert(start < section_table + n_section_table);
|
||||||
|
|
||||||
/* Look for the next .profile (or the end of the table), this is where the the sections for this
|
/* Look for the next .profile (or the end of the table), this is where the sections for this
|
||||||
* profile end. The base profile does not start with a .profile, the others do, hence conditionally
|
* profile end. The base profile does not start with a .profile, the others do, hence conditionally
|
||||||
* skip over the first entry. */
|
* skip over the first entry. */
|
||||||
const PeSectionHeader *e;
|
const PeSectionHeader *e;
|
||||||
|
@ -485,7 +485,7 @@ EFI_STATUS pe_locate_profile_sections(
|
||||||
if (!p)
|
if (!p)
|
||||||
return EFI_NOT_FOUND;
|
return EFI_NOT_FOUND;
|
||||||
|
|
||||||
/* Look for the next .profile (or the end of the table), this is where the the sections for this
|
/* Look for the next .profile (or the end of the table), this is where the sections for this
|
||||||
* profile end. */
|
* profile end. */
|
||||||
size_t n = pe_section_table_find_profile_length(section_table, n_section_table, p, profile);
|
size_t n = pe_section_table_find_profile_length(section_table, n_section_table, p, profile);
|
||||||
|
|
||||||
|
|
|
@ -1005,7 +1005,7 @@ static int validate_stub(void) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (tpm2_support() != TPM2_SUPPORT_FULL)
|
if (!tpm2_is_fully_supported())
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Sorry, system lacks full TPM2 support.");
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Sorry, system lacks full TPM2 support.");
|
||||||
|
|
||||||
r = efi_stub_get_features(&features);
|
r = efi_stub_get_features(&features);
|
||||||
|
|
|
@ -181,7 +181,7 @@ static int manager_find_user_config_paths(char ***ret_files, char ***ret_dirs) {
|
||||||
_cleanup_strv_free_ char **files = NULL, **dirs = NULL;
|
_cleanup_strv_free_ char **files = NULL, **dirs = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = xdg_user_config_dir(&base, "/systemd");
|
r = xdg_user_config_dir("/systemd", &base);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -2478,7 +2478,7 @@ static int initialize_runtime(
|
||||||
/* Create the runtime directory and place the inaccessible device nodes there, if we run in
|
/* Create the runtime directory and place the inaccessible device nodes there, if we run in
|
||||||
* user mode. In system mode mount_setup() already did that. */
|
* user mode. In system mode mount_setup() already did that. */
|
||||||
|
|
||||||
r = xdg_user_runtime_dir(&p, "/systemd");
|
r = xdg_user_runtime_dir("/systemd", &p);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*ret_error_message = "$XDG_RUNTIME_DIR is not set";
|
*ret_error_message = "$XDG_RUNTIME_DIR is not set";
|
||||||
return log_struct_errno(LOG_EMERG, r,
|
return log_struct_errno(LOG_EMERG, r,
|
||||||
|
|
|
@ -1031,7 +1031,7 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags,
|
||||||
r = mkdir_label("/run/systemd/units", 0755);
|
r = mkdir_label("/run/systemd/units", 0755);
|
||||||
else {
|
else {
|
||||||
_cleanup_free_ char *units_path = NULL;
|
_cleanup_free_ char *units_path = NULL;
|
||||||
r = xdg_user_runtime_dir(&units_path, "/systemd/units");
|
r = xdg_user_runtime_dir("/systemd/units", &units_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -5576,12 +5576,13 @@ static int unit_get_invocation_path(Unit *u, char **ret) {
|
||||||
p = strjoin("/run/systemd/units/invocation:", u->id);
|
p = strjoin("/run/systemd/units/invocation:", u->id);
|
||||||
else {
|
else {
|
||||||
_cleanup_free_ char *user_path = NULL;
|
_cleanup_free_ char *user_path = NULL;
|
||||||
r = xdg_user_runtime_dir(&user_path, "/systemd/units/invocation:");
|
|
||||||
|
r = xdg_user_runtime_dir("/systemd/units/invocation:", &user_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
p = strjoin(user_path, u->id);
|
p = strjoin(user_path, u->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p)
|
if (!p)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ sd_journal_sources += [audit_type_to_name]
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
id128_sources = files(
|
sd_id128_sources = files(
|
||||||
'sd-id128/id128-util.c',
|
'sd-id128/id128-util.c',
|
||||||
'sd-id128/sd-id128.c',
|
'sd-id128/sd-id128.c',
|
||||||
)
|
)
|
||||||
|
@ -62,6 +62,41 @@ sd_event_sources = files(
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
|
sd_bus_sources = files(
|
||||||
|
'sd-bus/bus-common-errors.c',
|
||||||
|
'sd-bus/bus-container.c',
|
||||||
|
'sd-bus/bus-control.c',
|
||||||
|
'sd-bus/bus-convenience.c',
|
||||||
|
'sd-bus/bus-creds.c',
|
||||||
|
'sd-bus/bus-dump.c',
|
||||||
|
'sd-bus/bus-error.c',
|
||||||
|
'sd-bus/bus-internal.c',
|
||||||
|
'sd-bus/bus-introspect.c',
|
||||||
|
'sd-bus/bus-kernel.c',
|
||||||
|
'sd-bus/bus-match.c',
|
||||||
|
'sd-bus/bus-message.c',
|
||||||
|
'sd-bus/bus-objects.c',
|
||||||
|
'sd-bus/bus-signature.c',
|
||||||
|
'sd-bus/bus-slot.c',
|
||||||
|
'sd-bus/bus-socket.c',
|
||||||
|
'sd-bus/bus-track.c',
|
||||||
|
'sd-bus/bus-type.c',
|
||||||
|
'sd-bus/sd-bus.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
sd_device_sources = files(
|
||||||
|
'sd-device/device-enumerator.c',
|
||||||
|
'sd-device/device-filter.c',
|
||||||
|
'sd-device/device-monitor.c',
|
||||||
|
'sd-device/device-private.c',
|
||||||
|
'sd-device/device-util.c',
|
||||||
|
'sd-device/sd-device.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
sd_login_sources = files('sd-login/sd-login.c')
|
sd_login_sources = files('sd-login/sd-login.c')
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
@ -83,33 +118,14 @@ sd_varlink_sources = files(
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
libsystemd_sources = files(
|
sd_path_sources = files(
|
||||||
'sd-bus/bus-common-errors.c',
|
'sd-path/path-lookup.c',
|
||||||
'sd-bus/bus-container.c',
|
'sd-path/sd-path.c',
|
||||||
'sd-bus/bus-control.c',
|
)
|
||||||
'sd-bus/bus-convenience.c',
|
|
||||||
'sd-bus/bus-creds.c',
|
############################################################
|
||||||
'sd-bus/bus-dump.c',
|
|
||||||
'sd-bus/bus-error.c',
|
sd_netlink_sources = files(
|
||||||
'sd-bus/bus-internal.c',
|
|
||||||
'sd-bus/bus-introspect.c',
|
|
||||||
'sd-bus/bus-kernel.c',
|
|
||||||
'sd-bus/bus-match.c',
|
|
||||||
'sd-bus/bus-message.c',
|
|
||||||
'sd-bus/bus-objects.c',
|
|
||||||
'sd-bus/bus-signature.c',
|
|
||||||
'sd-bus/bus-slot.c',
|
|
||||||
'sd-bus/bus-socket.c',
|
|
||||||
'sd-bus/bus-track.c',
|
|
||||||
'sd-bus/bus-type.c',
|
|
||||||
'sd-bus/sd-bus.c',
|
|
||||||
'sd-device/device-enumerator.c',
|
|
||||||
'sd-device/device-filter.c',
|
|
||||||
'sd-device/device-monitor.c',
|
|
||||||
'sd-device/device-private.c',
|
|
||||||
'sd-device/device-util.c',
|
|
||||||
'sd-device/sd-device.c',
|
|
||||||
'sd-hwdb/sd-hwdb.c',
|
|
||||||
'sd-netlink/netlink-genl.c',
|
'sd-netlink/netlink-genl.c',
|
||||||
'sd-netlink/netlink-message-nfnl.c',
|
'sd-netlink/netlink-message-nfnl.c',
|
||||||
'sd-netlink/netlink-message-rtnl.c',
|
'sd-netlink/netlink-message-rtnl.c',
|
||||||
|
@ -122,11 +138,24 @@ libsystemd_sources = files(
|
||||||
'sd-netlink/netlink-types.c',
|
'sd-netlink/netlink-types.c',
|
||||||
'sd-netlink/netlink-util.c',
|
'sd-netlink/netlink-util.c',
|
||||||
'sd-netlink/sd-netlink.c',
|
'sd-netlink/sd-netlink.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
sd_network_sources = files(
|
||||||
'sd-network/network-util.c',
|
'sd-network/network-util.c',
|
||||||
'sd-network/sd-network.c',
|
'sd-network/sd-network.c',
|
||||||
'sd-path/sd-path.c',
|
)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
libsystemd_sources = files(
|
||||||
|
'sd-hwdb/sd-hwdb.c',
|
||||||
'sd-resolve/sd-resolve.c',
|
'sd-resolve/sd-resolve.c',
|
||||||
) + sd_journal_sources + id128_sources + sd_daemon_sources + sd_event_sources + sd_login_sources + sd_json_sources + sd_varlink_sources
|
) + sd_journal_sources + sd_id128_sources + sd_daemon_sources \
|
||||||
|
+ sd_event_sources + sd_bus_sources + sd_device_sources \
|
||||||
|
+ sd_login_sources + sd_json_sources + sd_varlink_sources \
|
||||||
|
+ sd_path_sources + sd_netlink_sources + sd_network_sources
|
||||||
|
|
||||||
libsystemd_c_args = ['-fvisibility=default']
|
libsystemd_c_args = ['-fvisibility=default']
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,734 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "fs-util.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "macro.h"
|
||||||
|
#include "nulstr-util.h"
|
||||||
|
#include "path-lookup.h"
|
||||||
|
#include "path-util.h"
|
||||||
|
#include "stat-util.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "strv.h"
|
||||||
|
#include "tmpfile-util.h"
|
||||||
|
#include "user-util.h"
|
||||||
|
|
||||||
|
int user_search_dirs(const char *suffix, char ***ret_config_dirs, char ***ret_data_dirs) {
|
||||||
|
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret_config_dirs);
|
||||||
|
assert(ret_data_dirs);
|
||||||
|
|
||||||
|
r = sd_path_lookup_strv(SD_PATH_SEARCH_CONFIGURATION, suffix, &config_dirs);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_path_lookup_strv(SD_PATH_SEARCH_SHARED, suffix, &data_dirs);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret_config_dirs = TAKE_PTR(config_dirs);
|
||||||
|
*ret_data_dirs = TAKE_PTR(data_dirs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int runtime_directory(RuntimeScope scope, const char *suffix, char **ret) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER));
|
||||||
|
assert(suffix);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
/* Accept $RUNTIME_DIRECTORY as authoritative
|
||||||
|
* If its missing apply the suffix to /run/, or $XDG_RUNTIME_DIR if we are in a user runtime scope.
|
||||||
|
*
|
||||||
|
* Return value indicates whether the suffix was applied or not */
|
||||||
|
|
||||||
|
const char *e = secure_getenv("RUNTIME_DIRECTORY");
|
||||||
|
if (e)
|
||||||
|
return strdup_to(ret, e);
|
||||||
|
|
||||||
|
if (scope == RUNTIME_SCOPE_USER) {
|
||||||
|
r = xdg_user_runtime_dir(suffix, ret);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
|
char *d = path_join("/run", suffix);
|
||||||
|
if (!d)
|
||||||
|
return -ENOMEM;
|
||||||
|
*ret = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* const user_data_unit_paths[] = {
|
||||||
|
"/usr/local/lib/systemd/user",
|
||||||
|
"/usr/local/share/systemd/user",
|
||||||
|
USER_DATA_UNIT_DIR,
|
||||||
|
"/usr/lib/systemd/user",
|
||||||
|
"/usr/share/systemd/user",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* const user_config_unit_paths[] = {
|
||||||
|
USER_CONFIG_UNIT_DIR,
|
||||||
|
"/etc/systemd/user",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
bool path_is_user_data_dir(const char *path) {
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
return path_strv_contains((char* const*) user_data_unit_paths, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool path_is_user_config_dir(const char *path) {
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
return path_strv_contains((char* const*) user_config_unit_paths, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acquire_generator_dirs(
|
||||||
|
RuntimeScope scope,
|
||||||
|
const char *tempdir,
|
||||||
|
char **ret,
|
||||||
|
char **ret_early,
|
||||||
|
char **ret_late) {
|
||||||
|
|
||||||
|
_cleanup_free_ char *prefix_alloc = NULL, *g = NULL, *early = NULL, *late = NULL;
|
||||||
|
const char *prefix;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||||
|
assert(ret);
|
||||||
|
assert(ret_early);
|
||||||
|
assert(ret_late);
|
||||||
|
|
||||||
|
if (scope == RUNTIME_SCOPE_GLOBAL)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (tempdir)
|
||||||
|
prefix = tempdir;
|
||||||
|
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
||||||
|
prefix = "/run/systemd";
|
||||||
|
else { /* RUNTIME_SCOPE_USER */
|
||||||
|
r = xdg_user_runtime_dir("/systemd", &prefix_alloc);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
prefix = prefix_alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
g = path_join(prefix, "generator");
|
||||||
|
if (!g)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
early = path_join(prefix, "generator.early");
|
||||||
|
if (!early)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
late = path_join(prefix, "generator.late");
|
||||||
|
if (!late)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(g);
|
||||||
|
*ret_early = TAKE_PTR(early);
|
||||||
|
*ret_late = TAKE_PTR(late);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acquire_transient_dir(RuntimeScope scope, const char *tempdir, char **ret) {
|
||||||
|
char *transient;
|
||||||
|
|
||||||
|
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (scope == RUNTIME_SCOPE_GLOBAL)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (tempdir)
|
||||||
|
transient = path_join(tempdir, "transient");
|
||||||
|
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
||||||
|
transient = strdup("/run/systemd/transient");
|
||||||
|
else /* RUNTIME_SCOPE_USER */
|
||||||
|
return xdg_user_runtime_dir("/systemd/transient", ret);
|
||||||
|
|
||||||
|
if (!transient)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = transient;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum LookupDirType {
|
||||||
|
LOOKUP_DIR_CONFIG,
|
||||||
|
LOOKUP_DIR_CONTROL,
|
||||||
|
LOOKUP_DIR_ATTACHED,
|
||||||
|
_LOOKUP_DIR_MAX,
|
||||||
|
_LOOKUP_DIR_INVALID = -EINVAL,
|
||||||
|
} LookupDirType;
|
||||||
|
|
||||||
|
static int acquire_lookup_dirs(
|
||||||
|
LookupDirType type,
|
||||||
|
RuntimeScope scope,
|
||||||
|
char **ret_persistent,
|
||||||
|
char **ret_runtime) {
|
||||||
|
|
||||||
|
/* RUNTIME_SCOPE_USER dirs are relative to XDG_CONFIG_DIR and XDG_RUNTIME_DIR, respectively */
|
||||||
|
static const struct {
|
||||||
|
const char *persistent;
|
||||||
|
const char *runtime;
|
||||||
|
} dirs[_LOOKUP_DIR_MAX][_RUNTIME_SCOPE_MAX] = {
|
||||||
|
[LOOKUP_DIR_CONFIG] = {
|
||||||
|
[RUNTIME_SCOPE_SYSTEM] = { SYSTEM_CONFIG_UNIT_DIR, "/run/systemd/system" },
|
||||||
|
[RUNTIME_SCOPE_GLOBAL] = { USER_CONFIG_UNIT_DIR, "/run/systemd/user" },
|
||||||
|
[RUNTIME_SCOPE_USER] = { "systemd/user", "systemd/user" },
|
||||||
|
},
|
||||||
|
[LOOKUP_DIR_CONTROL] = {
|
||||||
|
[RUNTIME_SCOPE_SYSTEM] = { "/etc/systemd/system.control", "/run/systemd/system.control" },
|
||||||
|
[RUNTIME_SCOPE_USER] = { "systemd/user.control", "systemd/user.control" },
|
||||||
|
},
|
||||||
|
[LOOKUP_DIR_ATTACHED] = {
|
||||||
|
[RUNTIME_SCOPE_SYSTEM] = { "/etc/systemd/system.attached", "/run/systemd/system.attached" },
|
||||||
|
/* Portable services are not available to regular users for now. */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
_cleanup_free_ char *a = NULL, *b = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(type >= 0 && type < _LOOKUP_DIR_MAX);
|
||||||
|
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||||
|
assert(ret_persistent);
|
||||||
|
assert(ret_runtime);
|
||||||
|
|
||||||
|
const char *persistent = dirs[type][scope].persistent;
|
||||||
|
const char *runtime = dirs[type][scope].runtime;
|
||||||
|
assert(!persistent == !runtime);
|
||||||
|
|
||||||
|
if (!persistent)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
switch (scope) {
|
||||||
|
|
||||||
|
case RUNTIME_SCOPE_SYSTEM:
|
||||||
|
case RUNTIME_SCOPE_GLOBAL:
|
||||||
|
a = strdup(persistent);
|
||||||
|
b = strdup(runtime);
|
||||||
|
if (!a || !b)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret_persistent = TAKE_PTR(a);
|
||||||
|
*ret_runtime = TAKE_PTR(b);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case RUNTIME_SCOPE_USER:
|
||||||
|
r = xdg_user_config_dir(persistent, &a);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = xdg_user_runtime_dir(runtime, ret_runtime);
|
||||||
|
if (r < 0) {
|
||||||
|
if (r != -ENXIO)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
|
||||||
|
* directory to NULL */
|
||||||
|
*ret_runtime = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_persistent = TAKE_PTR(a);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int patch_root_prefix(char **p, const char *root_dir) {
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
if (!root_dir)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!*p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
c = path_join(root_dir, *p);
|
||||||
|
if (!c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
free_and_replace(*p, c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int patch_root_prefix_strv(char **l, const char *root_dir) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!root_dir)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
STRV_FOREACH(i, l) {
|
||||||
|
r = patch_root_prefix(i, root_dir);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_paths_from_environ(const char *var, char ***ret) {
|
||||||
|
const char *e;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(var);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
e = getenv(var);
|
||||||
|
if (!e) {
|
||||||
|
*ret = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool append = endswith(e, ":"); /* Whether to append the normal search paths after what's obtained
|
||||||
|
from envvar */
|
||||||
|
|
||||||
|
/* FIXME: empty components in other places should be rejected. */
|
||||||
|
|
||||||
|
r = path_split_and_make_absolute(e, ret);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return append;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char** user_unit_search_dirs(
|
||||||
|
const char *persistent_config,
|
||||||
|
const char *runtime_config,
|
||||||
|
const char *global_persistent_config,
|
||||||
|
const char *global_runtime_config,
|
||||||
|
const char *generator,
|
||||||
|
const char *generator_early,
|
||||||
|
const char *generator_late,
|
||||||
|
const char *transient,
|
||||||
|
const char *persistent_control,
|
||||||
|
const char *runtime_control) {
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **paths = NULL, **config_dirs = NULL, **data_dirs = NULL;
|
||||||
|
|
||||||
|
/* The returned strv might contain duplicates, and we expect caller to filter them. */
|
||||||
|
|
||||||
|
assert(persistent_config);
|
||||||
|
assert(global_persistent_config);
|
||||||
|
assert(global_runtime_config);
|
||||||
|
assert(persistent_control);
|
||||||
|
|
||||||
|
if (user_search_dirs("/systemd/user", &config_dirs, &data_dirs) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
paths = strv_new(persistent_control,
|
||||||
|
STRV_IFNOTNULL(runtime_control),
|
||||||
|
STRV_IFNOTNULL(transient),
|
||||||
|
STRV_IFNOTNULL(generator_early),
|
||||||
|
persistent_config);
|
||||||
|
if (!paths)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_strv(&paths, config_dirs, /* filter_duplicates = */ false) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* global config has lower priority than the user config of the same type */
|
||||||
|
if (strv_extend(&paths, global_persistent_config) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_strv(&paths, (char* const*) user_config_unit_paths, /* filter_duplicates = */ false) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_many(&paths,
|
||||||
|
STRV_IFNOTNULL(runtime_config),
|
||||||
|
global_runtime_config,
|
||||||
|
STRV_IFNOTNULL(generator)) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_strv(&paths, data_dirs, /* filter_duplicates = */ false) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_strv(&paths, (char* const*) user_data_unit_paths, false) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend(&paths, generator_late) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (path_strv_make_absolute_cwd(paths) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return TAKE_PTR(paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lookup_paths_init(
|
||||||
|
LookupPaths *lp,
|
||||||
|
RuntimeScope scope,
|
||||||
|
LookupPathsFlags flags,
|
||||||
|
const char *root_dir) {
|
||||||
|
|
||||||
|
_cleanup_(rmdir_and_freep) char *tempdir = NULL;
|
||||||
|
_cleanup_free_ char
|
||||||
|
*root = NULL,
|
||||||
|
*persistent_config = NULL, *runtime_config = NULL,
|
||||||
|
*global_persistent_config = NULL, *global_runtime_config = NULL,
|
||||||
|
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
|
||||||
|
*transient = NULL,
|
||||||
|
*persistent_control = NULL, *runtime_control = NULL,
|
||||||
|
*persistent_attached = NULL, *runtime_attached = NULL;
|
||||||
|
_cleanup_strv_free_ char **paths = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(lp);
|
||||||
|
assert(scope >= 0);
|
||||||
|
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||||
|
|
||||||
|
if (!empty_or_root(root_dir)) {
|
||||||
|
if (scope == RUNTIME_SCOPE_USER)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
r = is_dir(root_dir, true);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return -ENOTDIR;
|
||||||
|
|
||||||
|
root = strdup(root_dir);
|
||||||
|
if (!root)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAGS_SET(flags, LOOKUP_PATHS_TEMPORARY_GENERATED)) {
|
||||||
|
r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Failed to create temporary directory: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
|
||||||
|
r = acquire_lookup_dirs(LOOKUP_DIR_CONFIG, scope, &persistent_config, &runtime_config);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (scope == RUNTIME_SCOPE_USER) {
|
||||||
|
r = acquire_lookup_dirs(LOOKUP_DIR_CONFIG, RUNTIME_SCOPE_GLOBAL, &global_persistent_config, &global_runtime_config);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = acquire_lookup_dirs(LOOKUP_DIR_CONTROL, scope, &persistent_control, &runtime_control);
|
||||||
|
if (r < 0 && r != -EOPNOTSUPP)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = acquire_lookup_dirs(LOOKUP_DIR_ATTACHED, scope, &persistent_attached, &runtime_attached);
|
||||||
|
if (r < 0 && r != -EOPNOTSUPP)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!FLAGS_SET(flags, LOOKUP_PATHS_EXCLUDE_GENERATED)) {
|
||||||
|
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
||||||
|
r = acquire_generator_dirs(scope, tempdir,
|
||||||
|
&generator, &generator_early, &generator_late);
|
||||||
|
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
||||||
|
r = acquire_transient_dir(scope, tempdir, &transient);
|
||||||
|
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* First priority is whatever has been passed to us via env vars */
|
||||||
|
r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!paths || r > 0) {
|
||||||
|
/* Let's figure something out. */
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **add = NULL;
|
||||||
|
|
||||||
|
/* For the user units we include share/ in the search paths in order to comply with
|
||||||
|
* the XDG basedir spec. For the system stuff we avoid such nonsense. OTOH we include (/usr/)lib/
|
||||||
|
* in the search paths for the system stuff but avoid it for user stuff. */
|
||||||
|
|
||||||
|
assert(persistent_config);
|
||||||
|
|
||||||
|
switch (scope) {
|
||||||
|
|
||||||
|
case RUNTIME_SCOPE_SYSTEM:
|
||||||
|
/* If you modify this you also want to modify systemdsystemunitpath= in systemd.pc.in! */
|
||||||
|
add = strv_new(ASSERT_PTR(persistent_control),
|
||||||
|
ASSERT_PTR(runtime_control),
|
||||||
|
ASSERT_PTR(transient),
|
||||||
|
ASSERT_PTR(generator_early),
|
||||||
|
persistent_config,
|
||||||
|
SYSTEM_CONFIG_UNIT_DIR,
|
||||||
|
"/etc/systemd/system",
|
||||||
|
ASSERT_PTR(persistent_attached),
|
||||||
|
ASSERT_PTR(runtime_config),
|
||||||
|
"/run/systemd/system",
|
||||||
|
ASSERT_PTR(runtime_attached),
|
||||||
|
ASSERT_PTR(generator),
|
||||||
|
"/usr/local/lib/systemd/system",
|
||||||
|
SYSTEM_DATA_UNIT_DIR,
|
||||||
|
"/usr/lib/systemd/system",
|
||||||
|
/* To be used ONLY for images which might be legacy split-usr */
|
||||||
|
FLAGS_SET(flags, LOOKUP_PATHS_SPLIT_USR) ? "/lib/systemd/system" : STRV_IGNORE,
|
||||||
|
ASSERT_PTR(generator_late));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RUNTIME_SCOPE_GLOBAL:
|
||||||
|
/* If you modify this you also want to modify systemduserunitpath= in systemd.pc.in,
|
||||||
|
* and RUNTIME_SCOPE_USER search paths below! */
|
||||||
|
add = strv_new(STRV_IFNOTNULL(persistent_control),
|
||||||
|
STRV_IFNOTNULL(runtime_control),
|
||||||
|
STRV_IFNOTNULL(transient),
|
||||||
|
STRV_IFNOTNULL(generator_early),
|
||||||
|
persistent_config,
|
||||||
|
USER_CONFIG_UNIT_DIR,
|
||||||
|
"/etc/systemd/user",
|
||||||
|
ASSERT_PTR(runtime_config),
|
||||||
|
"/run/systemd/user",
|
||||||
|
STRV_IFNOTNULL(generator),
|
||||||
|
"/usr/local/share/systemd/user",
|
||||||
|
"/usr/share/systemd/user",
|
||||||
|
"/usr/local/lib/systemd/user",
|
||||||
|
USER_DATA_UNIT_DIR,
|
||||||
|
"/usr/lib/systemd/user",
|
||||||
|
STRV_IFNOTNULL(generator_late));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RUNTIME_SCOPE_USER:
|
||||||
|
add = user_unit_search_dirs(persistent_config, runtime_config,
|
||||||
|
global_persistent_config, global_runtime_config,
|
||||||
|
generator, generator_early, generator_late,
|
||||||
|
transient,
|
||||||
|
persistent_control, runtime_control);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!add)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (paths) {
|
||||||
|
/* strv_uniq() below would filter all duplicates against the final strv */
|
||||||
|
r = strv_extend_strv(&paths, add, /* filter_duplicates = */ false);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
} else
|
||||||
|
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
||||||
|
* and don't have to copy anything */
|
||||||
|
paths = TAKE_PTR(add);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = patch_root_prefix(&persistent_config, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = patch_root_prefix(&runtime_config, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = patch_root_prefix(&generator, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = patch_root_prefix(&generator_early, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = patch_root_prefix(&generator_late, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = patch_root_prefix(&transient, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = patch_root_prefix(&persistent_control, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = patch_root_prefix(&runtime_control, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = patch_root_prefix(&persistent_attached, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = patch_root_prefix(&runtime_attached, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = patch_root_prefix_strv(paths, root);
|
||||||
|
if (r < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*lp = (LookupPaths) {
|
||||||
|
.search_path = strv_uniq(TAKE_PTR(paths)),
|
||||||
|
|
||||||
|
.persistent_config = TAKE_PTR(persistent_config),
|
||||||
|
.runtime_config = TAKE_PTR(runtime_config),
|
||||||
|
|
||||||
|
.generator = TAKE_PTR(generator),
|
||||||
|
.generator_early = TAKE_PTR(generator_early),
|
||||||
|
.generator_late = TAKE_PTR(generator_late),
|
||||||
|
|
||||||
|
.transient = TAKE_PTR(transient),
|
||||||
|
|
||||||
|
.persistent_control = TAKE_PTR(persistent_control),
|
||||||
|
.runtime_control = TAKE_PTR(runtime_control),
|
||||||
|
|
||||||
|
.persistent_attached = TAKE_PTR(persistent_attached),
|
||||||
|
.runtime_attached = TAKE_PTR(runtime_attached),
|
||||||
|
|
||||||
|
.root_dir = TAKE_PTR(root),
|
||||||
|
.temporary_dir = TAKE_PTR(tempdir),
|
||||||
|
};
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = lookup_paths_init(lp, scope, flags, root_dir);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to initialize unit search paths%s%s: %m",
|
||||||
|
isempty(root_dir) ? "" : " for root directory ", strempty(root_dir));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lookup_paths_done(LookupPaths *lp) {
|
||||||
|
assert(lp);
|
||||||
|
|
||||||
|
lp->search_path = strv_free(lp->search_path);
|
||||||
|
|
||||||
|
lp->persistent_config = mfree(lp->persistent_config);
|
||||||
|
lp->runtime_config = mfree(lp->runtime_config);
|
||||||
|
|
||||||
|
lp->persistent_attached = mfree(lp->persistent_attached);
|
||||||
|
lp->runtime_attached = mfree(lp->runtime_attached);
|
||||||
|
|
||||||
|
lp->generator = mfree(lp->generator);
|
||||||
|
lp->generator_early = mfree(lp->generator_early);
|
||||||
|
lp->generator_late = mfree(lp->generator_late);
|
||||||
|
|
||||||
|
lp->transient = mfree(lp->transient);
|
||||||
|
|
||||||
|
lp->persistent_control = mfree(lp->persistent_control);
|
||||||
|
lp->runtime_control = mfree(lp->runtime_control);
|
||||||
|
|
||||||
|
lp->root_dir = mfree(lp->root_dir);
|
||||||
|
lp->temporary_dir = mfree(lp->temporary_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lookup_paths_log(LookupPaths *lp) {
|
||||||
|
assert(lp);
|
||||||
|
|
||||||
|
if (strv_isempty(lp->search_path)) {
|
||||||
|
log_debug("Ignoring unit files.");
|
||||||
|
lp->search_path = strv_free(lp->search_path);
|
||||||
|
} else {
|
||||||
|
_cleanup_free_ char *t = NULL;
|
||||||
|
|
||||||
|
t = strv_join(lp->search_path, "\n\t");
|
||||||
|
log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* const system_generator_paths[] = {
|
||||||
|
"/run/systemd/system-generators",
|
||||||
|
"/etc/systemd/system-generators",
|
||||||
|
"/usr/local/lib/systemd/system-generators",
|
||||||
|
SYSTEM_GENERATOR_DIR,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* const user_generator_paths[] = {
|
||||||
|
"/run/systemd/user-generators",
|
||||||
|
"/etc/systemd/user-generators",
|
||||||
|
"/usr/local/lib/systemd/user-generators",
|
||||||
|
USER_GENERATOR_DIR,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* const system_env_generator_paths[] = {
|
||||||
|
"/run/systemd/system-environment-generators",
|
||||||
|
"/etc/systemd/system-environment-generators",
|
||||||
|
"/usr/local/lib/systemd/system-environment-generators",
|
||||||
|
SYSTEM_ENV_GENERATOR_DIR,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* const user_env_generator_paths[] = {
|
||||||
|
"/run/systemd/user-environment-generators",
|
||||||
|
"/etc/systemd/user-environment-generators",
|
||||||
|
"/usr/local/lib/systemd/user-environment-generators",
|
||||||
|
USER_ENV_GENERATOR_DIR,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
char** generator_binary_paths_internal(RuntimeScope scope, bool env_generator) {
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *env_name;
|
||||||
|
const char * const *paths[_RUNTIME_SCOPE_MAX];
|
||||||
|
} unit_generator = {
|
||||||
|
"SYSTEMD_GENERATOR_PATH",
|
||||||
|
{
|
||||||
|
[RUNTIME_SCOPE_SYSTEM] = system_generator_paths,
|
||||||
|
[RUNTIME_SCOPE_USER] = user_generator_paths,
|
||||||
|
}
|
||||||
|
}, environment_generator = {
|
||||||
|
"SYSTEMD_ENVIRONMENT_GENERATOR_PATH",
|
||||||
|
{
|
||||||
|
[RUNTIME_SCOPE_SYSTEM] = system_env_generator_paths,
|
||||||
|
[RUNTIME_SCOPE_USER] = user_env_generator_paths,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **paths = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER));
|
||||||
|
|
||||||
|
const char *env_name = ASSERT_PTR((env_generator ? environment_generator : unit_generator).env_name);
|
||||||
|
const char * const *search_paths = ASSERT_PTR((env_generator ? environment_generator : unit_generator).paths[scope]);
|
||||||
|
|
||||||
|
/* First priority is whatever has been passed to us via env vars */
|
||||||
|
r = get_paths_from_environ(env_name, &paths);
|
||||||
|
if (r < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!paths || r > 0) {
|
||||||
|
_cleanup_strv_free_ char **add = strv_copy((char* const*) search_paths);
|
||||||
|
if (!add)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (paths) {
|
||||||
|
r = strv_extend_strv(&paths, add, /* filter_duplicates = */ true);
|
||||||
|
if (r < 0)
|
||||||
|
return NULL;
|
||||||
|
} else
|
||||||
|
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
||||||
|
* and don't have to copy anything */
|
||||||
|
paths = TAKE_PTR(add);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TAKE_PTR(paths);
|
||||||
|
}
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "constants.h"
|
#include "sd-path.h"
|
||||||
#include "macro.h"
|
|
||||||
#include "runtime-scope.h"
|
#include "runtime-scope.h"
|
||||||
|
|
||||||
typedef enum LookupPathsFlags {
|
typedef enum LookupPathsFlags {
|
||||||
|
@ -55,11 +55,21 @@ typedef struct LookupPaths {
|
||||||
int lookup_paths_init(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
int lookup_paths_init(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
||||||
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
||||||
|
|
||||||
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
|
int runtime_directory(RuntimeScope scope, const char *suffix, char **ret);
|
||||||
int xdg_user_runtime_dir(char **ret, const char *suffix);
|
|
||||||
int xdg_user_config_dir(char **ret, const char *suffix);
|
/* We don't treat /etc/xdg/systemd in these functions as the xdg base dir spec suggests because we assume
|
||||||
int xdg_user_data_dir(char **ret, const char *suffix);
|
* that is a link to /etc/systemd/ anyway. */
|
||||||
int runtime_directory(char **ret, RuntimeScope scope, const char *suffix);
|
|
||||||
|
int user_search_dirs(const char *suffix, char ***ret_config_dirs, char ***ret_data_dirs);
|
||||||
|
static inline int xdg_user_runtime_dir(const char *suffix, char **ret) {
|
||||||
|
return sd_path_lookup(SD_PATH_USER_RUNTIME, suffix, ret);
|
||||||
|
}
|
||||||
|
static inline int xdg_user_config_dir(const char *suffix, char **ret) {
|
||||||
|
return sd_path_lookup(SD_PATH_USER_CONFIGURATION, suffix, ret);
|
||||||
|
}
|
||||||
|
static inline int xdg_user_data_dir(const char *suffix, char **ret) {
|
||||||
|
return sd_path_lookup(SD_PATH_USER_SHARED, suffix, ret);
|
||||||
|
}
|
||||||
|
|
||||||
bool path_is_user_data_dir(const char *path);
|
bool path_is_user_data_dir(const char *path);
|
||||||
bool path_is_user_config_dir(const char *path);
|
bool path_is_user_config_dir(const char *path);
|
||||||
|
@ -67,11 +77,10 @@ bool path_is_user_config_dir(const char *path);
|
||||||
void lookup_paths_log(LookupPaths *p);
|
void lookup_paths_log(LookupPaths *p);
|
||||||
void lookup_paths_done(LookupPaths *p);
|
void lookup_paths_done(LookupPaths *p);
|
||||||
|
|
||||||
char **generator_binary_paths(RuntimeScope scope);
|
char** generator_binary_paths_internal(RuntimeScope scope, bool env_generator);
|
||||||
char **env_generator_binary_paths(RuntimeScope scope);
|
static inline char** generator_binary_paths(RuntimeScope runtime_scope) {
|
||||||
|
return generator_binary_paths_internal(runtime_scope, false);
|
||||||
#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
|
}
|
||||||
#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network")
|
static inline char** env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||||
|
return generator_binary_paths_internal(runtime_scope, true);
|
||||||
#define PORTABLE_PROFILE_DIRS CONF_PATHS_NULSTR("systemd/portable/profile")
|
}
|
||||||
int find_portable_profile(const char *name, const char *unit, char **ret_path);
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "architecture.h"
|
#include "architecture.h"
|
||||||
|
#include "conf-files.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
@ -65,26 +66,20 @@ static int from_home_dir(const char *envname, const char *suffix, char **buffer,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
static int from_xdg_user_dir(const char *field, char **buffer, const char **ret) {
|
||||||
|
_cleanup_free_ char *user_dirs = NULL;
|
||||||
_cleanup_fclose_ FILE *f = NULL;
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
_cleanup_free_ char *b = NULL;
|
|
||||||
_cleanup_free_ const char *fn = NULL;
|
|
||||||
const char *c = NULL;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(field);
|
assert(field);
|
||||||
assert(buffer);
|
assert(buffer);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c);
|
r = sd_path_lookup(SD_PATH_USER_CONFIGURATION, "user-dirs.dirs", &user_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
fn = path_join(c, "user-dirs.dirs");
|
f = fopen(user_dirs, "re");
|
||||||
if (!fn)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
f = fopen(fn, "re");
|
|
||||||
if (!f) {
|
if (!f) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
goto fallback;
|
goto fallback;
|
||||||
|
@ -107,14 +102,12 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
||||||
if (!p)
|
if (!p)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
p += strspn(p, WHITESPACE);
|
p = skip_leading_chars(p, WHITESPACE);
|
||||||
|
|
||||||
if (*p != '=')
|
if (*p != '=')
|
||||||
continue;
|
continue;
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
p += strspn(p, WHITESPACE);
|
p = skip_leading_chars(p, WHITESPACE);
|
||||||
|
|
||||||
if (*p != '"')
|
if (*p != '"')
|
||||||
continue;
|
continue;
|
||||||
p++;
|
p++;
|
||||||
|
@ -125,62 +118,34 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
||||||
*e = 0;
|
*e = 0;
|
||||||
|
|
||||||
/* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
|
/* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
|
||||||
if (startswith(p, "$HOME/")) {
|
if (streq(p, "$HOME"))
|
||||||
_cleanup_free_ char *h = NULL;
|
goto home;
|
||||||
|
|
||||||
r = get_home_dir(&h);
|
const char *s = startswith(p, "$HOME/");
|
||||||
if (r < 0)
|
if (s)
|
||||||
return r;
|
return from_home_dir(/* envname = */ NULL, s, buffer, ret);
|
||||||
|
|
||||||
if (!path_extend(&h, p+5))
|
if (path_is_absolute(p)) {
|
||||||
|
char *c = strdup(p);
|
||||||
|
if (!c)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*buffer = h;
|
*ret = *buffer = c;
|
||||||
*ret = TAKE_PTR(h);
|
|
||||||
return 0;
|
|
||||||
} else if (streq(p, "$HOME")) {
|
|
||||||
|
|
||||||
r = get_home_dir(buffer);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = *buffer;
|
|
||||||
return 0;
|
|
||||||
} else if (path_is_absolute(p)) {
|
|
||||||
char *copy;
|
|
||||||
|
|
||||||
copy = strdup(p);
|
|
||||||
if (!copy)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*buffer = copy;
|
|
||||||
*ret = copy;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fallback:
|
fallback:
|
||||||
/* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
|
/* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
|
||||||
if (streq(field, "XDG_DESKTOP_DIR")) {
|
if (streq(field, "XDG_DESKTOP_DIR"))
|
||||||
_cleanup_free_ char *h = NULL;
|
return from_home_dir(/* envname = */ NULL, "Desktop", buffer, ret);
|
||||||
|
|
||||||
r = get_home_dir(&h);
|
home:
|
||||||
if (r < 0)
|
r = get_home_dir(buffer);
|
||||||
return r;
|
if (r < 0)
|
||||||
|
return r;
|
||||||
if (!path_extend(&h, "Desktop"))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*buffer = h;
|
|
||||||
*ret = TAKE_PTR(h);
|
|
||||||
} else {
|
|
||||||
r = get_home_dir(buffer);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = *buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
*ret = *buffer;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,28 +252,28 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case SD_PATH_USER_DOCUMENTS:
|
case SD_PATH_USER_DOCUMENTS:
|
||||||
return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_MUSIC:
|
case SD_PATH_USER_MUSIC:
|
||||||
return from_user_dir("XDG_MUSIC_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_MUSIC_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_PICTURES:
|
case SD_PATH_USER_PICTURES:
|
||||||
return from_user_dir("XDG_PICTURES_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_PICTURES_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_VIDEOS:
|
case SD_PATH_USER_VIDEOS:
|
||||||
return from_user_dir("XDG_VIDEOS_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_VIDEOS_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_DOWNLOAD:
|
case SD_PATH_USER_DOWNLOAD:
|
||||||
return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_PUBLIC:
|
case SD_PATH_USER_PUBLIC:
|
||||||
return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_TEMPLATES:
|
case SD_PATH_USER_TEMPLATES:
|
||||||
return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_DESKTOP:
|
case SD_PATH_USER_DESKTOP:
|
||||||
return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_DESKTOP_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_SYSTEMD_UTIL:
|
case SD_PATH_SYSTEMD_UTIL:
|
||||||
*ret = PREFIX_NOSLASH "/lib/systemd";
|
*ret = PREFIX_NOSLASH "/lib/systemd";
|
||||||
|
@ -390,55 +355,56 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_path_alloc(uint64_t type, const char *suffix, char **path) {
|
static int get_path_alloc(uint64_t type, const char *suffix, char **ret) {
|
||||||
_cleanup_free_ char *buffer = NULL;
|
_cleanup_free_ char *buffer = NULL;
|
||||||
char *buffer2 = NULL;
|
const char *p;
|
||||||
const char *ret;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(path);
|
assert(ret);
|
||||||
|
|
||||||
r = get_path(type, &buffer, &ret);
|
r = get_path(type, &buffer, &p);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (suffix) {
|
if (suffix) {
|
||||||
suffix += strspn(suffix, "/");
|
char *suffixed = path_join(p, suffix);
|
||||||
buffer2 = path_join(ret, suffix);
|
if (!suffixed)
|
||||||
if (!buffer2)
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
path_simplify(suffixed);
|
||||||
|
|
||||||
|
free_and_replace(buffer, suffixed);
|
||||||
} else if (!buffer) {
|
} else if (!buffer) {
|
||||||
buffer = strdup(ret);
|
buffer = strdup(p);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
*path = buffer2 ?: TAKE_PTR(buffer);
|
*ret = TAKE_PTR(buffer);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
|
_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **ret) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_return(path, -EINVAL);
|
assert_return(ret, -EINVAL);
|
||||||
|
|
||||||
r = get_path_alloc(type, suffix, path);
|
r = get_path_alloc(type, suffix, ret);
|
||||||
if (r != -EOPNOTSUPP)
|
if (r != -EOPNOTSUPP)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* Fall back to sd_path_lookup_strv */
|
/* Fall back to sd_path_lookup_strv */
|
||||||
_cleanup_strv_free_ char **l = NULL;
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
char *buffer;
|
|
||||||
|
|
||||||
r = sd_path_lookup_strv(type, suffix, &l);
|
r = sd_path_lookup_strv(type, suffix, &l);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
buffer = strv_join(l, ":");
|
char *joined = strv_join(l, ":");
|
||||||
if (!buffer)
|
if (!joined)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*path = buffer;
|
*ret = joined;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,25 +581,16 @@ static int get_search(uint64_t type, char ***ret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
|
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
|
||||||
case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: {
|
case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR:
|
||||||
RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ?
|
|
||||||
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
|
||||||
char **t;
|
|
||||||
|
|
||||||
t = generator_binary_paths(scope);
|
|
||||||
if (!t)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret = t;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR:
|
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR:
|
||||||
case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: {
|
case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: {
|
||||||
char **t;
|
RuntimeScope scope = IN_SET(type, SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR,
|
||||||
|
SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR) ?
|
||||||
|
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
||||||
|
bool env_generator = IN_SET(type, SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR,
|
||||||
|
SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR);
|
||||||
|
|
||||||
t = env_generator_binary_paths(type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR ?
|
char **t = generator_binary_paths_internal(scope, env_generator);
|
||||||
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER);
|
|
||||||
if (!t)
|
if (!t)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -649,11 +606,11 @@ static int get_search(uint64_t type, char ***ret) {
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) {
|
_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret) {
|
||||||
_cleanup_strv_free_ char **l = NULL, **n = NULL;
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_return(paths, -EINVAL);
|
assert_return(ret, -EINVAL);
|
||||||
|
|
||||||
r = get_search(type, &l);
|
r = get_search(type, &l);
|
||||||
if (r == -EOPNOTSUPP) {
|
if (r == -EOPNOTSUPP) {
|
||||||
|
@ -669,31 +626,20 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***path
|
||||||
l[0] = TAKE_PTR(t);
|
l[0] = TAKE_PTR(t);
|
||||||
l[1] = NULL;
|
l[1] = NULL;
|
||||||
|
|
||||||
*paths = TAKE_PTR(l);
|
*ret = TAKE_PTR(l);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
} else if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!suffix) {
|
if (suffix)
|
||||||
*paths = TAKE_PTR(l);
|
STRV_FOREACH(i, l) {
|
||||||
return 0;
|
if (!path_extend(i, suffix))
|
||||||
}
|
return -ENOMEM;
|
||||||
|
|
||||||
n = new(char*, strv_length(l)+1);
|
path_simplify(*i);
|
||||||
if (!n)
|
}
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
char **j = n;
|
*ret = TAKE_PTR(l);
|
||||||
STRV_FOREACH(i, l) {
|
|
||||||
*j = path_join(*i, suffix);
|
|
||||||
if (!*j)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
*j = NULL;
|
|
||||||
|
|
||||||
*paths = TAKE_PTR(n);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1229,7 +1229,7 @@ static int generic_method_get_interface_description(
|
||||||
sd_varlink_method_flags_t flags,
|
sd_varlink_method_flags_t flags,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
static const struct sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "interface", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
|
{ "interface", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -416,19 +416,18 @@ static int list_machine_one(sd_varlink *link, Machine *m, bool more) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||||
Manager *m = ASSERT_PTR(userdata);
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
const char *mn = NULL;
|
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
|
||||||
|
|
||||||
const sd_json_dispatch_field dispatch_table[] = {
|
|
||||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&mn), 0 },
|
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Manager *m = ASSERT_PTR(userdata);
|
||||||
|
const char *mn = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(parameters);
|
assert(parameters);
|
||||||
|
|
||||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, 0);
|
r = sd_varlink_dispatch(link, parameters, dispatch_table, &mn);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -97,8 +97,8 @@ static int neighbor_append_json(Neighbor *n, sd_json_variant **array) {
|
||||||
|
|
||||||
return sd_json_variant_append_arraybo(
|
return sd_json_variant_append_arraybo(
|
||||||
array,
|
array,
|
||||||
SD_JSON_BUILD_PAIR_INTEGER("Family", n->family),
|
SD_JSON_BUILD_PAIR_INTEGER("Family", n->dst_addr.family),
|
||||||
JSON_BUILD_PAIR_IN_ADDR("Destination", &n->in_addr, n->family),
|
JSON_BUILD_PAIR_IN_ADDR("Destination", &n->dst_addr.address, n->dst_addr.family),
|
||||||
JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr),
|
JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr),
|
||||||
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
|
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
|
||||||
SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
|
SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
|
||||||
|
@ -168,7 +168,7 @@ static int nexthop_append_json(NextHop *n, sd_json_variant **array) {
|
||||||
return sd_json_variant_append_arraybo(
|
return sd_json_variant_append_arraybo(
|
||||||
array,
|
array,
|
||||||
SD_JSON_BUILD_PAIR_UNSIGNED("ID", n->id),
|
SD_JSON_BUILD_PAIR_UNSIGNED("ID", n->id),
|
||||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw, n->family),
|
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw.address, n->family),
|
||||||
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags),
|
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags),
|
||||||
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
|
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
|
||||||
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", n->protocol),
|
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", n->protocol),
|
||||||
|
|
|
@ -147,29 +147,29 @@ static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) {
|
||||||
static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
|
static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
|
||||||
assert(neighbor);
|
assert(neighbor);
|
||||||
|
|
||||||
siphash24_compress_typesafe(neighbor->family, state);
|
siphash24_compress_typesafe(neighbor->dst_addr.family, state);
|
||||||
|
|
||||||
if (!IN_SET(neighbor->family, AF_INET, AF_INET6))
|
if (!IN_SET(neighbor->dst_addr.family, AF_INET, AF_INET6))
|
||||||
/* treat any other address family as AF_UNSPEC */
|
/* treat any other address family as AF_UNSPEC */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Equality of neighbors are given by the destination address.
|
/* Equality of neighbors are given by the destination address.
|
||||||
* See neigh_lookup() in the kernel. */
|
* See neigh_lookup() in the kernel. */
|
||||||
in_addr_hash_func(&neighbor->in_addr, neighbor->family, state);
|
in_addr_hash_func(&neighbor->dst_addr.address, neighbor->dst_addr.family, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
|
static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = CMP(a->family, b->family);
|
r = CMP(a->dst_addr.family, b->dst_addr.family);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!IN_SET(a->family, AF_INET, AF_INET6))
|
if (!IN_SET(a->dst_addr.family, AF_INET, AF_INET6))
|
||||||
/* treat any other address family as AF_UNSPEC */
|
/* treat any other address family as AF_UNSPEC */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
|
return memcmp(&a->dst_addr.address, &b->dst_addr.address, FAMILY_ADDRESS_SIZE(a->dst_addr.family));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request **ret) {
|
static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request **ret) {
|
||||||
|
@ -244,7 +244,7 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const
|
||||||
"%s %s neighbor (%s): lladdr: %s, dst: %s",
|
"%s %s neighbor (%s): lladdr: %s, dst: %s",
|
||||||
str, strna(network_config_source_to_string(neighbor->source)), strna(state),
|
str, strna(network_config_source_to_string(neighbor->source)), strna(state),
|
||||||
HW_ADDR_TO_STR(&neighbor->ll_addr),
|
HW_ADDR_TO_STR(&neighbor->ll_addr),
|
||||||
IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr));
|
IN_ADDR_TO_STRING(neighbor->dst_addr.family, &neighbor->dst_addr.address));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) {
|
static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) {
|
||||||
|
@ -261,7 +261,7 @@ static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) {
|
||||||
log_neighbor_debug(neighbor, "Configuring", link);
|
log_neighbor_debug(neighbor, "Configuring", link);
|
||||||
|
|
||||||
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH,
|
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH,
|
||||||
link->ifindex, neighbor->family);
|
link->ifindex, neighbor->dst_addr.family);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->family, &neighbor->in_addr);
|
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->dst_addr.family, &neighbor->dst_addr.address);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ static int link_request_neighbor(Link *link, const Neighbor *neighbor) {
|
||||||
"The link layer address length (%zu) for neighbor %s does not match with "
|
"The link layer address length (%zu) for neighbor %s does not match with "
|
||||||
"the hardware address length (%zu), ignoring the setting.",
|
"the hardware address length (%zu), ignoring the setting.",
|
||||||
neighbor->ll_addr.length,
|
neighbor->ll_addr.length,
|
||||||
IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr),
|
IN_ADDR_TO_STRING(neighbor->dst_addr.family, &neighbor->dst_addr.address),
|
||||||
link->hw_addr.length);
|
link->hw_addr.length);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -451,11 +451,11 @@ int neighbor_remove(Neighbor *neighbor, Link *link) {
|
||||||
log_neighbor_debug(neighbor, "Removing", link);
|
log_neighbor_debug(neighbor, "Removing", link);
|
||||||
|
|
||||||
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_DELNEIGH,
|
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_DELNEIGH,
|
||||||
link->ifindex, neighbor->family);
|
link->ifindex, neighbor->dst_addr.family);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_link_error_errno(link, r, "Could not allocate RTM_DELNEIGH message: %m");
|
return log_link_error_errno(link, r, "Could not allocate RTM_DELNEIGH message: %m");
|
||||||
|
|
||||||
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->family, &neighbor->in_addr);
|
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->dst_addr.family, &neighbor->dst_addr.address);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
|
return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
|
||||||
|
|
||||||
|
@ -593,19 +593,19 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
/* First, retrieve the fundamental information about the neighbor. */
|
/* First, retrieve the fundamental information about the neighbor. */
|
||||||
r = sd_rtnl_message_neigh_get_family(message, &tmp->family);
|
r = sd_rtnl_message_neigh_get_family(message, &tmp->dst_addr.family);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_link_warning(link, "rtnl: received neighbor message without family, ignoring.");
|
log_link_warning(link, "rtnl: received neighbor message without family, ignoring.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (tmp->family == AF_BRIDGE) /* Currently, we do not support it. */
|
if (tmp->dst_addr.family == AF_BRIDGE) /* Currently, we do not support it. */
|
||||||
return 0;
|
return 0;
|
||||||
if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
|
if (!IN_SET(tmp->dst_addr.family, AF_INET, AF_INET6)) {
|
||||||
log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family);
|
log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->dst_addr.family);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->family, &tmp->in_addr);
|
r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->dst_addr.family, &tmp->dst_addr.address);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m");
|
log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -660,28 +660,28 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define log_neighbor_section(neighbor, fmt, ...) \
|
||||||
|
({ \
|
||||||
|
const Neighbor *_neighbor = (neighbor); \
|
||||||
|
log_section_warning_errno( \
|
||||||
|
_neighbor ? _neighbor->section : NULL, \
|
||||||
|
SYNTHETIC_ERRNO(EINVAL), \
|
||||||
|
fmt " Ignoring [Neighbor] section.", \
|
||||||
|
##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
|
||||||
static int neighbor_section_verify(Neighbor *neighbor) {
|
static int neighbor_section_verify(Neighbor *neighbor) {
|
||||||
if (section_is_invalid(neighbor->section))
|
if (section_is_invalid(neighbor->section))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (neighbor->family == AF_UNSPEC)
|
if (neighbor->dst_addr.family == AF_UNSPEC)
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_neighbor_section(neighbor, "Neighbor section without Address= configured.");
|
||||||
"%s: Neighbor section without Address= configured. "
|
|
||||||
"Ignoring [Neighbor] section from line %u.",
|
|
||||||
neighbor->section->filename, neighbor->section->line);
|
|
||||||
|
|
||||||
if (neighbor->family == AF_INET6 && !socket_ipv6_is_supported())
|
if (neighbor->dst_addr.family == AF_INET6 && !socket_ipv6_is_supported())
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_neighbor_section(neighbor, "Neighbor section with an IPv6 destination address configured, but the kernel does not support IPv6.");
|
||||||
"%s: Neighbor section with an IPv6 destination address configured, "
|
|
||||||
"but the kernel does not support IPv6. "
|
|
||||||
"Ignoring [Neighbor] section from line %u.",
|
|
||||||
neighbor->section->filename, neighbor->section->line);
|
|
||||||
|
|
||||||
if (neighbor->ll_addr.length == 0)
|
if (neighbor->ll_addr.length == 0)
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_neighbor_section(neighbor, "Neighbor section without LinkLayerAddress= configured.");
|
||||||
"%s: Neighbor section without LinkLayerAddress= configured. "
|
|
||||||
"Ignoring [Neighbor] section from line %u.",
|
|
||||||
neighbor->section->filename, neighbor->section->line);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -709,7 +709,7 @@ int network_drop_invalid_neighbors(Network *network) {
|
||||||
log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, "
|
log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, "
|
||||||
"dropping the neighbor setting specified at line %u.",
|
"dropping the neighbor setting specified at line %u.",
|
||||||
dup->section->filename,
|
dup->section->filename,
|
||||||
IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr),
|
IN_ADDR_TO_STRING(neighbor->dst_addr.family, &neighbor->dst_addr.address),
|
||||||
neighbor->section->line,
|
neighbor->section->line,
|
||||||
dup->section->line, dup->section->line);
|
dup->section->line, dup->section->line);
|
||||||
/* neighbor_detach() will drop the neighbor from neighbors_by_section. */
|
/* neighbor_detach() will drop the neighbor from neighbors_by_section. */
|
||||||
|
@ -728,7 +728,7 @@ int network_drop_invalid_neighbors(Network *network) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int config_parse_neighbor_address(
|
int config_parse_neighbor_section(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
unsigned line,
|
unsigned line,
|
||||||
|
@ -740,76 +740,26 @@ int config_parse_neighbor_address(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
_cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL;
|
static const ConfigSectionParser table[_NEIGHBOR_CONF_PARSER_MAX] = {
|
||||||
|
[NEIGHBOR_DESTINATION_ADDRESS] = { .parser = config_parse_in_addr_data, .ltype = 0, .offset = offsetof(Neighbor, dst_addr), },
|
||||||
|
[NEIGHBOR_LINK_LAYER_ADDRESS] = { .parser = config_parse_hw_addr, .ltype = 0, .offset = offsetof(Neighbor, ll_addr), },
|
||||||
|
};
|
||||||
|
|
||||||
|
_cleanup_(neighbor_unref_or_set_invalidp) Neighbor *neighbor = NULL;
|
||||||
Network *network = ASSERT_PTR(userdata);
|
Network *network = ASSERT_PTR(userdata);
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(filename);
|
assert(filename);
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
|
|
||||||
r = neighbor_new_static(network, filename, section_line, &n);
|
r = neighbor_new_static(network, filename, section_line, &neighbor);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
r = config_section_parse(table, ELEMENTSOF(table),
|
||||||
n->family = AF_UNSPEC;
|
unit, filename, line, section, section_line, lvalue, ltype, rvalue, neighbor);
|
||||||
n->in_addr = IN_ADDR_NULL;
|
if (r <= 0) /* 0 means non-critical error, but the section will be ignored. */
|
||||||
TAKE_PTR(n);
|
return r;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
|
TAKE_PTR(neighbor);
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Neighbor Address is invalid, ignoring assignment: %s", rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_neighbor_lladdr(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL;
|
|
||||||
Network *network = ASSERT_PTR(userdata);
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
|
|
||||||
r = neighbor_new_static(network, filename, section_line, &n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
|
||||||
n->ll_addr = HW_ADDR_NULL;
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = parse_hw_addr(rvalue, &n->ll_addr);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Neighbor %s= is invalid, ignoring assignment: %s",
|
|
||||||
lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,7 @@ typedef struct Neighbor {
|
||||||
|
|
||||||
unsigned n_ref;
|
unsigned n_ref;
|
||||||
|
|
||||||
int family;
|
struct in_addr_data dst_addr;
|
||||||
union in_addr_union in_addr;
|
|
||||||
struct hw_addr_data ll_addr;
|
struct hw_addr_data ll_addr;
|
||||||
} Neighbor;
|
} Neighbor;
|
||||||
|
|
||||||
|
@ -46,5 +45,11 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
|
||||||
|
|
||||||
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Neighbor, neighbor);
|
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Neighbor, neighbor);
|
||||||
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
|
typedef enum NeighborConfParserType {
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_lladdr);
|
NEIGHBOR_DESTINATION_ADDRESS,
|
||||||
|
NEIGHBOR_LINK_LAYER_ADDRESS,
|
||||||
|
_NEIGHBOR_CONF_PARSER_MAX,
|
||||||
|
_NEIGHBOR_CONF_PARSER_INVALID = -EINVAL,
|
||||||
|
} NeighborConfParserType;
|
||||||
|
|
||||||
|
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_section);
|
||||||
|
|
|
@ -173,9 +173,9 @@ Address.NetLabel, config_parse_address_section,
|
||||||
Address.NFTSet, config_parse_address_section, ADDRESS_NFT_SET, 0
|
Address.NFTSet, config_parse_address_section, ADDRESS_NFT_SET, 0
|
||||||
IPv6AddressLabel.Prefix, config_parse_ipv6_address_label_section, IPV6_ADDRESS_LABEL_PREFIX, 0
|
IPv6AddressLabel.Prefix, config_parse_ipv6_address_label_section, IPV6_ADDRESS_LABEL_PREFIX, 0
|
||||||
IPv6AddressLabel.Label, config_parse_ipv6_address_label_section, IPV6_ADDRESS_LABEL, 0
|
IPv6AddressLabel.Label, config_parse_ipv6_address_label_section, IPV6_ADDRESS_LABEL, 0
|
||||||
Neighbor.Address, config_parse_neighbor_address, 0, 0
|
Neighbor.Address, config_parse_neighbor_section, NEIGHBOR_DESTINATION_ADDRESS, 0
|
||||||
Neighbor.LinkLayerAddress, config_parse_neighbor_lladdr, 0, 0
|
Neighbor.LinkLayerAddress, config_parse_neighbor_section, NEIGHBOR_LINK_LAYER_ADDRESS, 0
|
||||||
Neighbor.MACAddress, config_parse_neighbor_lladdr, 0, 0 /* deprecated */
|
Neighbor.MACAddress, config_parse_neighbor_section, NEIGHBOR_LINK_LAYER_ADDRESS, 0 /* deprecated */
|
||||||
RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_TOS, 0
|
RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_TOS, 0
|
||||||
RoutingPolicyRule.Priority, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_PRIORITY, 0
|
RoutingPolicyRule.Priority, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_PRIORITY, 0
|
||||||
RoutingPolicyRule.GoTo, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_GOTO, 0
|
RoutingPolicyRule.GoTo, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_GOTO, 0
|
||||||
|
@ -219,12 +219,12 @@ Route.QuickAck, config_parse_route_metric_boolean,
|
||||||
Route.TCPCongestionControlAlgorithm, config_parse_route_metric_tcp_congestion, RTAX_CC_ALGO, 0
|
Route.TCPCongestionControlAlgorithm, config_parse_route_metric_tcp_congestion, RTAX_CC_ALGO, 0
|
||||||
Route.FastOpenNoCookie, config_parse_route_metric_boolean, RTAX_FASTOPEN_NO_COOKIE, 0
|
Route.FastOpenNoCookie, config_parse_route_metric_boolean, RTAX_FASTOPEN_NO_COOKIE, 0
|
||||||
Route.TTLPropagate, config_parse_warn_compat, DISABLED_LEGACY, 0
|
Route.TTLPropagate, config_parse_warn_compat, DISABLED_LEGACY, 0
|
||||||
NextHop.Id, config_parse_nexthop_id, 0, 0
|
NextHop.Id, config_parse_nexthop_section, NEXTHOP_ID, 0
|
||||||
NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
|
NextHop.Gateway, config_parse_nexthop_section, NEXTHOP_GATEWAY, 0
|
||||||
NextHop.Family, config_parse_nexthop_family, 0, 0
|
NextHop.Family, config_parse_nexthop_section, NEXTHOP_FAMILY, 0
|
||||||
NextHop.OnLink, config_parse_nexthop_onlink, 0, 0
|
NextHop.OnLink, config_parse_nexthop_section, NEXTHOP_ONLINK, 0
|
||||||
NextHop.Blackhole, config_parse_nexthop_blackhole, 0, 0
|
NextHop.Blackhole, config_parse_nexthop_section, NEXTHOP_BLACKHOLE, 0
|
||||||
NextHop.Group, config_parse_nexthop_group, 0, 0
|
NextHop.Group, config_parse_nexthop_section, NEXTHOP_GROUP, 0
|
||||||
DHCPv4.RequestAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_request_address)
|
DHCPv4.RequestAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_request_address)
|
||||||
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
||||||
DHCPv4.UseDNS, config_parse_tristate, 0, offsetof(Network, dhcp_use_dns)
|
DHCPv4.UseDNS, config_parse_tristate, 0, offsetof(Network, dhcp_use_dns)
|
||||||
|
|
|
@ -854,7 +854,7 @@ bool network_has_static_ipv6_configurations(Network *network) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ORDERED_HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
|
ORDERED_HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
|
||||||
if (neighbor->family == AF_INET6)
|
if (neighbor->dst_addr.family == AF_INET6)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!hashmap_isempty(network->address_labels_by_section))
|
if (!hashmap_isempty(network->address_labels_by_section))
|
||||||
|
|
|
@ -236,7 +236,7 @@ static int nexthop_compare_full(const NextHop *a, const NextHop *b) {
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (IN_SET(a->family, AF_INET, AF_INET6)) {
|
if (IN_SET(a->family, AF_INET, AF_INET6)) {
|
||||||
r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
|
r = memcmp(&a->gw.address, &b->gw.address, FAMILY_ADDRESS_SIZE(a->family));
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -481,7 +481,7 @@ static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *
|
||||||
log_link_debug(link, "%s %s nexthop (%s): id: %"PRIu32", gw: %s, blackhole: %s, group: %s, flags: %s",
|
log_link_debug(link, "%s %s nexthop (%s): id: %"PRIu32", gw: %s, blackhole: %s, group: %s, flags: %s",
|
||||||
str, strna(network_config_source_to_string(nexthop->source)), strna(state),
|
str, strna(network_config_source_to_string(nexthop->source)), strna(state),
|
||||||
nexthop->id,
|
nexthop->id,
|
||||||
IN_ADDR_TO_STRING(nexthop->family, &nexthop->gw),
|
IN_ADDR_TO_STRING(nexthop->family, &nexthop->gw.address),
|
||||||
yes_no(nexthop->blackhole), strna(group), strna(flags));
|
yes_no(nexthop->blackhole), strna(group), strna(flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,8 +627,8 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (in_addr_is_set(nexthop->family, &nexthop->gw)) {
|
if (in_addr_is_set(nexthop->family, &nexthop->gw.address)) {
|
||||||
r = netlink_message_append_in_addr_union(m, NHA_GATEWAY, nexthop->family, &nexthop->gw);
|
r = netlink_message_append_in_addr_union(m, NHA_GATEWAY, nexthop->family, &nexthop->gw.address);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -722,7 +722,7 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw);
|
return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
|
static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
|
||||||
|
@ -1093,9 +1093,9 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
|
||||||
(void) nexthop_update_group(nexthop, message);
|
(void) nexthop_update_group(nexthop, message);
|
||||||
|
|
||||||
if (nexthop->family != AF_UNSPEC) {
|
if (nexthop->family != AF_UNSPEC) {
|
||||||
r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw);
|
r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw.address);
|
||||||
if (r == -ENODATA)
|
if (r == -ENODATA)
|
||||||
nexthop->gw = IN_ADDR_NULL;
|
nexthop->gw.address = IN_ADDR_NULL;
|
||||||
else if (r < 0)
|
else if (r < 0)
|
||||||
log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
|
log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
|
||||||
}
|
}
|
||||||
|
@ -1129,66 +1129,60 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define log_nexthop_section(nexthop, fmt, ...) \
|
||||||
|
({ \
|
||||||
|
const NextHop *_nexthop = (nexthop); \
|
||||||
|
log_section_warning_errno( \
|
||||||
|
_nexthop ? _nexthop->section : NULL, \
|
||||||
|
SYNTHETIC_ERRNO(EINVAL), \
|
||||||
|
fmt " Ignoring [NextHop] section.", \
|
||||||
|
##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
|
||||||
static int nexthop_section_verify(NextHop *nh) {
|
static int nexthop_section_verify(NextHop *nh) {
|
||||||
if (section_is_invalid(nh->section))
|
if (section_is_invalid(nh->section))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0)
|
if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0)
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_nexthop_section(nh, "Nexthop without specifying Id= is not supported if ManageForeignNextHops=no is set in networkd.conf.");
|
||||||
"%s: [NextHop] section without specifying Id= is not supported "
|
|
||||||
"if ManageForeignNextHops=no is set in networkd.conf. "
|
if (nh->family == AF_UNSPEC)
|
||||||
"Ignoring [NextHop] section from line %u.",
|
nh->family = nh->gw.family;
|
||||||
nh->section->filename, nh->section->line);
|
else if (nh->gw.family != AF_UNSPEC && nh->gw.family != nh->family)
|
||||||
|
return log_nexthop_section(nh, "Family= and Gateway= settings for nexthop contradict each other.");
|
||||||
|
|
||||||
|
assert(nh->gw.family == nh->family || nh->gw.family == AF_UNSPEC);
|
||||||
|
|
||||||
if (!hashmap_isempty(nh->group)) {
|
if (!hashmap_isempty(nh->group)) {
|
||||||
if (in_addr_is_set(nh->family, &nh->gw))
|
if (in_addr_is_set(nh->family, &nh->gw.address))
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_nexthop_section(nh, "Nexthop group cannot have gateway address.");
|
||||||
"%s: nexthop group cannot have gateway address. "
|
|
||||||
"Ignoring [NextHop] section from line %u.",
|
|
||||||
nh->section->filename, nh->section->line);
|
|
||||||
|
|
||||||
if (nh->family != AF_UNSPEC)
|
if (nh->family != AF_UNSPEC)
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_nexthop_section(nh, "Nexthop group cannot have Family= setting.");
|
||||||
"%s: nexthop group cannot have Family= setting. "
|
|
||||||
"Ignoring [NextHop] section from line %u.",
|
|
||||||
nh->section->filename, nh->section->line);
|
|
||||||
|
|
||||||
if (nh->blackhole)
|
if (nh->blackhole)
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_nexthop_section(nh, "Nexthop group cannot be a blackhole.");
|
||||||
"%s: nexthop group cannot be a blackhole. "
|
|
||||||
"Ignoring [NextHop] section from line %u.",
|
|
||||||
nh->section->filename, nh->section->line);
|
|
||||||
|
|
||||||
if (nh->onlink > 0)
|
if (nh->onlink > 0)
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_nexthop_section(nh, "Nexthop group cannot have on-link flag.");
|
||||||
"%s: nexthop group cannot have on-link flag. "
|
|
||||||
"Ignoring [NextHop] section from line %u.",
|
|
||||||
nh->section->filename, nh->section->line);
|
|
||||||
} else if (nh->family == AF_UNSPEC)
|
} else if (nh->family == AF_UNSPEC)
|
||||||
/* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
|
/* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
|
||||||
nh->family = AF_INET;
|
nh->family = AF_INET;
|
||||||
|
|
||||||
if (nh->blackhole) {
|
if (nh->blackhole) {
|
||||||
if (in_addr_is_set(nh->family, &nh->gw))
|
if (in_addr_is_set(nh->family, &nh->gw.address))
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_nexthop_section(nh, "Blackhole nexthop cannot have gateway address.");
|
||||||
"%s: blackhole nexthop cannot have gateway address. "
|
|
||||||
"Ignoring [NextHop] section from line %u.",
|
|
||||||
nh->section->filename, nh->section->line);
|
|
||||||
|
|
||||||
if (nh->onlink > 0)
|
if (nh->onlink > 0)
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_nexthop_section(nh, "Blackhole nexthop cannot have on-link flag.");
|
||||||
"%s: blackhole nexthop cannot have on-link flag. "
|
|
||||||
"Ignoring [NextHop] section from line %u.",
|
|
||||||
nh->section->filename, nh->section->line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) &&
|
if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw.address) &&
|
||||||
ordered_hashmap_isempty(nh->network->addresses_by_section)) {
|
ordered_hashmap_isempty(nh->network->addresses_by_section)) {
|
||||||
/* If no address is configured, in most cases the gateway cannot be reachable.
|
/* If no address is configured, in most cases the gateway cannot be reachable.
|
||||||
* TODO: we may need to improve the condition above. */
|
* TODO: we may need to improve the condition above. */
|
||||||
log_warning("%s: Gateway= without static address configured. "
|
log_section_warning(nh->section, "Nexthop with Gateway= specified, but no static address configured. Enabling OnLink= option.");
|
||||||
"Enabling OnLink= option.",
|
|
||||||
nh->section->filename);
|
|
||||||
nh->onlink = true;
|
nh->onlink = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1262,7 +1256,7 @@ int manager_build_nexthop_ids(Manager *manager) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_parse_nexthop_id(
|
static int config_parse_nexthop_family(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
unsigned line,
|
unsigned line,
|
||||||
|
@ -1274,261 +1268,38 @@ int config_parse_nexthop_id(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
int *family = ASSERT_PTR(data);
|
||||||
Network *network = userdata;
|
|
||||||
uint32_t id;
|
if (isempty(rvalue))
|
||||||
|
*family = AF_UNSPEC;
|
||||||
|
else if (streq(rvalue, "ipv4"))
|
||||||
|
*family = AF_INET;
|
||||||
|
else if (streq(rvalue, "ipv6"))
|
||||||
|
*family = AF_INET6;
|
||||||
|
else
|
||||||
|
return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_parse_nexthop_group(
|
||||||
|
const char *unit,
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
unsigned section_line,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
Hashmap **group = ASSERT_PTR(data);
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = nexthop_new_static(network, filename, section_line, &n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
if (isempty(rvalue)) {
|
||||||
n->id = 0;
|
*group = hashmap_free_free(*group);
|
||||||
TAKE_PTR(n);
|
return 1;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = safe_atou32(rvalue, &id);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (id == 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
||||||
"Invalid nexthop id \"%s\", ignoring assignment: %m", rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->id = id;
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_nexthop_gateway(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
|
||||||
Network *network = userdata;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = nexthop_new_static(network, filename, section_line, &n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
|
||||||
n->family = AF_UNSPEC;
|
|
||||||
n->gw = IN_ADDR_NULL;
|
|
||||||
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_nexthop_family(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
|
||||||
Network *network = userdata;
|
|
||||||
AddressFamily a;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = nexthop_new_static(network, filename, section_line, &n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (isempty(rvalue) &&
|
|
||||||
!in_addr_is_set(n->family, &n->gw)) {
|
|
||||||
/* Accept an empty string only when Gateway= is null or not specified. */
|
|
||||||
n->family = AF_UNSPEC;
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a = nexthop_address_family_from_string(rvalue);
|
|
||||||
if (a < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
||||||
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_addr_is_set(n->family, &n->gw) &&
|
|
||||||
((a == ADDRESS_FAMILY_IPV4 && n->family == AF_INET6) ||
|
|
||||||
(a == ADDRESS_FAMILY_IPV6 && n->family == AF_INET))) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
||||||
"Specified family '%s' conflicts with the family of the previously specified Gateway=, "
|
|
||||||
"ignoring assignment.", rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (a) {
|
|
||||||
case ADDRESS_FAMILY_IPV4:
|
|
||||||
n->family = AF_INET;
|
|
||||||
break;
|
|
||||||
case ADDRESS_FAMILY_IPV6:
|
|
||||||
n->family = AF_INET6;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_nexthop_onlink(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
|
||||||
Network *network = userdata;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = nexthop_new_static(network, filename, section_line, &n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = parse_tristate(rvalue, &n->onlink);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_nexthop_blackhole(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
|
||||||
Network *network = userdata;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = nexthop_new_static(network, filename, section_line, &n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = parse_boolean(rvalue);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->blackhole = r;
|
|
||||||
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_nexthop_group(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
|
||||||
Network *network = userdata;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(section);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = nexthop_new_static(network, filename, section_line, &n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
|
||||||
n->group = hashmap_free_free(n->group);
|
|
||||||
TAKE_PTR(n);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const char *p = rvalue;;) {
|
for (const char *p = rvalue;;) {
|
||||||
|
@ -1538,15 +1309,10 @@ int config_parse_nexthop_group(
|
||||||
char *sep;
|
char *sep;
|
||||||
|
|
||||||
r = extract_first_word(&p, &word, NULL, 0);
|
r = extract_first_word(&p, &word, NULL, 0);
|
||||||
if (r == -ENOMEM)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
break;
|
return 1;
|
||||||
|
|
||||||
nhg = new0(struct nexthop_grp, 1);
|
nhg = new0(struct nexthop_grp, 1);
|
||||||
if (!nhg)
|
if (!nhg)
|
||||||
|
@ -1586,7 +1352,7 @@ int config_parse_nexthop_group(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = hashmap_ensure_put(&n->group, NULL, UINT32_TO_PTR(nhg->id), nhg);
|
r = hashmap_ensure_put(group, NULL, UINT32_TO_PTR(nhg->id), nhg);
|
||||||
if (r == -ENOMEM)
|
if (r == -ENOMEM)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
if (r == -EEXIST) {
|
if (r == -EEXIST) {
|
||||||
|
@ -1598,7 +1364,44 @@ int config_parse_nexthop_group(
|
||||||
assert(r > 0);
|
assert(r > 0);
|
||||||
TAKE_PTR(nhg);
|
TAKE_PTR(nhg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TAKE_PTR(n);
|
int config_parse_nexthop_section(
|
||||||
|
const char *unit,
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
unsigned section_line,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
static const ConfigSectionParser table[_NEXTHOP_CONF_PARSER_MAX] = {
|
||||||
|
[NEXTHOP_ID] = { .parser = config_parse_uint32, .ltype = 0, .offset = offsetof(NextHop, id), },
|
||||||
|
[NEXTHOP_GATEWAY] = { .parser = config_parse_in_addr_data, .ltype = 0, .offset = offsetof(NextHop, gw), },
|
||||||
|
[NEXTHOP_FAMILY] = { .parser = config_parse_nexthop_family, .ltype = 0, .offset = offsetof(NextHop, family), },
|
||||||
|
[NEXTHOP_ONLINK] = { .parser = config_parse_tristate, .ltype = 0, .offset = offsetof(NextHop, onlink), },
|
||||||
|
[NEXTHOP_BLACKHOLE] = { .parser = config_parse_bool, .ltype = 0, .offset = offsetof(NextHop, blackhole), },
|
||||||
|
[NEXTHOP_GROUP] = { .parser = config_parse_nexthop_group, .ltype = 0, .offset = offsetof(NextHop, group), },
|
||||||
|
};
|
||||||
|
|
||||||
|
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *nexthop = NULL;
|
||||||
|
Network *network = ASSERT_PTR(userdata);
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
|
||||||
|
r = nexthop_new_static(network, filename, section_line, &nexthop);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = config_section_parse(table, ELEMENTSOF(table),
|
||||||
|
unit, filename, line, section, section_line, lvalue, ltype, rvalue, nexthop);
|
||||||
|
if (r <= 0) /* 0 means non-critical error, but the section will be ignored. */
|
||||||
|
return r;
|
||||||
|
|
||||||
|
TAKE_PTR(nexthop);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ typedef struct NextHop {
|
||||||
Hashmap *group; /* NHA_GROUP */
|
Hashmap *group; /* NHA_GROUP */
|
||||||
bool blackhole; /* NHA_BLACKHOLE */
|
bool blackhole; /* NHA_BLACKHOLE */
|
||||||
int ifindex; /* NHA_OIF */
|
int ifindex; /* NHA_OIF */
|
||||||
union in_addr_union gw; /* NHA_GATEWAY */
|
struct in_addr_data gw; /* NHA_GATEWAY, gw.family is only used by conf parser. */
|
||||||
|
|
||||||
/* Only used in conf parser and nexthop_section_verify(). */
|
/* Only used in conf parser and nexthop_section_verify(). */
|
||||||
int onlink;
|
int onlink;
|
||||||
|
@ -71,9 +71,15 @@ int manager_build_nexthop_ids(Manager *manager);
|
||||||
|
|
||||||
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop);
|
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop);
|
||||||
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
|
typedef enum NextHopConfParserType {
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway);
|
NEXTHOP_ID,
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_family);
|
NEXTHOP_GATEWAY,
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_onlink);
|
NEXTHOP_FAMILY,
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_blackhole);
|
NEXTHOP_ONLINK,
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_group);
|
NEXTHOP_BLACKHOLE,
|
||||||
|
NEXTHOP_GROUP,
|
||||||
|
_NEXTHOP_CONF_PARSER_MAX,
|
||||||
|
_NEXTHOP_CONF_PARSER_INVALID = -EINVAL,
|
||||||
|
} NextHopConfParserType;
|
||||||
|
|
||||||
|
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_section);
|
||||||
|
|
|
@ -1742,7 +1742,7 @@ static int oci_seccomp_args(const char *name, sd_json_variant *v, sd_json_dispat
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
JSON_VARIANT_ARRAY_FOREACH(e, v) {
|
JSON_VARIANT_ARRAY_FOREACH(e, v) {
|
||||||
static const struct sd_json_dispatch_field table[] = {
|
static const sd_json_dispatch_field table[] = {
|
||||||
{ "index", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, offsetof(struct scmp_arg_cmp, arg), SD_JSON_MANDATORY },
|
{ "index", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, offsetof(struct scmp_arg_cmp, arg), SD_JSON_MANDATORY },
|
||||||
{ "value", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_a), SD_JSON_MANDATORY },
|
{ "value", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_a), SD_JSON_MANDATORY },
|
||||||
{ "valueTwo", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_b), 0 },
|
{ "valueTwo", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_b), 0 },
|
||||||
|
|
|
@ -369,7 +369,7 @@ static int run(int argc, char *argv[]) {
|
||||||
event = TPM2_EVENT_PHASE;
|
event = TPM2_EVENT_PHASE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg_graceful && tpm2_support() != TPM2_SUPPORT_FULL) {
|
if (arg_graceful && !tpm2_is_fully_supported()) {
|
||||||
log_notice("No complete TPM2 support detected, exiting gracefully.");
|
log_notice("No complete TPM2 support detected, exiting gracefully.");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2876,55 +2876,76 @@ static int print_answer(sd_json_variant *answer) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct MonitorQueryParams {
|
||||||
|
sd_json_variant *question;
|
||||||
|
sd_json_variant *answer;
|
||||||
|
sd_json_variant *collected_questions;
|
||||||
|
int rcode;
|
||||||
|
int error;
|
||||||
|
int ede_code;
|
||||||
|
const char *state;
|
||||||
|
const char *result;
|
||||||
|
const char *ede_msg;
|
||||||
|
} MonitorQueryParams;
|
||||||
|
|
||||||
|
static void monitor_query_params_done(MonitorQueryParams *p) {
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
sd_json_variant_unref(p->question);
|
||||||
|
sd_json_variant_unref(p->answer);
|
||||||
|
sd_json_variant_unref(p->collected_questions);
|
||||||
|
}
|
||||||
|
|
||||||
static void monitor_query_dump(sd_json_variant *v) {
|
static void monitor_query_dump(sd_json_variant *v) {
|
||||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *question = NULL, *answer = NULL, *collected_questions = NULL;
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
int rcode = -1, error = 0, ede_code = -1;
|
{ "question", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, question), SD_JSON_MANDATORY },
|
||||||
const char *state = NULL, *result = NULL, *ede_msg = NULL;
|
{ "answer", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, answer), 0 },
|
||||||
|
{ "collectedQuestions", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, collected_questions), 0 },
|
||||||
assert(v);
|
{ "state", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, state), SD_JSON_MANDATORY },
|
||||||
|
{ "result", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, result), 0 },
|
||||||
sd_json_dispatch_field dispatch_table[] = {
|
{ "rcode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, rcode), 0 },
|
||||||
{ "question", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&question), SD_JSON_MANDATORY },
|
{ "errno", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, error), 0 },
|
||||||
{ "answer", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&answer), 0 },
|
{ "extendedDNSErrorCode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, ede_code), 0 },
|
||||||
{ "collectedQuestions", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&collected_questions), 0 },
|
{ "extendedDNSErrorMessage", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, ede_msg), 0 },
|
||||||
{ "state", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&state), SD_JSON_MANDATORY },
|
|
||||||
{ "result", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&result), 0 },
|
|
||||||
{ "rcode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, PTR_TO_SIZE(&rcode), 0 },
|
|
||||||
{ "errno", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, PTR_TO_SIZE(&error), 0 },
|
|
||||||
{ "extendedDNSErrorCode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, PTR_TO_SIZE(&ede_code), 0 },
|
|
||||||
{ "extendedDNSErrorMessage", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&ede_msg), 0 },
|
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (sd_json_dispatch(v, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, NULL) < 0)
|
_cleanup_(monitor_query_params_done) MonitorQueryParams p = {
|
||||||
|
.rcode = -1,
|
||||||
|
.ede_code = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(v);
|
||||||
|
|
||||||
|
if (sd_json_dispatch(v, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &p) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* First show the current question */
|
/* First show the current question */
|
||||||
print_question('Q', ansi_highlight_cyan(), question);
|
print_question('Q', ansi_highlight_cyan(), p.question);
|
||||||
|
|
||||||
/* And then show the questions that led to this one in case this was a CNAME chain */
|
/* And then show the questions that led to this one in case this was a CNAME chain */
|
||||||
print_question('C', ansi_highlight_grey(), collected_questions);
|
print_question('C', ansi_highlight_grey(), p.collected_questions);
|
||||||
|
|
||||||
printf("%s%s S%s: %s",
|
printf("%s%s S%s: %s",
|
||||||
streq_ptr(state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
|
streq_ptr(p.state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
|
||||||
special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
|
special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
|
||||||
ansi_normal(),
|
ansi_normal(),
|
||||||
strna(streq_ptr(state, "errno") ? errno_to_name(error) :
|
strna(streq_ptr(p.state, "errno") ? errno_to_name(p.error) :
|
||||||
streq_ptr(state, "rcode-failure") ? dns_rcode_to_string(rcode) :
|
streq_ptr(p.state, "rcode-failure") ? dns_rcode_to_string(p.rcode) :
|
||||||
state));
|
p.state));
|
||||||
|
|
||||||
if (!isempty(result))
|
if (!isempty(p.result))
|
||||||
printf(": %s", result);
|
printf(": %s", p.result);
|
||||||
|
|
||||||
if (ede_code >= 0)
|
if (p.ede_code >= 0)
|
||||||
printf(" (%s%s%s)",
|
printf(" (%s%s%s)",
|
||||||
FORMAT_DNS_EDE_RCODE(ede_code),
|
FORMAT_DNS_EDE_RCODE(p.ede_code),
|
||||||
!isempty(ede_msg) ? ": " : "",
|
!isempty(p.ede_msg) ? ": " : "",
|
||||||
strempty(ede_msg));
|
strempty(p.ede_msg));
|
||||||
|
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
print_answer(answer);
|
print_answer(p.answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int monitor_reply(
|
static int monitor_reply(
|
||||||
|
|
|
@ -2145,26 +2145,31 @@ int dns_resource_key_to_json(DnsResourceKey *key, sd_json_variant **ret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int dns_resource_key_from_json(sd_json_variant *v, DnsResourceKey **ret) {
|
int dns_resource_key_from_json(sd_json_variant *v, DnsResourceKey **ret) {
|
||||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
struct params {
|
||||||
uint16_t type = 0, class = 0;
|
uint16_t type;
|
||||||
const char *name = NULL;
|
uint16_t class;
|
||||||
int r;
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "class", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, PTR_TO_SIZE(&class), SD_JSON_MANDATORY },
|
{ "class", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(struct params, class), SD_JSON_MANDATORY },
|
||||||
{ "type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, PTR_TO_SIZE(&type), SD_JSON_MANDATORY },
|
{ "type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(struct params, type), SD_JSON_MANDATORY },
|
||||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&name), SD_JSON_MANDATORY },
|
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, name), SD_JSON_MANDATORY },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||||
|
struct params p;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(v);
|
assert(v);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
r = sd_json_dispatch(v, dispatch_table, 0, NULL);
|
r = sd_json_dispatch(v, dispatch_table, 0, &p);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
key = dns_resource_key_new(class, type, name);
|
key = dns_resource_key_new(p.class, p.type, p.name);
|
||||||
if (!key)
|
if (!key)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
@ -667,7 +667,7 @@ static int has_tpm2(void) {
|
||||||
*
|
*
|
||||||
* Note that we don't check if we ourselves are built with TPM2 support here! */
|
* Note that we don't check if we ourselves are built with TPM2 support here! */
|
||||||
|
|
||||||
return FLAGS_SET(tpm2_support(), TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE);
|
return FLAGS_SET(tpm2_support_full(TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE), TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int condition_test_security(Condition *c, char **env) {
|
static int condition_test_security(Condition *c, char **env) {
|
||||||
|
|
|
@ -1764,7 +1764,7 @@ int config_parse_hw_addr(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
struct hw_addr_data a, *hwaddr = ASSERT_PTR(data);
|
struct hw_addr_data *hwaddr = ASSERT_PTR(data);
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(filename);
|
assert(filename);
|
||||||
|
@ -1776,11 +1776,10 @@ int config_parse_hw_addr(
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = parse_hw_addr_full(rvalue, ltype, &a);
|
r = parse_hw_addr_full(rvalue, ltype, hwaddr);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||||
|
|
||||||
*hwaddr = a;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1973,6 +1972,36 @@ int config_parse_in_addr_non_null(
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int config_parse_in_addr_data(
|
||||||
|
const char *unit,
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
unsigned section_line,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
struct in_addr_data *p = ASSERT_PTR(data);
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(lvalue);
|
||||||
|
|
||||||
|
if (isempty(rvalue)) {
|
||||||
|
*p = (struct in_addr_data) {};
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = in_addr_from_string_auto(rvalue, &p->family, &p->address);
|
||||||
|
if (r < 0)
|
||||||
|
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int config_parse_unsigned_bounded(
|
int config_parse_unsigned_bounded(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
|
|
|
@ -304,6 +304,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
|
CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
|
CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
|
CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
|
||||||
|
CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_data);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_percent);
|
CONFIG_PARSER_PROTOTYPE(config_parse_percent);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
|
CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_pid);
|
CONFIG_PARSER_PROTOTYPE(config_parse_pid);
|
||||||
|
|
|
@ -886,7 +886,7 @@ int encrypt_credential_and_warn(
|
||||||
* container tpm2_support will detect this, and will return a different flag combination of
|
* container tpm2_support will detect this, and will return a different flag combination of
|
||||||
* TPM2_SUPPORT_FULL, effectively skipping the use of TPM2 when inside one. */
|
* TPM2_SUPPORT_FULL, effectively skipping the use of TPM2 when inside one. */
|
||||||
|
|
||||||
try_tpm2 = tpm2_support() == TPM2_SUPPORT_FULL;
|
try_tpm2 = tpm2_is_fully_supported();
|
||||||
if (!try_tpm2)
|
if (!try_tpm2)
|
||||||
log_debug("System lacks TPM2 support or running in a container, not attempting to use TPM2.");
|
log_debug("System lacks TPM2 support or running in a container, not attempting to use TPM2.");
|
||||||
} else
|
} else
|
||||||
|
@ -1582,14 +1582,12 @@ int ipc_encrypt_credential(const char *name, usec_t timestamp, usec_t not_after,
|
||||||
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to encrypt: %s", error_id);
|
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to encrypt: %s", error_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_json_dispatch(
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
reply,
|
{ "blob", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, 0, SD_JSON_MANDATORY },
|
||||||
(const sd_json_dispatch_field[]) {
|
{},
|
||||||
{ "blob", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, PTR_TO_SIZE(ret), SD_JSON_MANDATORY },
|
};
|
||||||
{},
|
|
||||||
},
|
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, ret);
|
||||||
SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS,
|
|
||||||
/* userdata= */ NULL);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -1649,14 +1647,12 @@ int ipc_decrypt_credential(const char *validate_name, usec_t validate_timestamp,
|
||||||
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to decrypt: %s", error_id);
|
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to decrypt: %s", error_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_json_dispatch(
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
reply,
|
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, 0, SD_JSON_MANDATORY },
|
||||||
(const sd_json_dispatch_field[]) {
|
{},
|
||||||
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, PTR_TO_SIZE(ret), SD_JSON_MANDATORY },
|
};
|
||||||
{},
|
|
||||||
},
|
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, ret);
|
||||||
SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS,
|
|
||||||
/* userdata= */ NULL);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,7 @@ shared_sources = files(
|
||||||
'tpm2-util.c',
|
'tpm2-util.c',
|
||||||
'tpm2-event-log.c',
|
'tpm2-event-log.c',
|
||||||
'udev-util.c',
|
'udev-util.c',
|
||||||
|
'unit-file.c',
|
||||||
'user-record-nss.c',
|
'user-record-nss.c',
|
||||||
'user-record-show.c',
|
'user-record-show.c',
|
||||||
'user-record.c',
|
'user-record.c',
|
||||||
|
|
|
@ -250,6 +250,18 @@ int nsresource_add_cgroup(int userns_fd, int cgroup_fd) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct InterfaceParams {
|
||||||
|
char *host_interface_name;
|
||||||
|
char *namespace_interface_name;
|
||||||
|
} InterfaceParams;
|
||||||
|
|
||||||
|
static void interface_params_done(InterfaceParams *p) {
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
free(p->host_interface_name);
|
||||||
|
free(p->namespace_interface_name);
|
||||||
|
}
|
||||||
|
|
||||||
int nsresource_add_netif(
|
int nsresource_add_netif(
|
||||||
int userns_fd,
|
int userns_fd,
|
||||||
int netns_fd,
|
int netns_fd,
|
||||||
|
@ -313,20 +325,20 @@ int nsresource_add_netif(
|
||||||
if (error_id)
|
if (error_id)
|
||||||
return log_debug_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to add network to user namespace: %s", error_id);
|
return log_debug_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to add network to user namespace: %s", error_id);
|
||||||
|
|
||||||
_cleanup_free_ char *host_interface_name = NULL, *namespace_interface_name = NULL;
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
r = sd_json_dispatch(
|
{ "hostInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(InterfaceParams, host_interface_name), 0 },
|
||||||
reply,
|
{ "namespaceInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(InterfaceParams, namespace_interface_name), 0 },
|
||||||
(const sd_json_dispatch_field[]) {
|
};
|
||||||
{ "hostInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, PTR_TO_SIZE(&host_interface_name) },
|
|
||||||
{ "namespaceInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, PTR_TO_SIZE(&namespace_interface_name) },
|
_cleanup_(interface_params_done) InterfaceParams p = {};
|
||||||
},
|
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||||
SD_JSON_ALLOW_EXTENSIONS,
|
if (r < 0)
|
||||||
/* userdata= */ NULL);
|
return r;
|
||||||
|
|
||||||
if (ret_host_ifname)
|
if (ret_host_ifname)
|
||||||
*ret_host_ifname = TAKE_PTR(host_interface_name);
|
*ret_host_ifname = TAKE_PTR(p.host_interface_name);
|
||||||
if (ret_namespace_ifname)
|
if (ret_namespace_ifname)
|
||||||
*ret_namespace_ifname = TAKE_PTR(namespace_interface_name);
|
*ret_namespace_ifname = TAKE_PTR(p.namespace_interface_name);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,6 +281,44 @@ static inline int run_test_table(void) {
|
||||||
} \
|
} \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#define ASSERT_OK_ZERO_ERRNO(expr) \
|
||||||
|
({ \
|
||||||
|
typeof(expr) _result = (expr); \
|
||||||
|
if (_result < 0) { \
|
||||||
|
log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
|
||||||
|
PROJECT_FILE, __LINE__, #expr); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
if (_result != 0) { \
|
||||||
|
char _sexpr[DECIMAL_STR_MAX(typeof(expr))]; \
|
||||||
|
xsprintf(_sexpr, DECIMAL_STR_FMT(_result), _result); \
|
||||||
|
log_error("%s:%i: Assertion failed: expected \"%s\" to be zero, but it is %s.", \
|
||||||
|
PROJECT_FILE, __LINE__, #expr, _sexpr); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define ASSERT_OK_EQ_ERRNO(expr1, expr2) \
|
||||||
|
({ \
|
||||||
|
typeof(expr1) _expr1 = (expr1); \
|
||||||
|
typeof(expr2) _expr2 = (expr2); \
|
||||||
|
if (_expr1 < 0) { \
|
||||||
|
log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
|
||||||
|
PROJECT_FILE, __LINE__, #expr1); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
if (_expr1 != _expr2) { \
|
||||||
|
char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \
|
||||||
|
char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \
|
||||||
|
xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \
|
||||||
|
xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \
|
||||||
|
log_error("%s:%i: Assertion failed: expected \"%s == %s\", but %s != %s", \
|
||||||
|
PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
#define ASSERT_FAIL(expr) \
|
#define ASSERT_FAIL(expr) \
|
||||||
({ \
|
({ \
|
||||||
typeof(expr) _result = (expr); \
|
typeof(expr) _result = (expr); \
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "ansi-color.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "creds-util.h"
|
#include "creds-util.h"
|
||||||
#include "cryptsetup-util.h"
|
#include "cryptsetup-util.h"
|
||||||
|
@ -7872,11 +7873,11 @@ int tpm2_sym_mode_from_string(const char *mode) {
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown symmetric mode name '%s'", mode);
|
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown symmetric mode name '%s'", mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tpm2Support tpm2_support(void) {
|
Tpm2Support tpm2_support_full(Tpm2Support mask) {
|
||||||
Tpm2Support support = TPM2_SUPPORT_NONE;
|
Tpm2Support support = TPM2_SUPPORT_NONE;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (detect_container() <= 0) {
|
if (((mask & (TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_DRIVER)) != 0) && detect_container() <= 0) {
|
||||||
/* Check if there's a /dev/tpmrm* device via sysfs. If we run in a container we likely just
|
/* Check if there's a /dev/tpmrm* device via sysfs. If we run in a container we likely just
|
||||||
* got the host sysfs mounted. Since devices are generally not virtualized for containers,
|
* got the host sysfs mounted. Since devices are generally not virtualized for containers,
|
||||||
* let's assume containers never have a TPM, at least for now. */
|
* let's assume containers never have a TPM, at least for now. */
|
||||||
|
@ -7893,18 +7894,24 @@ Tpm2Support tpm2_support(void) {
|
||||||
support |= TPM2_SUPPORT_SUBSYSTEM;
|
support |= TPM2_SUPPORT_SUBSYSTEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (efi_has_tpm2())
|
if (FLAGS_SET(mask, TPM2_SUPPORT_FIRMWARE) && efi_has_tpm2())
|
||||||
support |= TPM2_SUPPORT_FIRMWARE;
|
support |= TPM2_SUPPORT_FIRMWARE;
|
||||||
|
|
||||||
#if HAVE_TPM2
|
#if HAVE_TPM2
|
||||||
support |= TPM2_SUPPORT_SYSTEM;
|
support |= TPM2_SUPPORT_SYSTEM;
|
||||||
|
|
||||||
r = dlopen_tpm2();
|
if (FLAGS_SET(mask, TPM2_SUPPORT_LIBRARIES)) {
|
||||||
if (r >= 0)
|
r = dlopen_tpm2();
|
||||||
support |= TPM2_SUPPORT_LIBRARIES;
|
if (r >= 0)
|
||||||
|
support |= TPM2_SUPPORT_LIBRARIES;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return support;
|
return support & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_field(const char *s, bool supported) {
|
||||||
|
printf("%s%s%s%s\n", supported ? ansi_green() : ansi_red(), plus_minus(supported), s, ansi_normal());
|
||||||
}
|
}
|
||||||
|
|
||||||
int verb_has_tpm2_generic(bool quiet) {
|
int verb_has_tpm2_generic(bool quiet) {
|
||||||
|
@ -7914,22 +7921,17 @@ int verb_has_tpm2_generic(bool quiet) {
|
||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
if (s == TPM2_SUPPORT_FULL)
|
if (s == TPM2_SUPPORT_FULL)
|
||||||
puts("yes");
|
printf("%syes%s\n", ansi_green(), ansi_normal());
|
||||||
else if (s == TPM2_SUPPORT_NONE)
|
else if (s == TPM2_SUPPORT_NONE)
|
||||||
puts("no");
|
printf("%sno%s\n", ansi_red(), ansi_normal());
|
||||||
else
|
else
|
||||||
puts("partial");
|
printf("%spartial%s\n", ansi_yellow(), ansi_normal());
|
||||||
|
|
||||||
printf("%sfirmware\n"
|
print_field("firmware", FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE));
|
||||||
"%sdriver\n"
|
print_field("driver", FLAGS_SET(s, TPM2_SUPPORT_DRIVER));
|
||||||
"%ssystem\n"
|
print_field("system", FLAGS_SET(s, TPM2_SUPPORT_SYSTEM));
|
||||||
"%ssubsystem\n"
|
print_field("subsystem", FLAGS_SET(s, TPM2_SUPPORT_SUBSYSTEM));
|
||||||
"%slibraries\n",
|
print_field("libraries", FLAGS_SET(s, TPM2_SUPPORT_LIBRARIES));
|
||||||
plus_minus(s & TPM2_SUPPORT_FIRMWARE),
|
|
||||||
plus_minus(s & TPM2_SUPPORT_DRIVER),
|
|
||||||
plus_minus(s & TPM2_SUPPORT_SYSTEM),
|
|
||||||
plus_minus(s & TPM2_SUPPORT_SUBSYSTEM),
|
|
||||||
plus_minus(s & TPM2_SUPPORT_LIBRARIES));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return inverted bit flags. So that TPM2_SUPPORT_FULL becomes EXIT_SUCCESS and the other values
|
/* Return inverted bit flags. So that TPM2_SUPPORT_FULL becomes EXIT_SUCCESS and the other values
|
||||||
|
|
|
@ -450,8 +450,8 @@ typedef struct {
|
||||||
} systemd_tpm2_plugin_params;
|
} systemd_tpm2_plugin_params;
|
||||||
|
|
||||||
typedef enum Tpm2Support {
|
typedef enum Tpm2Support {
|
||||||
/* NOTE! The systemd-creds tool returns these flags 1:1 as exit status. Hence these flags are pretty
|
/* NOTE! The systemd-analyze has-tpm2 command returns these flags 1:1 as exit status. Hence these
|
||||||
* much ABI! Hence, be extra careful when changing/extending these definitions. */
|
* flags are pretty much ABI! Hence, be extra careful when changing/extending these definitions. */
|
||||||
TPM2_SUPPORT_NONE = 0, /* no support */
|
TPM2_SUPPORT_NONE = 0, /* no support */
|
||||||
TPM2_SUPPORT_FIRMWARE = 1 << 0, /* firmware reports TPM2 was used */
|
TPM2_SUPPORT_FIRMWARE = 1 << 0, /* firmware reports TPM2 was used */
|
||||||
TPM2_SUPPORT_DRIVER = 1 << 1, /* the kernel has a driver loaded for it */
|
TPM2_SUPPORT_DRIVER = 1 << 1, /* the kernel has a driver loaded for it */
|
||||||
|
@ -461,7 +461,13 @@ typedef enum Tpm2Support {
|
||||||
TPM2_SUPPORT_FULL = TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES,
|
TPM2_SUPPORT_FULL = TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES,
|
||||||
} Tpm2Support;
|
} Tpm2Support;
|
||||||
|
|
||||||
Tpm2Support tpm2_support(void);
|
Tpm2Support tpm2_support_full(Tpm2Support mask);
|
||||||
|
static inline Tpm2Support tpm2_support(void) {
|
||||||
|
return tpm2_support_full(TPM2_SUPPORT_FULL);
|
||||||
|
}
|
||||||
|
static inline bool tpm2_is_fully_supported(void) {
|
||||||
|
return tpm2_support() == TPM2_SUPPORT_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
int verb_has_tpm2_generic(bool quiet);
|
int verb_has_tpm2_generic(bool quiet);
|
||||||
|
|
||||||
|
|
|
@ -16,25 +16,6 @@
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "unit-file.h"
|
#include "unit-file.h"
|
||||||
|
|
||||||
bool unit_type_may_alias(UnitType type) {
|
|
||||||
return IN_SET(type,
|
|
||||||
UNIT_SERVICE,
|
|
||||||
UNIT_SOCKET,
|
|
||||||
UNIT_TARGET,
|
|
||||||
UNIT_DEVICE,
|
|
||||||
UNIT_TIMER,
|
|
||||||
UNIT_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unit_type_may_template(UnitType type) {
|
|
||||||
return IN_SET(type,
|
|
||||||
UNIT_SERVICE,
|
|
||||||
UNIT_SOCKET,
|
|
||||||
UNIT_TARGET,
|
|
||||||
UNIT_TIMER,
|
|
||||||
UNIT_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
|
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
|
||||||
_cleanup_free_ char *template = NULL;
|
_cleanup_free_ char *template = NULL;
|
||||||
int r, un_type1, un_type2;
|
int r, un_type1, un_type2;
|
|
@ -28,8 +28,24 @@ enum UnitFileState {
|
||||||
_UNIT_FILE_STATE_INVALID = -EINVAL,
|
_UNIT_FILE_STATE_INVALID = -EINVAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool unit_type_may_alias(UnitType type) _const_;
|
static inline bool unit_type_may_alias(UnitType type) {
|
||||||
bool unit_type_may_template(UnitType type) _const_;
|
return IN_SET(type,
|
||||||
|
UNIT_SERVICE,
|
||||||
|
UNIT_SOCKET,
|
||||||
|
UNIT_TARGET,
|
||||||
|
UNIT_DEVICE,
|
||||||
|
UNIT_TIMER,
|
||||||
|
UNIT_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool unit_type_may_template(UnitType type) {
|
||||||
|
return IN_SET(type,
|
||||||
|
UNIT_SERVICE,
|
||||||
|
UNIT_SOCKET,
|
||||||
|
UNIT_TARGET,
|
||||||
|
UNIT_TIMER,
|
||||||
|
UNIT_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
|
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
|
||||||
int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target);
|
int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target);
|
|
@ -161,12 +161,12 @@ static int process_machine(const char *machine, const char *port) {
|
||||||
|
|
||||||
uint32_t cid = VMADDR_CID_ANY;
|
uint32_t cid = VMADDR_CID_ANY;
|
||||||
|
|
||||||
const sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "vSockCid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, PTR_TO_SIZE(&cid), 0 },
|
{ "vSockCid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, 0, 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
r = sd_json_dispatch(result, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, NULL);
|
r = sd_json_dispatch(result, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &cid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse Varlink reply: %m");
|
return log_error_errno(r, "Failed to parse Varlink reply: %m");
|
||||||
|
|
||||||
|
|
|
@ -123,8 +123,8 @@ enum {
|
||||||
_SD_PATH_MAX
|
_SD_PATH_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
int sd_path_lookup(uint64_t type, const char *suffix, char **path);
|
int sd_path_lookup(uint64_t type, const char *suffix, char **ret);
|
||||||
int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths);
|
int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret);
|
||||||
|
|
||||||
_SD_END_DECLARATIONS;
|
_SD_END_DECLARATIONS;
|
||||||
|
|
||||||
|
|
|
@ -321,12 +321,27 @@ static int list_targets(sd_bus *bus) {
|
||||||
return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, arg_legend);
|
return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, arg_legend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct DescribeParams {
|
||||||
|
Version v;
|
||||||
|
sd_json_variant *contents_json;
|
||||||
|
bool newest;
|
||||||
|
bool available;
|
||||||
|
bool installed;
|
||||||
|
bool obsolete;
|
||||||
|
bool protected;
|
||||||
|
bool incomplete;
|
||||||
|
} DescribeParams;
|
||||||
|
|
||||||
|
static void describe_params_done(DescribeParams *p) {
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
version_done(&p->v);
|
||||||
|
sd_json_variant_unref(p->contents_json);
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_describe(sd_bus_message *reply, Version *ret) {
|
static int parse_describe(sd_bus_message *reply, Version *ret) {
|
||||||
Version v = {};
|
|
||||||
char *version_json = NULL;
|
char *version_json = NULL;
|
||||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL, *contents_json = NULL;
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
|
||||||
bool newest = false, available = false, installed = false, obsolete = false, protected = false,
|
|
||||||
incomplete = false;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(reply);
|
assert(reply);
|
||||||
|
@ -342,36 +357,37 @@ static int parse_describe(sd_bus_message *reply, Version *ret) {
|
||||||
|
|
||||||
assert(sd_json_variant_is_object(json));
|
assert(sd_json_variant_is_object(json));
|
||||||
|
|
||||||
r = sd_json_dispatch(json,
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
(const sd_json_dispatch_field[]) {
|
{ "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DescribeParams, v.version), 0 },
|
||||||
{ "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, PTR_TO_SIZE(&v.version), 0 },
|
{ "newest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, newest), 0 },
|
||||||
{ "newest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&newest), 0 },
|
{ "available", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, available), 0 },
|
||||||
{ "available", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&available), 0 },
|
{ "installed", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, installed), 0 },
|
||||||
{ "installed", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&installed), 0 },
|
{ "obsolete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, obsolete), 0 },
|
||||||
{ "obsolete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&obsolete), 0 },
|
{ "protected", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, protected), 0 },
|
||||||
{ "protected", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&protected), 0 },
|
{ "incomplete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, incomplete), 0 },
|
||||||
{ "incomplete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&incomplete), 0 },
|
{ "changelog_urls", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DescribeParams, v.changelog), 0 },
|
||||||
{ "changelog_urls", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, PTR_TO_SIZE(&v.changelog), 0 },
|
{ "contents", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(DescribeParams, contents_json), 0 },
|
||||||
{ "contents", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&contents_json), 0 },
|
{},
|
||||||
{},
|
};
|
||||||
},
|
|
||||||
SD_JSON_ALLOW_EXTENSIONS,
|
_cleanup_(describe_params_done) DescribeParams p = {};
|
||||||
/* userdata= */ NULL);
|
|
||||||
|
r = sd_json_dispatch(json, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse JSON: %m");
|
return log_error_errno(r, "Failed to parse JSON: %m");
|
||||||
|
|
||||||
SET_FLAG(v.flags, UPDATE_NEWEST, newest);
|
SET_FLAG(p.v.flags, UPDATE_NEWEST, p.newest);
|
||||||
SET_FLAG(v.flags, UPDATE_AVAILABLE, available);
|
SET_FLAG(p.v.flags, UPDATE_AVAILABLE, p.available);
|
||||||
SET_FLAG(v.flags, UPDATE_INSTALLED, installed);
|
SET_FLAG(p.v.flags, UPDATE_INSTALLED, p.installed);
|
||||||
SET_FLAG(v.flags, UPDATE_OBSOLETE, obsolete);
|
SET_FLAG(p.v.flags, UPDATE_OBSOLETE, p.obsolete);
|
||||||
SET_FLAG(v.flags, UPDATE_PROTECTED, protected);
|
SET_FLAG(p.v.flags, UPDATE_PROTECTED, p.protected);
|
||||||
SET_FLAG(v.flags, UPDATE_INCOMPLETE, incomplete);
|
SET_FLAG(p.v.flags, UPDATE_INCOMPLETE, p.incomplete);
|
||||||
|
|
||||||
r = sd_json_variant_format(contents_json, 0, &v.contents_json);
|
r = sd_json_variant_format(p.contents_json, 0, &p.v.contents_json);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to format JSON for contents: %m");
|
return log_error_errno(r, "Failed to format JSON for contents: %m");
|
||||||
|
|
||||||
*ret = TAKE_STRUCT(v);
|
*ret = TAKE_STRUCT(p.v);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1141,6 +1141,18 @@ TEST(ASSERT) {
|
||||||
ASSERT_SIGNAL(ASSERT_OK_ERRNO(-1), SIGABRT);
|
ASSERT_SIGNAL(ASSERT_OK_ERRNO(-1), SIGABRT);
|
||||||
ASSERT_SIGNAL(ASSERT_OK_ERRNO(-ENOANO), SIGABRT);
|
ASSERT_SIGNAL(ASSERT_OK_ERRNO(-ENOANO), SIGABRT);
|
||||||
|
|
||||||
|
ASSERT_OK_ZERO_ERRNO(0);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_ZERO_ERRNO(1), SIGABRT);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_ZERO_ERRNO(255), SIGABRT);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_ZERO_ERRNO(-1), SIGABRT);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_ZERO_ERRNO(-ENOANO), SIGABRT);
|
||||||
|
|
||||||
|
ASSERT_OK_EQ_ERRNO(0, 0);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_EQ_ERRNO(1, 0), SIGABRT);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_EQ_ERRNO(255, 5), SIGABRT);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_EQ_ERRNO(-1, 0), SIGABRT);
|
||||||
|
ASSERT_SIGNAL(ASSERT_OK_EQ_ERRNO(-ENOANO, 0), SIGABRT);
|
||||||
|
|
||||||
ASSERT_FAIL(-ENOENT);
|
ASSERT_FAIL(-ENOENT);
|
||||||
ASSERT_FAIL(-EPERM);
|
ASSERT_FAIL(-EPERM);
|
||||||
ASSERT_SIGNAL(ASSERT_FAIL(0), SIGABRT);
|
ASSERT_SIGNAL(ASSERT_FAIL(0), SIGABRT);
|
||||||
|
|
|
@ -54,46 +54,51 @@ static void test_pid_get_comm_one(pid_t pid) {
|
||||||
xsprintf(path, "/proc/"PID_FMT"/comm", pid);
|
xsprintf(path, "/proc/"PID_FMT"/comm", pid);
|
||||||
|
|
||||||
if (stat(path, &st) == 0) {
|
if (stat(path, &st) == 0) {
|
||||||
assert_se(pid_get_comm(pid, &a) >= 0);
|
ASSERT_OK(pid_get_comm(pid, &a));
|
||||||
log_info("PID"PID_FMT" comm: '%s'", pid, a);
|
log_info("PID"PID_FMT" comm: '%s'", pid, a);
|
||||||
} else
|
} else
|
||||||
log_warning("%s not exist.", path);
|
log_warning("%s not exist.", path);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(pid, 0, PROCESS_CMDLINE_COMM_FALLBACK, &c) >= 0);
|
ASSERT_OK(pid_get_cmdline(pid, 0, PROCESS_CMDLINE_COMM_FALLBACK, &c));
|
||||||
log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
|
log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(pid, 8, 0, &d) >= 0);
|
ASSERT_OK(pid_get_cmdline(pid, 8, 0, &d));
|
||||||
log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
|
log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
|
||||||
|
|
||||||
free(d);
|
free(d);
|
||||||
assert_se(pid_get_cmdline(pid, 1, 0, &d) >= 0);
|
ASSERT_OK(pid_get_cmdline(pid, 1, 0, &d));
|
||||||
log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
|
log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
|
||||||
|
|
||||||
r = get_process_ppid(pid, &e);
|
r = get_process_ppid(pid, &e);
|
||||||
assert_se(pid == 1 ? r == -EADDRNOTAVAIL : r >= 0);
|
if (pid == 1)
|
||||||
|
ASSERT_ERROR(r, EADDRNOTAVAIL);
|
||||||
|
else
|
||||||
|
ASSERT_OK(r);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
|
log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
|
||||||
assert_se(e > 0);
|
ASSERT_GT(e, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_se(pid_is_kernel_thread(pid) == 0 || pid != 1);
|
ASSERT_TRUE(pid_is_kernel_thread(pid) == 0 || pid != 1);
|
||||||
|
|
||||||
r = get_process_exe(pid, &f);
|
r = get_process_exe(pid, &f);
|
||||||
assert_se(r >= 0 || r == -EACCES);
|
if (r != -EACCES)
|
||||||
|
ASSERT_OK(r);
|
||||||
log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));
|
log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));
|
||||||
|
|
||||||
assert_se(pid_get_uid(pid, &u) == 0);
|
ASSERT_OK_ZERO(pid_get_uid(pid, &u));
|
||||||
log_info("PID"PID_FMT" UID: "UID_FMT, pid, u);
|
log_info("PID"PID_FMT" UID: "UID_FMT, pid, u);
|
||||||
|
|
||||||
assert_se(get_process_gid(pid, &g) == 0);
|
ASSERT_OK_ZERO(get_process_gid(pid, &g));
|
||||||
log_info("PID"PID_FMT" GID: "GID_FMT, pid, g);
|
log_info("PID"PID_FMT" GID: "GID_FMT, pid, g);
|
||||||
|
|
||||||
r = get_process_environ(pid, &env);
|
r = get_process_environ(pid, &env);
|
||||||
assert_se(r >= 0 || r == -EACCES);
|
if (r != -EACCES)
|
||||||
|
ASSERT_OK(r);
|
||||||
log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);
|
log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);
|
||||||
|
|
||||||
if (!detect_container())
|
if (!detect_container() && pid == 1)
|
||||||
assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1);
|
ASSERT_ERROR(get_ctty_devnr(pid, &h), ENXIO);
|
||||||
|
|
||||||
(void) getenv_for_pid(pid, "PATH", &i);
|
(void) getenv_for_pid(pid, "PATH", &i);
|
||||||
log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
|
log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
|
||||||
|
@ -136,14 +141,14 @@ static void test_pid_get_cmdline_one(pid_t pid) {
|
||||||
|
|
||||||
r = pid_get_cmdline_strv(pid, 0, &strv_a);
|
r = pid_get_cmdline_strv(pid, 0, &strv_a);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
assert_se(joined = strv_join(strv_a, "\", \""));
|
ASSERT_NOT_NULL(joined = strv_join(strv_a, "\", \""));
|
||||||
log_info(" \"%s\"", r >= 0 ? joined : errno_to_name(r));
|
log_info(" \"%s\"", r >= 0 ? joined : errno_to_name(r));
|
||||||
|
|
||||||
joined = mfree(joined);
|
joined = mfree(joined);
|
||||||
|
|
||||||
r = pid_get_cmdline_strv(pid, PROCESS_CMDLINE_COMM_FALLBACK, &strv_b);
|
r = pid_get_cmdline_strv(pid, PROCESS_CMDLINE_COMM_FALLBACK, &strv_b);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
assert_se(joined = strv_join(strv_b, "\", \""));
|
ASSERT_NOT_NULL(joined = strv_join(strv_b, "\", \""));
|
||||||
log_info(" \"%s\"", r >= 0 ? joined : errno_to_name(r));
|
log_info(" \"%s\"", r >= 0 ? joined : errno_to_name(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,13 +156,13 @@ TEST(pid_get_cmdline) {
|
||||||
_cleanup_closedir_ DIR *d = NULL;
|
_cleanup_closedir_ DIR *d = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_se(proc_dir_open(&d) >= 0);
|
ASSERT_OK(proc_dir_open(&d));
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
r = proc_dir_read(d, &pid);
|
r = proc_dir_read(d, &pid);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
|
|
||||||
if (r == 0) /* EOF */
|
if (r == 0) /* EOF */
|
||||||
break;
|
break;
|
||||||
|
@ -171,8 +176,8 @@ static void test_pid_get_comm_escape_one(const char *input, const char *output)
|
||||||
|
|
||||||
log_debug("input: <%s> — output: <%s>", input, output);
|
log_debug("input: <%s> — output: <%s>", input, output);
|
||||||
|
|
||||||
assert_se(prctl(PR_SET_NAME, input) >= 0);
|
ASSERT_OK_ERRNO(prctl(PR_SET_NAME, input));
|
||||||
assert_se(pid_get_comm(0, &n) >= 0);
|
ASSERT_OK(pid_get_comm(0, &n));
|
||||||
|
|
||||||
log_debug("got: <%s>", n);
|
log_debug("got: <%s>", n);
|
||||||
|
|
||||||
|
@ -182,7 +187,7 @@ static void test_pid_get_comm_escape_one(const char *input, const char *output)
|
||||||
TEST(pid_get_comm_escape) {
|
TEST(pid_get_comm_escape) {
|
||||||
_cleanup_free_ char *saved = NULL;
|
_cleanup_free_ char *saved = NULL;
|
||||||
|
|
||||||
assert_se(pid_get_comm(0, &saved) >= 0);
|
ASSERT_OK(pid_get_comm(0, &saved));
|
||||||
|
|
||||||
test_pid_get_comm_escape_one("", "");
|
test_pid_get_comm_escape_one("", "");
|
||||||
test_pid_get_comm_escape_one("foo", "foo");
|
test_pid_get_comm_escape_one("foo", "foo");
|
||||||
|
@ -195,62 +200,62 @@ TEST(pid_get_comm_escape) {
|
||||||
test_pid_get_comm_escape_one("xxxxäöüß", "xxxx\\303\\244\\303\\266\\303\\274\\303\\237");
|
test_pid_get_comm_escape_one("xxxxäöüß", "xxxx\\303\\244\\303\\266\\303\\274\\303\\237");
|
||||||
test_pid_get_comm_escape_one("xxxxxäöüß", "xxxxx\\303\\244\\303\\266\\303\\274\\303\\237");
|
test_pid_get_comm_escape_one("xxxxxäöüß", "xxxxx\\303\\244\\303\\266\\303\\274\\303\\237");
|
||||||
|
|
||||||
assert_se(prctl(PR_SET_NAME, saved) >= 0);
|
ASSERT_OK_ERRNO(prctl(PR_SET_NAME, saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pid_is_unwaited) {
|
TEST(pid_is_unwaited) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
assert_se(pid >= 0);
|
ASSERT_OK_ERRNO(pid);
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
assert_se(waitpid(pid, &status, 0) == pid);
|
ASSERT_OK_EQ_ERRNO(waitpid(pid, &status, 0), pid);
|
||||||
assert_se(pid_is_unwaited(pid) == 0);
|
ASSERT_OK_ZERO(pid_is_unwaited(pid));
|
||||||
}
|
}
|
||||||
assert_se(pid_is_unwaited(getpid_cached()) > 0);
|
ASSERT_OK_POSITIVE(pid_is_unwaited(getpid_cached()));
|
||||||
assert_se(pid_is_unwaited(-1) < 0);
|
ASSERT_FAIL(pid_is_unwaited(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pid_is_alive) {
|
TEST(pid_is_alive) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
assert_se(pid >= 0);
|
ASSERT_OK_ERRNO(pid);
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
assert_se(waitpid(pid, &status, 0) == pid);
|
ASSERT_OK_EQ_ERRNO(waitpid(pid, &status, 0), pid);
|
||||||
assert_se(pid_is_alive(pid) == 0);
|
ASSERT_OK_ZERO(pid_is_alive(pid));
|
||||||
}
|
}
|
||||||
assert_se(pid_is_alive(getpid_cached()) > 0);
|
ASSERT_OK_POSITIVE(pid_is_alive(getpid_cached()));
|
||||||
assert_se(pid_is_alive(-1) < 0);
|
ASSERT_FAIL(pid_is_alive(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(personality) {
|
TEST(personality) {
|
||||||
assert_se(personality_to_string(PER_LINUX));
|
ASSERT_NOT_NULL(personality_to_string(PER_LINUX));
|
||||||
assert_se(!personality_to_string(PERSONALITY_INVALID));
|
ASSERT_NULL(personality_to_string(PERSONALITY_INVALID));
|
||||||
|
|
||||||
ASSERT_STREQ(personality_to_string(PER_LINUX), architecture_to_string(native_architecture()));
|
ASSERT_STREQ(personality_to_string(PER_LINUX), architecture_to_string(native_architecture()));
|
||||||
|
|
||||||
assert_se(personality_from_string(personality_to_string(PER_LINUX)) == PER_LINUX);
|
ASSERT_EQ(personality_from_string(personality_to_string(PER_LINUX)), (unsigned long) PER_LINUX);
|
||||||
assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX);
|
ASSERT_EQ(personality_from_string(architecture_to_string(native_architecture())), (unsigned long) PER_LINUX);
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
ASSERT_STREQ(personality_to_string(PER_LINUX), "x86-64");
|
ASSERT_STREQ(personality_to_string(PER_LINUX), "x86-64");
|
||||||
ASSERT_STREQ(personality_to_string(PER_LINUX32), "x86");
|
ASSERT_STREQ(personality_to_string(PER_LINUX32), "x86");
|
||||||
|
|
||||||
assert_se(personality_from_string("x86-64") == PER_LINUX);
|
ASSERT_EQ(personality_from_string("x86-64"), (unsigned long) PER_LINUX);
|
||||||
assert_se(personality_from_string("x86") == PER_LINUX32);
|
ASSERT_EQ(personality_from_string("x86"), (unsigned long) PER_LINUX32);
|
||||||
assert_se(personality_from_string("ia64") == PERSONALITY_INVALID);
|
ASSERT_EQ(personality_from_string("ia64"), PERSONALITY_INVALID);
|
||||||
assert_se(personality_from_string(NULL) == PERSONALITY_INVALID);
|
ASSERT_EQ(personality_from_string(NULL), PERSONALITY_INVALID);
|
||||||
|
|
||||||
assert_se(personality_from_string(personality_to_string(PER_LINUX32)) == PER_LINUX32);
|
ASSERT_EQ(personality_from_string(personality_to_string(PER_LINUX32)), (unsigned long) PER_LINUX32);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,30 +293,31 @@ TEST(pid_get_cmdline_harder) {
|
||||||
|
|
||||||
(void) wait_for_terminate(pid, &si);
|
(void) wait_for_terminate(pid, &si);
|
||||||
|
|
||||||
assert_se(si.si_code == CLD_EXITED);
|
ASSERT_EQ(si.si_code, CLD_EXITED);
|
||||||
assert_se(si.si_status == 0);
|
ASSERT_OK_ZERO(si.si_status);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_se(pid == 0);
|
ASSERT_OK_ZERO(pid);
|
||||||
|
|
||||||
r = detach_mount_namespace();
|
r = detach_mount_namespace();
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_warning_errno(r, "detach mount namespace failed: %m");
|
log_warning_errno(r, "detach mount namespace failed: %m");
|
||||||
assert_se(ERRNO_IS_PRIVILEGE(r));
|
if (!ERRNO_IS_PRIVILEGE(r))
|
||||||
|
ASSERT_OK(r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = mkostemp(path, O_CLOEXEC);
|
fd = mkostemp(path, O_CLOEXEC);
|
||||||
assert_se(fd >= 0);
|
ASSERT_OK_ERRNO(fd);
|
||||||
|
|
||||||
/* Note that we don't unmount the following bind-mount at the end of the test because the kernel
|
/* Note that we don't unmount the following bind-mount at the end of the test because the kernel
|
||||||
* will clear up its /proc/PID/ hierarchy automatically as soon as the test stops. */
|
* will clear up its /proc/PID/ hierarchy automatically as soon as the test stops. */
|
||||||
if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
|
if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
|
||||||
/* This happens under selinux… Abort the test in this case. */
|
/* This happens under selinux… Abort the test in this case. */
|
||||||
log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
|
log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
|
||||||
assert_se(IN_SET(errno, EPERM, EACCES));
|
ASSERT_TRUE(IN_SET(errno, EPERM, EACCES));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,197 +326,197 @@ TEST(pid_get_cmdline_harder) {
|
||||||
if (setrlimit(RLIMIT_STACK, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
|
if (setrlimit(RLIMIT_STACK, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
|
||||||
log_warning("Testing without RLIMIT_STACK=infinity");
|
log_warning("Testing without RLIMIT_STACK=infinity");
|
||||||
|
|
||||||
assert_se(unlink(path) >= 0);
|
ASSERT_OK_ERRNO(unlink(path));
|
||||||
|
|
||||||
assert_se(prctl(PR_SET_NAME, "testa") >= 0);
|
ASSERT_OK_ERRNO(prctl(PR_SET_NAME, "testa"));
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) == -ENOENT);
|
ASSERT_ERROR(pid_get_cmdline(0, SIZE_MAX, 0, &line), ENOENT);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "[testa]");
|
ASSERT_STREQ(line, "[testa]");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "\"[testa]\""); /* quoting is enabled here */
|
ASSERT_STREQ(line, "\"[testa]\""); /* quoting is enabled here */
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 0, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "");
|
ASSERT_STREQ(line, "");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "…");
|
ASSERT_STREQ(line, "…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "[…");
|
ASSERT_STREQ(line, "[…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "[t…");
|
ASSERT_STREQ(line, "[t…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "[te…");
|
ASSERT_STREQ(line, "[te…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "[tes…");
|
ASSERT_STREQ(line, "[tes…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "[test…");
|
ASSERT_STREQ(line, "[test…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "[testa]");
|
ASSERT_STREQ(line, "[testa]");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "[testa]");
|
ASSERT_STREQ(line, "[testa]");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
|
ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
|
||||||
assert_se(strv_equal(args, STRV_MAKE("[testa]")));
|
ASSERT_TRUE(strv_equal(args, STRV_MAKE("[testa]")));
|
||||||
args = strv_free(args);
|
args = strv_free(args);
|
||||||
|
|
||||||
/* Test with multiple arguments that don't require quoting */
|
/* Test with multiple arguments that don't require quoting */
|
||||||
|
|
||||||
assert_se(write(fd, "foo\0bar", 8) == 8);
|
ASSERT_OK_EQ_ERRNO(write(fd, "foo\0bar", 8), 8);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, 0, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar");
|
ASSERT_STREQ(line, "foo bar");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
ASSERT_STREQ(line, "foo bar");
|
ASSERT_STREQ(line, "foo bar");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
|
ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
|
||||||
assert_se(strv_equal(args, STRV_MAKE("foo", "bar")));
|
ASSERT_TRUE(strv_equal(args, STRV_MAKE("foo", "bar")));
|
||||||
args = strv_free(args);
|
args = strv_free(args);
|
||||||
|
|
||||||
assert_se(write(fd, "quux", 4) == 4);
|
ASSERT_OK_EQ_ERRNO(write(fd, "quux", 4), 4);
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, 0, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar quux");
|
ASSERT_STREQ(line, "foo bar quux");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar quux");
|
ASSERT_STREQ(line, "foo bar quux");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "…");
|
ASSERT_STREQ(line, "…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "f…");
|
ASSERT_STREQ(line, "f…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "fo…");
|
ASSERT_STREQ(line, "fo…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo…");
|
ASSERT_STREQ(line, "foo…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo …");
|
ASSERT_STREQ(line, "foo …");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo b…");
|
ASSERT_STREQ(line, "foo b…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo ba…");
|
ASSERT_STREQ(line, "foo ba…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar…");
|
ASSERT_STREQ(line, "foo bar…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 9, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar …");
|
ASSERT_STREQ(line, "foo bar …");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar q…");
|
ASSERT_STREQ(line, "foo bar q…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar qu…");
|
ASSERT_STREQ(line, "foo bar qu…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar quux");
|
ASSERT_STREQ(line, "foo bar quux");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 13, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar quux");
|
ASSERT_STREQ(line, "foo bar quux");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 14, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar quux");
|
ASSERT_STREQ(line, "foo bar quux");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "foo bar quux");
|
ASSERT_STREQ(line, "foo bar quux");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
|
ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
|
||||||
assert_se(strv_equal(args, STRV_MAKE("foo", "bar", "quux")));
|
ASSERT_TRUE(strv_equal(args, STRV_MAKE("foo", "bar", "quux")));
|
||||||
args = strv_free(args);
|
args = strv_free(args);
|
||||||
|
|
||||||
assert_se(ftruncate(fd, 0) >= 0);
|
ASSERT_OK_ERRNO(ftruncate(fd, 0));
|
||||||
assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
|
ASSERT_OK_ERRNO(prctl(PR_SET_NAME, "aaaa bbbb cccc"));
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) == -ENOENT);
|
ASSERT_ERROR(pid_get_cmdline(0, SIZE_MAX, 0, &line), ENOENT);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "[aaaa bbbb cccc]");
|
ASSERT_STREQ(line, "[aaaa bbbb cccc]");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "[aaaa bbb…");
|
ASSERT_STREQ(line, "[aaaa bbb…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "[aaaa bbbb…");
|
ASSERT_STREQ(line, "[aaaa bbbb…");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line));
|
||||||
log_debug("'%s'", line);
|
log_debug("'%s'", line);
|
||||||
ASSERT_STREQ(line, "[aaaa bbbb …");
|
ASSERT_STREQ(line, "[aaaa bbbb …");
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
|
ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
|
||||||
assert_se(strv_equal(args, STRV_MAKE("[aaaa bbbb cccc]")));
|
ASSERT_TRUE(strv_equal(args, STRV_MAKE("[aaaa bbbb cccc]")));
|
||||||
args = strv_free(args);
|
args = strv_free(args);
|
||||||
|
|
||||||
/* Test with multiple arguments that do require quoting */
|
/* Test with multiple arguments that do require quoting */
|
||||||
|
@ -520,24 +526,24 @@ TEST(pid_get_cmdline_harder) {
|
||||||
#define EXPECT1p "foo $'\\'bar\\'' $'\"bar$\"' $'x y z' $'!``'"
|
#define EXPECT1p "foo $'\\'bar\\'' $'\"bar$\"' $'x y z' $'!``'"
|
||||||
#define EXPECT1v STRV_MAKE("foo", "'bar'", "\"bar$\"", "x y z", "!``")
|
#define EXPECT1v STRV_MAKE("foo", "'bar'", "\"bar$\"", "x y z", "!``")
|
||||||
|
|
||||||
assert_se(lseek(fd, SEEK_SET, 0) == 0);
|
ASSERT_OK_ZERO_ERRNO(lseek(fd, SEEK_SET, 0));
|
||||||
assert_se(write(fd, CMDLINE1, sizeof CMDLINE1) == sizeof CMDLINE1);
|
ASSERT_OK_EQ_ERRNO(write(fd, CMDLINE1, sizeof(CMDLINE1)), (ssize_t) sizeof(CMDLINE1));
|
||||||
assert_se(ftruncate(fd, sizeof CMDLINE1) == 0);
|
ASSERT_OK_ZERO_ERRNO(ftruncate(fd, sizeof(CMDLINE1)));
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line));
|
||||||
log_debug("got: ==%s==", line);
|
log_debug("got: ==%s==", line);
|
||||||
log_debug("exp: ==%s==", EXPECT1);
|
log_debug("exp: ==%s==", EXPECT1);
|
||||||
ASSERT_STREQ(line, EXPECT1);
|
ASSERT_STREQ(line, EXPECT1);
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line));
|
||||||
log_debug("got: ==%s==", line);
|
log_debug("got: ==%s==", line);
|
||||||
log_debug("exp: ==%s==", EXPECT1p);
|
log_debug("exp: ==%s==", EXPECT1p);
|
||||||
ASSERT_STREQ(line, EXPECT1p);
|
ASSERT_STREQ(line, EXPECT1p);
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline_strv(0, 0, &args) >= 0);
|
ASSERT_OK(pid_get_cmdline_strv(0, 0, &args));
|
||||||
assert_se(strv_equal(args, EXPECT1v));
|
ASSERT_TRUE(strv_equal(args, EXPECT1v));
|
||||||
args = strv_free(args);
|
args = strv_free(args);
|
||||||
|
|
||||||
#define CMDLINE2 "foo\0\1\2\3\0\0"
|
#define CMDLINE2 "foo\0\1\2\3\0\0"
|
||||||
|
@ -545,24 +551,24 @@ TEST(pid_get_cmdline_harder) {
|
||||||
#define EXPECT2p "foo $'\\001\\002\\003'"
|
#define EXPECT2p "foo $'\\001\\002\\003'"
|
||||||
#define EXPECT2v STRV_MAKE("foo", "\1\2\3")
|
#define EXPECT2v STRV_MAKE("foo", "\1\2\3")
|
||||||
|
|
||||||
assert_se(lseek(fd, SEEK_SET, 0) == 0);
|
ASSERT_OK_ZERO_ERRNO(lseek(fd, SEEK_SET, 0));
|
||||||
assert_se(write(fd, CMDLINE2, sizeof CMDLINE2) == sizeof CMDLINE2);
|
ASSERT_OK_EQ_ERRNO(write(fd, CMDLINE2, sizeof(CMDLINE2)), (ssize_t) sizeof(CMDLINE2));
|
||||||
assert_se(ftruncate(fd, sizeof CMDLINE2) == 0);
|
ASSERT_OK_ZERO_ERRNO(ftruncate(fd, sizeof CMDLINE2));
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line));
|
||||||
log_debug("got: ==%s==", line);
|
log_debug("got: ==%s==", line);
|
||||||
log_debug("exp: ==%s==", EXPECT2);
|
log_debug("exp: ==%s==", EXPECT2);
|
||||||
ASSERT_STREQ(line, EXPECT2);
|
ASSERT_STREQ(line, EXPECT2);
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
|
ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line));
|
||||||
log_debug("got: ==%s==", line);
|
log_debug("got: ==%s==", line);
|
||||||
log_debug("exp: ==%s==", EXPECT2p);
|
log_debug("exp: ==%s==", EXPECT2p);
|
||||||
ASSERT_STREQ(line, EXPECT2p);
|
ASSERT_STREQ(line, EXPECT2p);
|
||||||
line = mfree(line);
|
line = mfree(line);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline_strv(0, 0, &args) >= 0);
|
ASSERT_OK(pid_get_cmdline_strv(0, 0, &args));
|
||||||
assert_se(strv_equal(args, EXPECT2v));
|
ASSERT_TRUE(strv_equal(args, EXPECT2v));
|
||||||
args = strv_free(args);
|
args = strv_free(args);
|
||||||
|
|
||||||
safe_close(fd);
|
safe_close(fd);
|
||||||
|
@ -577,10 +583,11 @@ TEST(getpid_cached) {
|
||||||
b = getpid_cached();
|
b = getpid_cached();
|
||||||
c = getpid();
|
c = getpid();
|
||||||
|
|
||||||
assert_se(a == b && a == c);
|
ASSERT_EQ(a, b);
|
||||||
|
ASSERT_EQ(a, c);
|
||||||
|
|
||||||
child = fork();
|
child = fork();
|
||||||
assert_se(child >= 0);
|
ASSERT_OK_ERRNO(child);
|
||||||
|
|
||||||
if (child == 0) {
|
if (child == 0) {
|
||||||
/* In child */
|
/* In child */
|
||||||
|
@ -588,7 +595,8 @@ TEST(getpid_cached) {
|
||||||
b = getpid_cached();
|
b = getpid_cached();
|
||||||
c = getpid();
|
c = getpid();
|
||||||
|
|
||||||
assert_se(a == b && a == c);
|
ASSERT_EQ(a, b);
|
||||||
|
ASSERT_EQ(a, c);
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,11 +604,13 @@ TEST(getpid_cached) {
|
||||||
e = getpid_cached();
|
e = getpid_cached();
|
||||||
f = getpid();
|
f = getpid();
|
||||||
|
|
||||||
assert_se(a == d && a == e && a == f);
|
ASSERT_EQ(a, d);
|
||||||
|
ASSERT_EQ(a, e);
|
||||||
|
ASSERT_EQ(a, f);
|
||||||
|
|
||||||
assert_se(wait_for_terminate(child, &si) >= 0);
|
ASSERT_OK(wait_for_terminate(child, &si));
|
||||||
assert_se(si.si_status == 0);
|
ASSERT_EQ(si.si_status, 0);
|
||||||
assert_se(si.si_code == CLD_EXITED);
|
ASSERT_EQ(si.si_code, CLD_EXITED);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(getpid_measure) {
|
TEST(getpid_measure) {
|
||||||
|
@ -635,7 +645,7 @@ TEST(safe_fork) {
|
||||||
BLOCK_SIGNALS(SIGCHLD);
|
BLOCK_SIGNALS(SIGCHLD);
|
||||||
|
|
||||||
r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG, &pid);
|
r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG, &pid);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
|
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
/* child */
|
/* child */
|
||||||
|
@ -644,43 +654,42 @@ TEST(safe_fork) {
|
||||||
_exit(88);
|
_exit(88);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_se(wait_for_terminate(pid, &status) >= 0);
|
ASSERT_OK(wait_for_terminate(pid, &status));
|
||||||
assert_se(status.si_code == CLD_EXITED);
|
ASSERT_EQ(status.si_code, CLD_EXITED);
|
||||||
assert_se(status.si_status == 88);
|
ASSERT_EQ(status.si_status, 88);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pid_to_ptr) {
|
TEST(pid_to_ptr) {
|
||||||
assert_se(PTR_TO_PID(NULL) == 0);
|
ASSERT_EQ(PTR_TO_PID(NULL), 0);
|
||||||
ASSERT_NULL(PID_TO_PTR(0));
|
ASSERT_NULL(PID_TO_PTR(0));
|
||||||
|
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(1)), 1);
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(2)), 2);
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(-1)) == -1);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(-1)), -1);
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(-2)) == -2);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(-2)), -2);
|
||||||
|
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT16_MAX)), INT16_MAX);
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT16_MIN)), INT16_MIN);
|
||||||
|
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT32_MAX)), INT32_MAX);
|
||||||
assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN);
|
ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT32_MIN)), INT32_MIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_ioprio_class_from_to_string_one(const char *val, int expected, int normalized) {
|
static void test_ioprio_class_from_to_string_one(const char *val, int expected, int normalized) {
|
||||||
assert_se(ioprio_class_from_string(val) == expected);
|
ASSERT_EQ(ioprio_class_from_string(val), expected);
|
||||||
if (expected >= 0) {
|
if (expected >= 0) {
|
||||||
_cleanup_free_ char *s = NULL;
|
_cleanup_free_ char *s = NULL;
|
||||||
unsigned ret;
|
unsigned ret;
|
||||||
int combined;
|
int combined;
|
||||||
|
|
||||||
assert_se(ioprio_class_to_string_alloc(expected, &s) == 0);
|
ASSERT_OK_ZERO(ioprio_class_to_string_alloc(expected, &s));
|
||||||
/* We sometimes get a class number and sometimes a name back */
|
/* We sometimes get a class number and sometimes a name back */
|
||||||
assert_se(streq(s, val) ||
|
ASSERT_TRUE(streq(s, val) || safe_atou(val, &ret) == 0);
|
||||||
safe_atou(val, &ret) == 0);
|
|
||||||
|
|
||||||
/* Make sure normalization works, i.e. NONE → BE gets normalized */
|
/* Make sure normalization works, i.e. NONE → BE gets normalized */
|
||||||
combined = ioprio_normalize(ioprio_prio_value(expected, 0));
|
combined = ioprio_normalize(ioprio_prio_value(expected, 0));
|
||||||
assert_se(ioprio_prio_class(combined) == normalized);
|
ASSERT_EQ(ioprio_prio_class(combined), normalized);
|
||||||
assert_se(expected != IOPRIO_CLASS_NONE || ioprio_prio_data(combined) == 4);
|
ASSERT_TRUE(expected != IOPRIO_CLASS_NONE || ioprio_prio_data(combined) == 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,8 +710,8 @@ TEST(setpriority_closest) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = safe_fork("(test-setprio)",
|
r = safe_fork("(test-setprio)",
|
||||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL);
|
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG|FORK_REOPEN_LOG, NULL);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
|
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
bool full_test;
|
bool full_test;
|
||||||
|
@ -713,16 +722,21 @@ TEST(setpriority_closest) {
|
||||||
if (setrlimit(RLIMIT_NICE, &RLIMIT_MAKE_CONST(30)) < 0) {
|
if (setrlimit(RLIMIT_NICE, &RLIMIT_MAKE_CONST(30)) < 0) {
|
||||||
/* If this fails we are probably unprivileged or in a userns of some kind, let's skip
|
/* If this fails we are probably unprivileged or in a userns of some kind, let's skip
|
||||||
* the full test */
|
* the full test */
|
||||||
assert_se(ERRNO_IS_PRIVILEGE(errno));
|
if (!ERRNO_IS_PRIVILEGE(errno))
|
||||||
|
ASSERT_OK_ERRNO(-1);
|
||||||
full_test = false;
|
full_test = false;
|
||||||
} else {
|
} else {
|
||||||
/* However, if the hard limit was above 30, setrlimit would succeed unprivileged, so
|
/* However, if the hard limit was above 30, setrlimit would succeed unprivileged, so
|
||||||
* check if the UID/GID can be changed before enabling the full test. */
|
* check if the UID/GID can be changed before enabling the full test. */
|
||||||
if (setresgid(GID_NOBODY, GID_NOBODY, GID_NOBODY) < 0) {
|
if (setresgid(GID_NOBODY, GID_NOBODY, GID_NOBODY) < 0) {
|
||||||
assert_se(ERRNO_IS_PRIVILEGE(errno));
|
/* If the nobody user does not exist (user namespace) we get EINVAL. */
|
||||||
|
if (!ERRNO_IS_PRIVILEGE(errno) && errno != EINVAL)
|
||||||
|
ASSERT_OK_ERRNO(-1);
|
||||||
full_test = false;
|
full_test = false;
|
||||||
} else if (setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY) < 0) {
|
} else if (setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY) < 0) {
|
||||||
assert_se(ERRNO_IS_PRIVILEGE(errno));
|
/* If the nobody user does not exist (user namespace) we get EINVAL. */
|
||||||
|
if (!ERRNO_IS_PRIVILEGE(errno) && errno != EINVAL)
|
||||||
|
ASSERT_OK_ERRNO(-1);
|
||||||
full_test = false;
|
full_test = false;
|
||||||
} else
|
} else
|
||||||
full_test = true;
|
full_test = true;
|
||||||
|
@ -730,61 +744,69 @@ TEST(setpriority_closest) {
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
p = getpriority(PRIO_PROCESS, 0);
|
p = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0);
|
ASSERT_EQ(errno, 0);
|
||||||
|
|
||||||
/* It should always be possible to set our nice level to the current one */
|
/* It should always be possible to set our nice level to the current one */
|
||||||
assert_se(setpriority_closest(p) > 0);
|
ASSERT_OK_POSITIVE(setpriority_closest(p));
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && p == q);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(p, q);
|
||||||
|
|
||||||
/* It should also be possible to set the nice level to one higher */
|
/* It should also be possible to set the nice level to one higher */
|
||||||
if (p < PRIO_MAX-1) {
|
if (p < PRIO_MAX-1) {
|
||||||
assert_se(setpriority_closest(++p) > 0);
|
ASSERT_OK_POSITIVE(setpriority_closest(++p));
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && p == q);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(p, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It should also be possible to set the nice level to two higher */
|
/* It should also be possible to set the nice level to two higher */
|
||||||
if (p < PRIO_MAX-1) {
|
if (p < PRIO_MAX-1) {
|
||||||
assert_se(setpriority_closest(++p) > 0);
|
ASSERT_OK_POSITIVE(setpriority_closest(++p));
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && p == q);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(p, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (full_test) {
|
if (full_test) {
|
||||||
/* These two should work, given the RLIMIT_NICE we set above */
|
/* These two should work, given the RLIMIT_NICE we set above */
|
||||||
assert_se(setpriority_closest(-10) > 0);
|
ASSERT_OK_POSITIVE(setpriority_closest(-10));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && q == -10);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(q, -10);
|
||||||
|
|
||||||
assert_se(setpriority_closest(-9) > 0);
|
ASSERT_OK_POSITIVE(setpriority_closest(-9));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && q == -9);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(q, -9);
|
||||||
|
|
||||||
/* This should succeed but should be clamped to the limit */
|
/* This should succeed but should be clamped to the limit */
|
||||||
assert_se(setpriority_closest(-11) == 0);
|
ASSERT_OK_ZERO(setpriority_closest(-11));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && q == -10);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(q, -10);
|
||||||
|
|
||||||
assert_se(setpriority_closest(-8) > 0);
|
ASSERT_OK_POSITIVE(setpriority_closest(-8));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && q == -8);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(q, -8);
|
||||||
|
|
||||||
/* This should succeed but should be clamped to the limit */
|
/* This should succeed but should be clamped to the limit */
|
||||||
assert_se(setpriority_closest(-12) == 0);
|
ASSERT_OK_ZERO(setpriority_closest(-12));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
q = getpriority(PRIO_PROCESS, 0);
|
q = getpriority(PRIO_PROCESS, 0);
|
||||||
assert_se(errno == 0 && q == -10);
|
ASSERT_EQ(errno, 0);
|
||||||
|
ASSERT_EQ(q, -10);
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
|
@ -795,10 +817,10 @@ TEST(get_process_ppid) {
|
||||||
uint64_t limit;
|
uint64_t limit;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_se(get_process_ppid(1, NULL) == -EADDRNOTAVAIL);
|
ASSERT_ERROR(get_process_ppid(1, NULL), EADDRNOTAVAIL);
|
||||||
|
|
||||||
/* the process with the PID above the global limit definitely doesn't exist. Verify that */
|
/* the process with the PID above the global limit definitely doesn't exist. Verify that */
|
||||||
assert_se(procfs_get_pid_max(&limit) >= 0);
|
ASSERT_OK(procfs_get_pid_max(&limit));
|
||||||
log_debug("kernel.pid_max = %"PRIu64, limit);
|
log_debug("kernel.pid_max = %"PRIu64, limit);
|
||||||
|
|
||||||
if (limit < INT_MAX) {
|
if (limit < INT_MAX) {
|
||||||
|
@ -817,10 +839,10 @@ TEST(get_process_ppid) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
|
|
||||||
assert_se(pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c1) >= 0);
|
ASSERT_OK(pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c1));
|
||||||
assert_se(pid_get_cmdline(ppid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c2) >= 0);
|
ASSERT_OK(pid_get_cmdline(ppid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c2));
|
||||||
|
|
||||||
log_info("Parent of " PID_FMT " (%s) is " PID_FMT " (%s).", pid, c1, ppid, c2);
|
log_info("Parent of " PID_FMT " (%s) is " PID_FMT " (%s).", pid, c1, ppid, c2);
|
||||||
|
|
||||||
|
@ -831,19 +853,20 @@ TEST(get_process_ppid) {
|
||||||
TEST(set_oom_score_adjust) {
|
TEST(set_oom_score_adjust) {
|
||||||
int a, b, r;
|
int a, b, r;
|
||||||
|
|
||||||
assert_se(get_oom_score_adjust(&a) >= 0);
|
ASSERT_OK(get_oom_score_adjust(&a));
|
||||||
|
|
||||||
r = set_oom_score_adjust(OOM_SCORE_ADJ_MIN);
|
r = set_oom_score_adjust(OOM_SCORE_ADJ_MIN);
|
||||||
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r));
|
if (!ERRNO_IS_PRIVILEGE(r))
|
||||||
|
ASSERT_OK(r);
|
||||||
|
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
assert_se(get_oom_score_adjust(&b) >= 0);
|
ASSERT_OK(get_oom_score_adjust(&b));
|
||||||
assert_se(b == OOM_SCORE_ADJ_MIN);
|
ASSERT_EQ(b, OOM_SCORE_ADJ_MIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_se(set_oom_score_adjust(a) >= 0);
|
ASSERT_OK(set_oom_score_adjust(a));
|
||||||
assert_se(get_oom_score_adjust(&b) >= 0);
|
ASSERT_OK(get_oom_score_adjust(&b));
|
||||||
assert_se(b == a);
|
ASSERT_EQ(b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* dummy_thread(void *p) {
|
static void* dummy_thread(void *p) {
|
||||||
|
@ -851,10 +874,10 @@ static void* dummy_thread(void *p) {
|
||||||
char x;
|
char x;
|
||||||
|
|
||||||
/* let main thread know we are ready */
|
/* let main thread know we are ready */
|
||||||
assert_se(write(fd, &(const char) { 'x' }, 1) == 1);
|
ASSERT_OK_EQ_ERRNO(write(fd, &(const char) { 'x' }, 1), 1);
|
||||||
|
|
||||||
/* wait for the main thread to tell us to shut down */
|
/* wait for the main thread to tell us to shut down */
|
||||||
assert_se(read(fd, &x, 1) == 1);
|
ASSERT_OK_EQ_ERRNO(read(fd, &x, 1), 1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -862,38 +885,42 @@ TEST(get_process_threads) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* Run this test in a child, so that we can guarantee there's exactly one thread around in the child */
|
/* Run this test in a child, so that we can guarantee there's exactly one thread around in the child */
|
||||||
r = safe_fork("(nthreads)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_WAIT|FORK_LOG, NULL);
|
r = safe_fork("(nthreads)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
|
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
_cleanup_close_pair_ int pfd[2] = EBADF_PAIR, ppfd[2] = EBADF_PAIR;
|
_cleanup_close_pair_ int pfd[2] = EBADF_PAIR, ppfd[2] = EBADF_PAIR;
|
||||||
pthread_t t, tt;
|
pthread_t t, tt;
|
||||||
char x;
|
char x;
|
||||||
|
|
||||||
assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, pfd) >= 0);
|
ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, pfd));
|
||||||
assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, ppfd) >= 0);
|
ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, ppfd));
|
||||||
|
|
||||||
assert_se(get_process_threads(0) == 1);
|
ASSERT_OK_EQ(get_process_threads(0), 1);
|
||||||
assert_se(pthread_create(&t, NULL, &dummy_thread, FD_TO_PTR(pfd[0])) == 0);
|
ASSERT_OK_ZERO_ERRNO(pthread_create(&t, NULL, &dummy_thread, FD_TO_PTR(pfd[0])));
|
||||||
assert_se(read(pfd[1], &x, 1) == 1);
|
ASSERT_OK_EQ_ERRNO(read(pfd[1], &x, 1), 1);
|
||||||
assert_se(get_process_threads(0) == 2);
|
ASSERT_OK_EQ(get_process_threads(0), 2);
|
||||||
assert_se(pthread_create(&tt, NULL, &dummy_thread, FD_TO_PTR(ppfd[0])) == 0);
|
ASSERT_OK_ZERO_ERRNO(pthread_create(&tt, NULL, &dummy_thread, FD_TO_PTR(ppfd[0])));
|
||||||
assert_se(read(ppfd[1], &x, 1) == 1);
|
ASSERT_OK_EQ_ERRNO(read(ppfd[1], &x, 1), 1);
|
||||||
assert_se(get_process_threads(0) == 3);
|
ASSERT_OK_EQ(get_process_threads(0), 3);
|
||||||
|
|
||||||
assert_se(write(pfd[1], &(const char) { 'x' }, 1) == 1);
|
ASSERT_OK_EQ_ERRNO(write(pfd[1], &(const char) { 'x' }, 1), 1);
|
||||||
assert_se(pthread_join(t, NULL) == 0);
|
ASSERT_OK_ZERO_ERRNO(pthread_join(t, NULL));
|
||||||
|
|
||||||
/* the value reported via /proc/ is decreased asynchronously, and there appears to be no nice
|
/* the value reported via /proc/ is decreased asynchronously, and there appears to be no nice
|
||||||
* way to sync on it. Hence we do the weak >= 2 check, even though == 2 is what we'd actually
|
* way to sync on it. Hence we do the weak >= 2 check, even though == 2 is what we'd actually
|
||||||
* like to check here */
|
* like to check here */
|
||||||
assert_se(get_process_threads(0) >= 2);
|
r = get_process_threads(0);
|
||||||
|
ASSERT_OK(r);
|
||||||
|
ASSERT_GE(r, 2);
|
||||||
|
|
||||||
assert_se(write(ppfd[1], &(const char) { 'x' }, 1) == 1);
|
ASSERT_OK_EQ_ERRNO(write(ppfd[1], &(const char) { 'x' }, 1), 1);
|
||||||
assert_se(pthread_join(tt, NULL) == 0);
|
ASSERT_OK_ZERO_ERRNO(pthread_join(tt, NULL));
|
||||||
|
|
||||||
/* similar here */
|
/* similar here */
|
||||||
assert_se(get_process_threads(0) >= 1);
|
r = get_process_threads(0);
|
||||||
|
ASSERT_OK(r);
|
||||||
|
ASSERT_GE(r, 1);
|
||||||
|
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -902,17 +929,17 @@ TEST(get_process_threads) {
|
||||||
TEST(is_reaper_process) {
|
TEST(is_reaper_process) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = safe_fork("(regular)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_WAIT, NULL);
|
r = safe_fork("(regular)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
/* child */
|
/* child */
|
||||||
|
|
||||||
assert_se(is_reaper_process() == 0);
|
ASSERT_OK_ZERO(is_reaper_process());
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = safe_fork("(newpid)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_WAIT, NULL);
|
r = safe_fork("(newpid)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
/* child */
|
/* child */
|
||||||
|
|
||||||
|
@ -923,25 +950,25 @@ TEST(is_reaper_process) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r = safe_fork("(newpid1)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_WAIT, NULL);
|
r = safe_fork("(newpid1)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
/* grandchild, which is PID1 in a pidns */
|
/* grandchild, which is PID1 in a pidns */
|
||||||
assert_se(getpid_cached() == 1);
|
ASSERT_OK_EQ(getpid_cached(), 1);
|
||||||
assert_se(is_reaper_process() > 0);
|
ASSERT_OK_POSITIVE(is_reaper_process());
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_WAIT, NULL);
|
r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
|
||||||
assert_se(r >= 0);
|
ASSERT_OK(r);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
/* child */
|
/* child */
|
||||||
assert_se(make_reaper_process(true) >= 0);
|
ASSERT_OK(make_reaper_process(true));
|
||||||
|
|
||||||
assert_se(is_reaper_process() > 0);
|
ASSERT_OK_POSITIVE(is_reaper_process());
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -949,22 +976,22 @@ TEST(is_reaper_process) {
|
||||||
TEST(pid_get_start_time) {
|
TEST(pid_get_start_time) {
|
||||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||||
|
|
||||||
assert_se(pidref_set_self(&pidref) >= 0);
|
ASSERT_OK(pidref_set_self(&pidref));
|
||||||
|
|
||||||
usec_t start_time;
|
usec_t start_time;
|
||||||
assert_se(pidref_get_start_time(&pidref, &start_time) >= 0);
|
ASSERT_OK(pidref_get_start_time(&pidref, &start_time));
|
||||||
log_info("our starttime: " USEC_FMT, start_time);
|
log_info("our starttime: " USEC_FMT, start_time);
|
||||||
|
|
||||||
_cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
|
_cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
|
||||||
|
|
||||||
assert_se(pidref_safe_fork("(stub)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &child) >= 0);
|
ASSERT_OK(pidref_safe_fork("(stub)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG, &child));
|
||||||
|
|
||||||
usec_t start_time2;
|
usec_t start_time2;
|
||||||
assert_se(pidref_get_start_time(&child, &start_time2) >= 0);
|
ASSERT_OK(pidref_get_start_time(&child, &start_time2));
|
||||||
|
|
||||||
log_info("child starttime: " USEC_FMT, start_time2);
|
log_info("child starttime: " USEC_FMT, start_time2);
|
||||||
|
|
||||||
assert_se(start_time2 >= start_time);
|
ASSERT_GE(start_time2, start_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intro(void) {
|
static int intro(void) {
|
||||||
|
|
|
@ -250,12 +250,12 @@ static void context_done(Context *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Different kinds of errors that mean that information is not available in the environment. */
|
/* Different kinds of errors that mean that information is not available in the environment. */
|
||||||
static bool ERRNO_IS_NOINFO(int r) {
|
static bool ERRNO_IS_NEG_NOINFO(intmax_t r) {
|
||||||
return IN_SET(abs(r),
|
return IN_SET(r,
|
||||||
EUNATCH, /* os-release or machine-id missing */
|
-EUNATCH, /* os-release or machine-id missing */
|
||||||
ENOMEDIUM, /* machine-id or another file empty */
|
-ENOMEDIUM, /* machine-id or another file empty */
|
||||||
ENOPKG, /* machine-id is uninitialized */
|
-ENOPKG, /* machine-id is uninitialized */
|
||||||
ENXIO); /* env var is unset */
|
-ENXIO); /* env var is unset */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int specifier_directory(
|
static int specifier_directory(
|
||||||
|
@ -349,49 +349,35 @@ static int log_unresolvable_specifier(const char *filename, unsigned line) {
|
||||||
arg_dry_run ? (would) : (doing), \
|
arg_dry_run ? (would) : (doing), \
|
||||||
__VA_ARGS__)
|
__VA_ARGS__)
|
||||||
|
|
||||||
static int user_config_paths(char*** ret) {
|
static int user_config_paths(char ***ret) {
|
||||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||||
_cleanup_free_ char *persistent_config = NULL, *runtime_config = NULL, *data_home = NULL;
|
_cleanup_free_ char *runtime_config = NULL;
|
||||||
_cleanup_strv_free_ char **res = NULL;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = xdg_user_dirs(&config_dirs, &data_dirs);
|
assert(ret);
|
||||||
|
|
||||||
|
/* Combined user-specific and global dirs */
|
||||||
|
r = user_search_dirs("/user-tmpfiles.d", &config_dirs, &data_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = xdg_user_config_dir(&persistent_config, "/user-tmpfiles.d");
|
r = xdg_user_runtime_dir("/user-tmpfiles.d", &runtime_config);
|
||||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
if (r < 0 && !ERRNO_IS_NEG_NOINFO(r))
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = xdg_user_runtime_dir(&runtime_config, "/user-tmpfiles.d");
|
r = strv_consume(&config_dirs, TAKE_PTR(runtime_config));
|
||||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = xdg_user_data_dir(&data_home, "/user-tmpfiles.d");
|
|
||||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/user-tmpfiles.d");
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = strv_extend_many(
|
r = strv_extend_strv(&config_dirs, data_dirs, /* filter_duplicates = */ true);
|
||||||
&res,
|
|
||||||
persistent_config,
|
|
||||||
runtime_config,
|
|
||||||
data_home);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/user-tmpfiles.d");
|
r = path_strv_make_absolute_cwd(config_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = path_strv_make_absolute_cwd(res);
|
*ret = TAKE_PTR(config_dirs);
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(res);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3694,7 +3680,7 @@ static int parse_line(
|
||||||
i.purge = purge;
|
i.purge = purge;
|
||||||
|
|
||||||
r = specifier_printf(path, PATH_MAX-1, specifier_table, arg_root, NULL, &i.path);
|
r = specifier_printf(path, PATH_MAX-1, specifier_table, arg_root, NULL, &i.path);
|
||||||
if (ERRNO_IS_NOINFO(r))
|
if (ERRNO_IS_NEG_NOINFO(r))
|
||||||
return log_unresolvable_specifier(fname, line);
|
return log_unresolvable_specifier(fname, line);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (IN_SET(r, -EINVAL, -EBADSLT))
|
if (IN_SET(r, -EINVAL, -EBADSLT))
|
||||||
|
@ -3858,7 +3844,7 @@ static int parse_line(
|
||||||
if (!unbase64) {
|
if (!unbase64) {
|
||||||
/* Do specifier expansion except if base64 mode is enabled */
|
/* Do specifier expansion except if base64 mode is enabled */
|
||||||
r = specifier_expansion_from_arg(specifier_table, &i);
|
r = specifier_expansion_from_arg(specifier_table, &i);
|
||||||
if (ERRNO_IS_NOINFO(r))
|
if (ERRNO_IS_NEG_NOINFO(r))
|
||||||
return log_unresolvable_specifier(fname, line);
|
return log_unresolvable_specifier(fname, line);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (IN_SET(r, -EINVAL, -EBADSLT))
|
if (IN_SET(r, -EINVAL, -EBADSLT))
|
||||||
|
@ -4551,7 +4537,7 @@ static int run(int argc, char *argv[]) {
|
||||||
PHASE_CREATE,
|
PHASE_CREATE,
|
||||||
_PHASE_MAX
|
_PHASE_MAX
|
||||||
} phase;
|
} phase;
|
||||||
int r, k;
|
int r;
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
|
@ -4694,21 +4680,15 @@ static int run(int argc, char *argv[]) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* The non-globbing ones usually create things, hence we apply them first */
|
/* The non-globbing ones usually create things, hence we apply them first */
|
||||||
ORDERED_HASHMAP_FOREACH(a, c.items) {
|
ORDERED_HASHMAP_FOREACH(a, c.items)
|
||||||
k = process_item_array(&c, a, op);
|
RET_GATHER(r, process_item_array(&c, a, op));
|
||||||
if (k < 0 && r >= 0)
|
|
||||||
r = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The globbing ones usually alter things, hence we apply them second. */
|
/* The globbing ones usually alter things, hence we apply them second. */
|
||||||
ORDERED_HASHMAP_FOREACH(a, c.globs) {
|
ORDERED_HASHMAP_FOREACH(a, c.globs)
|
||||||
k = process_item_array(&c, a, op);
|
RET_GATHER(r, process_item_array(&c, a, op));
|
||||||
if (k < 0 && r >= 0)
|
|
||||||
r = k;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ERRNO_IS_RESOURCE(r))
|
if (ERRNO_IS_NEG_RESOURCE(r))
|
||||||
return r;
|
return r;
|
||||||
if (invalid_config)
|
if (invalid_config)
|
||||||
return EX_DATAERR;
|
return EX_DATAERR;
|
||||||
|
|
|
@ -259,7 +259,7 @@ static int run(int argc, char *argv[]) {
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (arg_graceful && tpm2_support() != TPM2_SUPPORT_FULL) {
|
if (arg_graceful && !tpm2_is_fully_supported()) {
|
||||||
log_notice("No complete TPM2 support detected, exiting gracefully.");
|
log_notice("No complete TPM2 support detected, exiting gracefully.");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,7 +288,7 @@ static int verb_info(int argc, char *argv[], void *userdata) {
|
||||||
pager_open(arg_pager_flags);
|
pager_open(arg_pager_flags);
|
||||||
|
|
||||||
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
|
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
|
||||||
static const struct sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "vendor", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, vendor), SD_JSON_MANDATORY },
|
{ "vendor", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, vendor), SD_JSON_MANDATORY },
|
||||||
{ "product", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, product), SD_JSON_MANDATORY },
|
{ "product", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, product), SD_JSON_MANDATORY },
|
||||||
{ "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, version), SD_JSON_MANDATORY },
|
{ "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, version), SD_JSON_MANDATORY },
|
||||||
|
@ -380,12 +380,12 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
const struct sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "interfaces", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, PTR_TO_SIZE(&auto_interfaces), SD_JSON_MANDATORY },
|
{ "interfaces", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, 0, SD_JSON_MANDATORY },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, NULL);
|
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &auto_interfaces);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -412,7 +412,7 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) || list_methods) {
|
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) || list_methods) {
|
||||||
static const struct sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
|
{ "description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -212,7 +212,7 @@ static int get_firmware_search_dirs(char ***ret) {
|
||||||
* Prioritising entries in "more specific" directories */
|
* Prioritising entries in "more specific" directories */
|
||||||
|
|
||||||
_cleanup_free_ char *user_firmware_dir = NULL;
|
_cleanup_free_ char *user_firmware_dir = NULL;
|
||||||
r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
|
r = xdg_user_config_dir("/qemu/firmware", &user_firmware_dir);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -1488,11 +1488,11 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||||
|
|
||||||
/* if we are going to be starting any units with state then create our runtime dir */
|
/* if we are going to be starting any units with state then create our runtime dir */
|
||||||
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
||||||
r = runtime_directory(&arg_runtime_directory, arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn");
|
r = runtime_directory(arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn",
|
||||||
|
&arg_runtime_directory);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to lookup runtime directory: %m");
|
return log_error_errno(r, "Failed to lookup runtime directory: %m");
|
||||||
if (r) {
|
if (r > 0) { /* We need to create our own runtime dir */
|
||||||
/* r > 0 means we need to create our own runtime dir */
|
|
||||||
r = mkdir_p(arg_runtime_directory, 0755);
|
r = mkdir_p(arg_runtime_directory, 0755);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to create runtime directory: %m");
|
return log_error_errno(r, "Failed to create runtime directory: %m");
|
||||||
|
|
|
@ -20,6 +20,39 @@
|
||||||
|
|
||||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(xdgautostartservice_hash_ops, char, string_hash_func, string_compare_func, XdgAutostartService, xdg_autostart_service_free);
|
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(xdgautostartservice_hash_ops, char, string_hash_func, string_compare_func, XdgAutostartService, xdg_autostart_service_free);
|
||||||
|
|
||||||
|
static int xdg_base_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
|
||||||
|
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||||
|
const char *e;
|
||||||
|
|
||||||
|
/* Implement the mechanisms defined in
|
||||||
|
* https://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html */
|
||||||
|
|
||||||
|
assert(ret_config_dirs);
|
||||||
|
assert(ret_data_dirs);
|
||||||
|
|
||||||
|
e = getenv("XDG_CONFIG_DIRS");
|
||||||
|
if (e)
|
||||||
|
config_dirs = strv_split(e, ":");
|
||||||
|
else
|
||||||
|
config_dirs = strv_new("/etc/xdg");
|
||||||
|
if (!config_dirs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
e = getenv("XDG_DATA_DIRS");
|
||||||
|
if (e)
|
||||||
|
data_dirs = strv_split(e, ":");
|
||||||
|
else
|
||||||
|
data_dirs = strv_new("/usr/local/share",
|
||||||
|
"/usr/share");
|
||||||
|
if (!data_dirs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret_config_dirs = TAKE_PTR(config_dirs);
|
||||||
|
*ret_data_dirs = TAKE_PTR(data_dirs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int enumerate_xdg_autostart(Hashmap *all_services) {
|
static int enumerate_xdg_autostart(Hashmap *all_services) {
|
||||||
_cleanup_strv_free_ char **autostart_dirs = NULL;
|
_cleanup_strv_free_ char **autostart_dirs = NULL;
|
||||||
_cleanup_strv_free_ char **config_dirs = NULL;
|
_cleanup_strv_free_ char **config_dirs = NULL;
|
||||||
|
@ -27,14 +60,14 @@ static int enumerate_xdg_autostart(Hashmap *all_services) {
|
||||||
_cleanup_free_ char *user_config_autostart_dir = NULL;
|
_cleanup_free_ char *user_config_autostart_dir = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = xdg_user_config_dir(&user_config_autostart_dir, "/autostart");
|
r = xdg_user_config_dir("/autostart", &user_config_autostart_dir);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
r = strv_extend(&autostart_dirs, user_config_autostart_dir);
|
r = strv_extend(&autostart_dirs, user_config_autostart_dir);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = xdg_user_dirs(&config_dirs, &data_dirs);
|
r = xdg_base_dirs(&config_dirs, &data_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
r = strv_extend_strv_concat(&autostart_dirs, (const char* const*) config_dirs, "/autostart");
|
r = strv_extend_strv_concat(&autostart_dirs, (const char* const*) config_dirs, "/autostart");
|
||||||
|
|
|
@ -4,5 +4,6 @@ integration_tests += [
|
||||||
integration_test_template + {
|
integration_test_template + {
|
||||||
'name' : fs.name(meson.current_source_dir()),
|
'name' : fs.name(meson.current_source_dir()),
|
||||||
'storage': 'persistent',
|
'storage': 'persistent',
|
||||||
|
'vm' : true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,6 +21,9 @@ at_exit() {
|
||||||
|
|
||||||
trap at_exit EXIT
|
trap at_exit EXIT
|
||||||
|
|
||||||
|
systemctl unmask systemd-networkd.service
|
||||||
|
systemctl start systemd-networkd.service
|
||||||
|
|
||||||
export NETWORK_NAME="10-networkctl-test-$RANDOM.network"
|
export NETWORK_NAME="10-networkctl-test-$RANDOM.network"
|
||||||
export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev"
|
export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev"
|
||||||
export LINK_NAME="10-networkctl-test-$RANDOM.link"
|
export LINK_NAME="10-networkctl-test-$RANDOM.link"
|
||||||
|
@ -75,15 +78,6 @@ cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test.conf"
|
||||||
networkctl cat "$NETWORK_NAME" | grep '^# ' |
|
networkctl cat "$NETWORK_NAME" | grep '^# ' |
|
||||||
cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf")
|
cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf")
|
||||||
|
|
||||||
networkctl edit --stdin --runtime "$NETDEV_NAME" <<EOF
|
|
||||||
[NetDev]
|
|
||||||
Name=test2
|
|
||||||
Kind=dummy
|
|
||||||
EOF
|
|
||||||
|
|
||||||
networkctl cat "$NETDEV_NAME" | grep -v '^# ' |
|
|
||||||
cmp - <(printf '%s\n' "[NetDev]" "Name=test2" "Kind=dummy")
|
|
||||||
|
|
||||||
cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF
|
cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF
|
||||||
[Match]
|
[Match]
|
||||||
OriginalName=test2
|
OriginalName=test2
|
||||||
|
@ -95,13 +89,23 @@ EOF
|
||||||
SYSTEMD_LOG_LEVEL=debug EDITOR='true' script -ec 'networkctl edit "$LINK_NAME"' /dev/null
|
SYSTEMD_LOG_LEVEL=debug EDITOR='true' script -ec 'networkctl edit "$LINK_NAME"' /dev/null
|
||||||
cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME"
|
cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME"
|
||||||
|
|
||||||
# Test links
|
# The interface test2 does not exist, hence the below do not work.
|
||||||
systemctl unmask systemd-networkd
|
|
||||||
systemctl stop systemd-networkd
|
|
||||||
(! networkctl cat @test2)
|
(! networkctl cat @test2)
|
||||||
(! networkctl cat @test2:netdev)
|
(! networkctl cat @test2:netdev)
|
||||||
|
(! networkctl cat @test2:link)
|
||||||
|
(! networkctl cat @test2:network)
|
||||||
|
|
||||||
systemctl start systemd-networkd
|
# create .netdev file at last, otherwise, the .link file will not be applied to the interface.
|
||||||
|
networkctl edit --stdin --runtime "$NETDEV_NAME" <<EOF
|
||||||
|
[NetDev]
|
||||||
|
Name=test2
|
||||||
|
Kind=dummy
|
||||||
|
EOF
|
||||||
|
|
||||||
|
networkctl cat "$NETDEV_NAME" | grep -v '^# ' |
|
||||||
|
cmp - <(printf '%s\n' "[NetDev]" "Name=test2" "Kind=dummy")
|
||||||
|
|
||||||
|
# wait for the interface being created and configured.
|
||||||
SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
|
SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
|
||||||
|
|
||||||
networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME")
|
networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME")
|
||||||
|
|
Loading…
Reference in New Issue