mirror of
https://github.com/systemd/systemd
synced 2024-07-09 04:26:06 +00:00
Merge pull request #27113 from keszybz/variable-expansion-rework
Rework serialization of command lines in pid1 and make run not expand variables
This commit is contained in:
commit
208a59c15f
|
@ -92,6 +92,11 @@
|
|||
Consider using the <option>exec</option> service type (i.e. <option>--property=Type=exec</option>) to
|
||||
ensure that <command>systemd-run</command> returns successfully only if the specified command line has
|
||||
been successfully started.</para>
|
||||
|
||||
<para>After <command>systemd-run</command> passes the command to the service manager, the manager
|
||||
performs variable expansion. This means that dollar characters (<literal>$</literal>) which should not be
|
||||
expanded need to be escaped as <literal>$$</literal>. Expansion can also be disabled using
|
||||
<varname>--expand-environment=no</varname>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -169,6 +174,25 @@
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--expand-environment=<replaceable>BOOL</replaceable></option></term>
|
||||
|
||||
<listitem><para>Expand environment variables in command arguments. If enabled (the default),
|
||||
environment variables specified as <literal>${<replaceable>VARIABLE</replaceable>}</literal> will be
|
||||
expanded in the same way as in commands specified via <varname>ExecStart=</varname> in units. With
|
||||
<varname>--scope</varname>, this expansion is performed by <command>systemd-run</command> itself, and
|
||||
in other cases by the service manager that spawns the command. Note that this is similar to, but not
|
||||
the same as variable expansion in
|
||||
<citerefentry project='man-pages'><refentrytitle>bash</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
and other shells.</para>
|
||||
|
||||
<para>See
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for a description of variable expansion. Disabling variable expansion is useful if the specified
|
||||
command includes or may include a <literal>$</literal> sign.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-r</option></term>
|
||||
<term><option>--remain-after-exit</option></term>
|
||||
|
@ -528,12 +552,49 @@ There is a screen on:
|
|||
<programlisting>$ loginctl enable-linger</programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Variable expansion by the manager</title>
|
||||
|
||||
<programlisting>$ systemd-run -t echo "<${INVOCATION_ID}>" '<${INVOCATION_ID}>'
|
||||
<> <5d0149bfa2c34b79bccb13074001eb20>
|
||||
</programlisting>
|
||||
|
||||
<para>The first argument is expanded by the shell (double quotes), but the second one is not expanded
|
||||
by the shell (single quotes). <command>echo</command> is called with [<literal>/usr/bin/echo</literal>,
|
||||
<literal>[]</literal>, <literal>[${INVOCATION_ID}]</literal>] as the argument array, and then
|
||||
<command>systemd</command> generates <varname>${INVOCATION_ID}</varname> and substitutes it in the
|
||||
command-line. This substitution could not be done on the client side, because the target ID that will
|
||||
be set for the service isn't known before the call is made.</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Variable expansion and output redirection using a shell</title>
|
||||
|
||||
<para>Variable expansion by <command>systemd</command> can be disabled with
|
||||
<varname>--expand-environment=no</varname>.</para>
|
||||
|
||||
<para>Disabling variable expansion can be useful if the command to execute contains dollar characters
|
||||
and escaping them would be inconvenient. For example, when a shell is used:</para>
|
||||
|
||||
<programlisting>$ systemd-run --expand-environment=no -t bash \
|
||||
-c 'echo $SHELL $$ >/dev/stdout'
|
||||
/bin/bash 12345
|
||||
</programlisting>
|
||||
|
||||
<para>The last argument is passed verbatim to the <command>bash</command> shell which is started by the
|
||||
service unit. The shell expands <literal>$SHELL</literal> to the path of the shell, and
|
||||
<literal>$$</literal> to its process number, and then those strings are passed to the
|
||||
<command>echo</command> built-in and printed to standard output (which in this case is connected to the
|
||||
calling terminal).</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Return value</title>
|
||||
|
||||
<programlisting>$ systemd-run --user --wait true
|
||||
$ systemd-run --user --wait -p SuccessExitStatus=11 bash -c 'exit 11'
|
||||
$ systemd-run --user --wait -p SuccessExitStatus=SIGUSR1 bash -c 'kill -SIGUSR1 $$$$'</programlisting>
|
||||
$ systemd-run --user --wait -p SuccessExitStatus=SIGUSR1 --expand-environment=no \
|
||||
bash -c 'kill -SIGUSR1 $$'</programlisting>
|
||||
|
||||
<para>Those three invocations will succeed, i.e. terminate with an exit code of 0.</para>
|
||||
</example>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "graphics.h"
|
||||
#include "logarithm.h"
|
||||
#include "proto/graphics-output.h"
|
||||
#include "splash.h"
|
||||
#include "unaligned-fundamental.h"
|
||||
|
@ -141,14 +142,14 @@ static void read_channel_maks(
|
|||
channel_shift[R] = __builtin_ctz(dib->channel_mask_r);
|
||||
channel_shift[G] = __builtin_ctz(dib->channel_mask_g);
|
||||
channel_shift[B] = __builtin_ctz(dib->channel_mask_b);
|
||||
channel_scale[R] = 0xff / ((1 << __builtin_popcount(dib->channel_mask_r)) - 1);
|
||||
channel_scale[G] = 0xff / ((1 << __builtin_popcount(dib->channel_mask_g)) - 1);
|
||||
channel_scale[B] = 0xff / ((1 << __builtin_popcount(dib->channel_mask_b)) - 1);
|
||||
channel_scale[R] = 0xff / ((1 << popcount(dib->channel_mask_r)) - 1);
|
||||
channel_scale[G] = 0xff / ((1 << popcount(dib->channel_mask_g)) - 1);
|
||||
channel_scale[B] = 0xff / ((1 << popcount(dib->channel_mask_b)) - 1);
|
||||
|
||||
if (dib->size >= SIZEOF_BMP_DIB_RGBA && dib->channel_mask_a != 0) {
|
||||
channel_mask[A] = dib->channel_mask_a;
|
||||
channel_shift[A] = __builtin_ctz(dib->channel_mask_a);
|
||||
channel_scale[A] = 0xff / ((1 << __builtin_popcount(dib->channel_mask_a)) - 1);
|
||||
channel_scale[A] = 0xff / ((1 << popcount(dib->channel_mask_a)) - 1);
|
||||
} else {
|
||||
channel_mask[A] = 0;
|
||||
channel_shift[A] = 0;
|
||||
|
|
|
@ -1543,6 +1543,9 @@ int bus_set_transient_exec_command(
|
|||
unsigned n = 0;
|
||||
int r;
|
||||
|
||||
/* Drop Ex from the written setting. E.g. ExecStart=, not ExecStartEx=. */
|
||||
const char *written_name = is_ex_prop ? strndupa(name, strlen(name) - 2) : name;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1624,31 +1627,32 @@ int bus_set_transient_exec_command(
|
|||
if (!f)
|
||||
return -ENOMEM;
|
||||
|
||||
fprintf(f, "%s=\n", name);
|
||||
fprintf(f, "%s=\n", written_name);
|
||||
|
||||
LIST_FOREACH(command, c, *exec_command) {
|
||||
_cleanup_free_ char *a = NULL, *exec_chars = NULL;
|
||||
UnitWriteFlags esc_flags = UNIT_ESCAPE_SPECIFIERS |
|
||||
(FLAGS_SET(c->flags, EXEC_COMMAND_NO_ENV_EXPAND) ? UNIT_ESCAPE_EXEC_SYNTAX : UNIT_ESCAPE_EXEC_SYNTAX_ENV);
|
||||
|
||||
exec_chars = exec_command_flags_to_exec_chars(c->flags);
|
||||
if (!exec_chars)
|
||||
return -ENOMEM;
|
||||
|
||||
a = unit_concat_strv(c->argv, UNIT_ESCAPE_SPECIFIERS|UNIT_ESCAPE_EXEC_SYNTAX);
|
||||
a = unit_concat_strv(c->argv, esc_flags);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
if (streq_ptr(c->path, c->argv ? c->argv[0] : NULL))
|
||||
fprintf(f, "%s=%s%s\n", name, exec_chars, a);
|
||||
fprintf(f, "%s=%s%s\n", written_name, exec_chars, a);
|
||||
else {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *p;
|
||||
|
||||
p = unit_escape_setting(c->path,
|
||||
UNIT_ESCAPE_SPECIFIERS|UNIT_ESCAPE_EXEC_SYNTAX, &t);
|
||||
p = unit_escape_setting(c->path, esc_flags, &t);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
fprintf(f, "%s=%s@%s %s\n", name, exec_chars, p, a);
|
||||
fprintf(f, "%s=%s@%s %s\n", written_name, exec_chars, p, a);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1656,7 +1660,7 @@ int bus_set_transient_exec_command(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
unit_write_setting(u, flags, name, buf);
|
||||
unit_write_setting(u, flags, written_name, buf);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -670,7 +670,8 @@ static int bus_service_set_transient_property(
|
|||
return bus_set_transient_exit_status(u, name, &s->success_status, message, flags, error);
|
||||
|
||||
ci = service_exec_command_from_string(name);
|
||||
ci = (ci >= 0) ? ci : service_exec_ex_command_from_string(name);
|
||||
if (ci < 0)
|
||||
ci = service_exec_ex_command_from_string(name);
|
||||
if (ci >= 0)
|
||||
return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error);
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "load-dropin.h"
|
||||
#include "load-fragment.h"
|
||||
#include "log.h"
|
||||
#include "logarithm.h"
|
||||
#include "macro.h"
|
||||
#include "missing_audit.h"
|
||||
#include "mkdir-label.h"
|
||||
|
@ -4375,7 +4376,7 @@ static const char* unit_drop_in_dir(Unit *u, UnitWriteFlags flags) {
|
|||
|
||||
const char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) {
|
||||
assert(s);
|
||||
assert(!FLAGS_SET(flags, UNIT_ESCAPE_EXEC_SYNTAX | UNIT_ESCAPE_C));
|
||||
assert(popcount(flags & (UNIT_ESCAPE_EXEC_SYNTAX_ENV | UNIT_ESCAPE_EXEC_SYNTAX | UNIT_ESCAPE_C)) <= 1);
|
||||
assert(buf);
|
||||
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
@ -4395,10 +4396,19 @@ const char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf)
|
|||
}
|
||||
|
||||
/* We either do C-escaping or shell-escaping, to additionally escape characters that we parse for
|
||||
* ExecStart= and friends, i.e. '$' and ';' and quotes. */
|
||||
* ExecStart= and friends, i.e. '$' and quotes. */
|
||||
|
||||
if (flags & UNIT_ESCAPE_EXEC_SYNTAX) {
|
||||
char *t2 = shell_escape(s, "$;'\"");
|
||||
if (flags & (UNIT_ESCAPE_EXEC_SYNTAX_ENV | UNIT_ESCAPE_EXEC_SYNTAX)) {
|
||||
char *t2;
|
||||
|
||||
if (flags & UNIT_ESCAPE_EXEC_SYNTAX_ENV) {
|
||||
t2 = strreplace(s, "$", "$$");
|
||||
if (!t2)
|
||||
return NULL;
|
||||
free_and_replace(t, t2);
|
||||
}
|
||||
|
||||
t2 = shell_escape(t ?: s, "\"");
|
||||
if (!t2)
|
||||
return NULL;
|
||||
free_and_replace(t, t2);
|
||||
|
@ -4406,7 +4416,9 @@ const char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf)
|
|||
s = t;
|
||||
|
||||
} else if (flags & UNIT_ESCAPE_C) {
|
||||
char *t2 = cescape(s);
|
||||
char *t2;
|
||||
|
||||
t2 = cescape(s);
|
||||
if (!t2)
|
||||
return NULL;
|
||||
free_and_replace(t, t2);
|
||||
|
|
|
@ -534,22 +534,25 @@ typedef struct UnitStatusMessageFormats {
|
|||
/* Flags used when writing drop-in files or transient unit files */
|
||||
typedef enum UnitWriteFlags {
|
||||
/* Write a runtime unit file or drop-in (i.e. one below /run) */
|
||||
UNIT_RUNTIME = 1 << 0,
|
||||
UNIT_RUNTIME = 1 << 0,
|
||||
|
||||
/* Write a persistent drop-in (i.e. one below /etc) */
|
||||
UNIT_PERSISTENT = 1 << 1,
|
||||
UNIT_PERSISTENT = 1 << 1,
|
||||
|
||||
/* Place this item in the per-unit-type private section, instead of [Unit] */
|
||||
UNIT_PRIVATE = 1 << 2,
|
||||
UNIT_PRIVATE = 1 << 2,
|
||||
|
||||
/* Apply specifier escaping before writing */
|
||||
UNIT_ESCAPE_SPECIFIERS = 1 << 3,
|
||||
/* Apply specifier escaping */
|
||||
UNIT_ESCAPE_SPECIFIERS = 1 << 3,
|
||||
|
||||
/* Escape elements of ExecStart= syntax before writing */
|
||||
UNIT_ESCAPE_EXEC_SYNTAX = 1 << 4,
|
||||
/* Escape elements of ExecStart= syntax, incl. prevention of variable expansion */
|
||||
UNIT_ESCAPE_EXEC_SYNTAX_ENV = 1 << 4,
|
||||
|
||||
/* Escape elements of ExecStart=: syntax (no variable expansion) */
|
||||
UNIT_ESCAPE_EXEC_SYNTAX = 1 << 5,
|
||||
|
||||
/* Apply C escaping before writing */
|
||||
UNIT_ESCAPE_C = 1 << 5,
|
||||
UNIT_ESCAPE_C = 1 << 6,
|
||||
} UnitWriteFlags;
|
||||
|
||||
/* Returns true if neither persistent, nor runtime storage is requested, i.e. this is a check invocation only */
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* Note: log2(0) == log2(1) == 0 here and below. */
|
||||
|
||||
#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0)
|
||||
|
@ -30,6 +28,14 @@ static inline unsigned u32ctz(uint32_t n) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#define popcount(n) \
|
||||
_Generic((n), \
|
||||
unsigned char: __builtin_popcount(n), \
|
||||
unsigned short: __builtin_popcount(n), \
|
||||
unsigned: __builtin_popcount(n), \
|
||||
unsigned long: __builtin_popcountl(n), \
|
||||
unsigned long long: __builtin_popcountll(n))
|
||||
|
||||
#define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0)
|
||||
#define NONCONST_LOG2U(x) ({ \
|
||||
unsigned _x = (x); \
|
414
src/run/run.c
414
src/run/run.c
|
@ -45,6 +45,7 @@ static const char *arg_unit = NULL;
|
|||
static const char *arg_description = NULL;
|
||||
static const char *arg_slice = NULL;
|
||||
static bool arg_slice_inherit = false;
|
||||
static bool arg_expand_environment = true;
|
||||
static bool arg_send_sighup = false;
|
||||
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
|
||||
static const char *arg_host = NULL;
|
||||
|
@ -102,6 +103,7 @@ static int help(void) {
|
|||
" --description=TEXT Description for unit\n"
|
||||
" --slice=SLICE Run in the specified slice\n"
|
||||
" --slice-inherit Inherit the slice\n"
|
||||
" --expand-environment=BOOL Control expansion of environment variables\n"
|
||||
" --no-block Do not wait until operation finished\n"
|
||||
" -r --remain-after-exit Leave service around until explicitly stopped\n"
|
||||
" --wait Wait until service stopped again\n"
|
||||
|
@ -168,6 +170,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_DESCRIPTION,
|
||||
ARG_SLICE,
|
||||
ARG_SLICE_INHERIT,
|
||||
ARG_EXPAND_ENVIRONMENT,
|
||||
ARG_SEND_SIGHUP,
|
||||
ARG_SERVICE_TYPE,
|
||||
ARG_EXEC_USER,
|
||||
|
@ -192,47 +195,48 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "scope", no_argument, NULL, ARG_SCOPE },
|
||||
{ "unit", required_argument, NULL, 'u' },
|
||||
{ "description", required_argument, NULL, ARG_DESCRIPTION },
|
||||
{ "slice", required_argument, NULL, ARG_SLICE },
|
||||
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
|
||||
{ "remain-after-exit", no_argument, NULL, 'r' },
|
||||
{ "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
|
||||
{ "host", required_argument, NULL, 'H' },
|
||||
{ "machine", required_argument, NULL, 'M' },
|
||||
{ "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
|
||||
{ "wait", no_argument, NULL, ARG_WAIT },
|
||||
{ "uid", required_argument, NULL, ARG_EXEC_USER },
|
||||
{ "gid", required_argument, NULL, ARG_EXEC_GROUP },
|
||||
{ "nice", required_argument, NULL, ARG_NICE },
|
||||
{ "setenv", required_argument, NULL, 'E' },
|
||||
{ "property", required_argument, NULL, 'p' },
|
||||
{ "tty", no_argument, NULL, 't' }, /* deprecated alias */
|
||||
{ "pty", no_argument, NULL, 't' },
|
||||
{ "pipe", no_argument, NULL, 'P' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "on-active", required_argument, NULL, ARG_ON_ACTIVE },
|
||||
{ "on-boot", required_argument, NULL, ARG_ON_BOOT },
|
||||
{ "on-startup", required_argument, NULL, ARG_ON_STARTUP },
|
||||
{ "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
|
||||
{ "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
|
||||
{ "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
|
||||
{ "on-timezone-change",no_argument, NULL, ARG_ON_TIMEZONE_CHANGE},
|
||||
{ "on-clock-change", no_argument, NULL, ARG_ON_CLOCK_CHANGE },
|
||||
{ "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
|
||||
{ "path-property", required_argument, NULL, ARG_PATH_PROPERTY },
|
||||
{ "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY },
|
||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{ "collect", no_argument, NULL, 'G' },
|
||||
{ "working-directory", required_argument, NULL, ARG_WORKING_DIRECTORY },
|
||||
{ "same-dir", no_argument, NULL, 'd' },
|
||||
{ "shell", no_argument, NULL, 'S' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "scope", no_argument, NULL, ARG_SCOPE },
|
||||
{ "unit", required_argument, NULL, 'u' },
|
||||
{ "description", required_argument, NULL, ARG_DESCRIPTION },
|
||||
{ "slice", required_argument, NULL, ARG_SLICE },
|
||||
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
|
||||
{ "remain-after-exit", no_argument, NULL, 'r' },
|
||||
{ "expand-environment", required_argument, NULL, ARG_EXPAND_ENVIRONMENT },
|
||||
{ "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
|
||||
{ "host", required_argument, NULL, 'H' },
|
||||
{ "machine", required_argument, NULL, 'M' },
|
||||
{ "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
|
||||
{ "wait", no_argument, NULL, ARG_WAIT },
|
||||
{ "uid", required_argument, NULL, ARG_EXEC_USER },
|
||||
{ "gid", required_argument, NULL, ARG_EXEC_GROUP },
|
||||
{ "nice", required_argument, NULL, ARG_NICE },
|
||||
{ "setenv", required_argument, NULL, 'E' },
|
||||
{ "property", required_argument, NULL, 'p' },
|
||||
{ "tty", no_argument, NULL, 't' }, /* deprecated alias */
|
||||
{ "pty", no_argument, NULL, 't' },
|
||||
{ "pipe", no_argument, NULL, 'P' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "on-active", required_argument, NULL, ARG_ON_ACTIVE },
|
||||
{ "on-boot", required_argument, NULL, ARG_ON_BOOT },
|
||||
{ "on-startup", required_argument, NULL, ARG_ON_STARTUP },
|
||||
{ "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
|
||||
{ "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
|
||||
{ "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
|
||||
{ "on-timezone-change", no_argument, NULL, ARG_ON_TIMEZONE_CHANGE },
|
||||
{ "on-clock-change", no_argument, NULL, ARG_ON_CLOCK_CHANGE },
|
||||
{ "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
|
||||
{ "path-property", required_argument, NULL, ARG_PATH_PROPERTY },
|
||||
{ "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY },
|
||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{ "collect", no_argument, NULL, 'G' },
|
||||
{ "working-directory", required_argument, NULL, ARG_WORKING_DIRECTORY },
|
||||
{ "same-dir", no_argument, NULL, 'd' },
|
||||
{ "shell", no_argument, NULL, 'S' },
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -287,6 +291,12 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_slice_inherit = true;
|
||||
break;
|
||||
|
||||
case ARG_EXPAND_ENVIRONMENT:
|
||||
r = parse_boolean_argument("--expand-environment=", optarg, &arg_expand_environment);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_SEND_SIGHUP:
|
||||
arg_send_sighup = true;
|
||||
break;
|
||||
|
@ -719,6 +729,11 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
|
|||
bool send_term = false;
|
||||
int r;
|
||||
|
||||
/* We disable environment expansion on the server side via ExecStartEx=:.
|
||||
* ExecStartEx was added relatively recently (v243), and some bugs were fixed only later.
|
||||
* So use that feature only if required. It will fail with older systemds. */
|
||||
bool use_ex_prop = !arg_expand_environment;
|
||||
|
||||
assert(m);
|
||||
|
||||
r = transient_unit_set_properties(m, UNIT_SERVICE, arg_property);
|
||||
|
@ -850,19 +865,23 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
|
|||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "s", "ExecStart");
|
||||
r = sd_bus_message_append(m, "s",
|
||||
use_ex_prop ? "ExecStartEx" : "ExecStart");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "a(sasb)");
|
||||
r = sd_bus_message_open_container(m, 'v',
|
||||
use_ex_prop ? "a(sasas)" : "a(sasb)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "(sasb)");
|
||||
r = sd_bus_message_open_container(m, 'a',
|
||||
use_ex_prop ? "(sasas)" : "(sasb)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'r', "sasb");
|
||||
r = sd_bus_message_open_container(m, 'r',
|
||||
use_ex_prop ? "sasas" : "sasb");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
|
@ -874,7 +893,12 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
|
|||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "b", false);
|
||||
if (use_ex_prop)
|
||||
r = sd_bus_message_append_strv(
|
||||
m,
|
||||
STRV_MAKE(arg_expand_environment ? NULL : "no-env-expand"));
|
||||
else
|
||||
r = sd_bus_message_append(m, "b", false);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
|
@ -1112,10 +1136,78 @@ static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int start_transient_service(
|
||||
static int make_transient_service_unit(
|
||||
sd_bus *bus,
|
||||
int *retval) {
|
||||
sd_bus_message **message,
|
||||
const char *service,
|
||||
const char *pty_path) {
|
||||
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(message);
|
||||
assert(service);
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* Name and mode */
|
||||
r = sd_bus_message_append(m, "ss", service, "fail");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* Properties */
|
||||
r = sd_bus_message_open_container(m, 'a', "(sv)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = transient_service_set_properties(m, pty_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* Auxiliary units */
|
||||
r = sd_bus_message_append(m, "a(sa(sv))", 0);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
*message = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_call_with_hint(
|
||||
sd_bus *bus,
|
||||
sd_bus_message *message,
|
||||
const char *name,
|
||||
sd_bus_message **reply) {
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int r;
|
||||
|
||||
r = sd_bus_call(bus, message, 0, &error, reply);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to start transient %s unit: %s", name, bus_error_message(&error, r));
|
||||
|
||||
if (!arg_expand_environment &&
|
||||
sd_bus_error_has_names(&error,
|
||||
SD_BUS_ERROR_UNKNOWN_PROPERTY,
|
||||
SD_BUS_ERROR_PROPERTY_READ_ONLY))
|
||||
log_notice_errno(r, "Hint: --expand-environment=no is not supported by old systemd");
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int start_transient_service(sd_bus *bus) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
|
||||
|
@ -1124,7 +1216,6 @@ static int start_transient_service(
|
|||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(retval);
|
||||
|
||||
if (arg_stdio == ARG_STDIO_PTY) {
|
||||
|
||||
|
@ -1197,42 +1288,15 @@ static int start_transient_service(
|
|||
return r;
|
||||
}
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* Name and mode */
|
||||
r = sd_bus_message_append(m, "ss", service, "fail");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* Properties */
|
||||
r = sd_bus_message_open_container(m, 'a', "(sv)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = transient_service_set_properties(m, pty_path);
|
||||
r = make_transient_service_unit(bus, &m, service, pty_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* Auxiliary units */
|
||||
r = sd_bus_message_append(m, "a(sa(sv))", 0);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
r = sd_bus_call(bus, m, 0, &error, &reply);
|
||||
r = bus_call_with_hint(bus, m, "service", &reply);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to start transient service unit: %s", bus_error_message(&error, r));
|
||||
return r;
|
||||
|
||||
if (w) {
|
||||
const char *object;
|
||||
|
@ -1363,16 +1427,15 @@ static int start_transient_service(
|
|||
/* Try to propagate the service's return value. But if the service defines
|
||||
* e.g. SuccessExitStatus, honour this, and return 0 to mean "success". */
|
||||
if (streq_ptr(c.result, "success"))
|
||||
*retval = 0;
|
||||
else if (streq_ptr(c.result, "exit-code") && c.exit_status > 0)
|
||||
*retval = c.exit_status;
|
||||
else if (streq_ptr(c.result, "signal"))
|
||||
*retval = EXIT_EXCEPTION;
|
||||
else
|
||||
*retval = EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
if (streq_ptr(c.result, "exit-code") && c.exit_status > 0)
|
||||
return c.exit_status;
|
||||
if (streq_ptr(c.result, "signal"))
|
||||
return EXIT_EXCEPTION;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int acquire_invocation_id(sd_bus *bus, sd_id128_t *ret) {
|
||||
|
@ -1411,7 +1474,7 @@ static int start_transient_scope(sd_bus *bus) {
|
|||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
|
||||
_cleanup_strv_free_ char **env = NULL, **user_env = NULL;
|
||||
_cleanup_strv_free_ char **env = NULL, **user_env = NULL, **expanded_cmdline = NULL;
|
||||
_cleanup_free_ char *scope = NULL;
|
||||
const char *object = NULL;
|
||||
sd_id128_t invocation_id;
|
||||
|
@ -1553,75 +1616,33 @@ static int start_transient_scope(sd_bus *bus) {
|
|||
if (!arg_quiet)
|
||||
log_info("Running scope as unit: %s", scope);
|
||||
|
||||
if (arg_expand_environment) {
|
||||
expanded_cmdline = replace_env_argv(arg_cmdline, env);
|
||||
if (!expanded_cmdline)
|
||||
return log_oom();
|
||||
arg_cmdline = expanded_cmdline;
|
||||
}
|
||||
|
||||
execvpe(arg_cmdline[0], arg_cmdline, env);
|
||||
|
||||
return log_error_errno(errno, "Failed to execute: %m");
|
||||
}
|
||||
|
||||
static int start_transient_trigger(
|
||||
static int make_transient_trigger_unit(
|
||||
sd_bus *bus,
|
||||
const char *suffix) {
|
||||
sd_bus_message **message,
|
||||
const char *suffix,
|
||||
const char *trigger,
|
||||
const char *service) {
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
|
||||
_cleanup_free_ char *trigger = NULL, *service = NULL;
|
||||
const char *object = NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
|
||||
r = bus_wait_for_jobs_new(bus, &w);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (arg_unit) {
|
||||
switch (unit_name_to_type(arg_unit)) {
|
||||
|
||||
case UNIT_SERVICE:
|
||||
service = strdup(arg_unit);
|
||||
if (!service)
|
||||
return log_oom();
|
||||
|
||||
r = unit_name_change_suffix(service, suffix, &trigger);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to change unit suffix: %m");
|
||||
break;
|
||||
|
||||
case UNIT_TIMER:
|
||||
trigger = strdup(arg_unit);
|
||||
if (!trigger)
|
||||
return log_oom();
|
||||
|
||||
r = unit_name_change_suffix(trigger, ".service", &service);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to change unit suffix: %m");
|
||||
break;
|
||||
|
||||
default:
|
||||
r = unit_name_mangle_with_suffix(arg_unit, "as unit",
|
||||
arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
|
||||
".service", &service);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mangle unit name: %m");
|
||||
|
||||
r = unit_name_mangle_with_suffix(arg_unit, "as trigger",
|
||||
arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
|
||||
suffix, &trigger);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mangle unit name: %m");
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
r = make_unit_name(bus, UNIT_SERVICE, &service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_name_change_suffix(service, suffix, &trigger);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to change unit suffix: %m");
|
||||
}
|
||||
assert(message);
|
||||
assert(suffix);
|
||||
assert(trigger);
|
||||
assert(service);
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
|
||||
if (r < 0)
|
||||
|
@ -1690,11 +1711,81 @@ static int start_transient_trigger(
|
|||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
*message = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_transient_trigger(sd_bus *bus, const char *suffix) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
|
||||
_cleanup_free_ char *trigger = NULL, *service = NULL;
|
||||
const char *object = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(suffix);
|
||||
|
||||
r = bus_wait_for_jobs_new(bus, &w);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (arg_unit) {
|
||||
switch (unit_name_to_type(arg_unit)) {
|
||||
|
||||
case UNIT_SERVICE:
|
||||
service = strdup(arg_unit);
|
||||
if (!service)
|
||||
return log_oom();
|
||||
|
||||
r = unit_name_change_suffix(service, suffix, &trigger);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to change unit suffix: %m");
|
||||
break;
|
||||
|
||||
case UNIT_TIMER:
|
||||
trigger = strdup(arg_unit);
|
||||
if (!trigger)
|
||||
return log_oom();
|
||||
|
||||
r = unit_name_change_suffix(trigger, ".service", &service);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to change unit suffix: %m");
|
||||
break;
|
||||
|
||||
default:
|
||||
r = unit_name_mangle_with_suffix(arg_unit, "as unit",
|
||||
arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
|
||||
".service", &service);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mangle unit name: %m");
|
||||
|
||||
r = unit_name_mangle_with_suffix(arg_unit, "as trigger",
|
||||
arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
|
||||
suffix, &trigger);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mangle unit name: %m");
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
r = make_unit_name(bus, UNIT_SERVICE, &service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_name_change_suffix(service, suffix, &trigger);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to change unit suffix: %m");
|
||||
}
|
||||
|
||||
r = make_transient_trigger_unit(bus, &m, suffix, trigger, service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
r = sd_bus_call(bus, m, 0, &error, &reply);
|
||||
r = bus_call_with_hint(bus, m, suffix + 1, &reply);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, r));
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read(reply, "o", &object);
|
||||
if (r < 0)
|
||||
|
@ -1710,7 +1801,7 @@ static int start_transient_trigger(
|
|||
log_info("Will run service as unit: %s", service);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static bool shall_make_executable_absolute(void) {
|
||||
|
@ -1729,7 +1820,7 @@ static bool shall_make_executable_absolute(void) {
|
|||
static int run(int argc, char* argv[]) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_free_ char *description = NULL;
|
||||
int r, retval = EXIT_SUCCESS;
|
||||
int r;
|
||||
|
||||
log_show_color(true);
|
||||
log_parse_environment();
|
||||
|
@ -1776,19 +1867,14 @@ static int run(int argc, char* argv[]) {
|
|||
return bus_log_connect_error(r, arg_transport);
|
||||
|
||||
if (arg_scope)
|
||||
r = start_transient_scope(bus);
|
||||
else if (arg_path_property)
|
||||
r = start_transient_trigger(bus, ".path");
|
||||
else if (arg_socket_property)
|
||||
r = start_transient_trigger(bus, ".socket");
|
||||
else if (arg_with_timer)
|
||||
r = start_transient_trigger(bus, ".timer");
|
||||
else
|
||||
r = start_transient_service(bus, &retval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return retval;
|
||||
return start_transient_scope(bus);
|
||||
if (arg_path_property)
|
||||
return start_transient_trigger(bus, ".path");
|
||||
if (arg_socket_property)
|
||||
return start_transient_trigger(bus, ".socket");
|
||||
if (arg_with_timer)
|
||||
return start_transient_trigger(bus, ".timer");
|
||||
return start_transient_service(bus);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "hexdecoct.h"
|
||||
#include "hmac.h"
|
||||
#include "lock-util.h"
|
||||
#include "log.h"
|
||||
#include "logarithm.h"
|
||||
#include "memory-util.h"
|
||||
#include "openssl-util.h"
|
||||
#include "parse-util.h"
|
||||
|
@ -774,7 +776,7 @@ size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s) {
|
|||
|
||||
uint32_t mask;
|
||||
tpm2_tpms_pcr_selection_to_mask(s, &mask);
|
||||
return (size_t)__builtin_popcount(mask);
|
||||
return popcount(mask);
|
||||
}
|
||||
|
||||
/* Utility functions for TPML_PCR_SELECTION. */
|
||||
|
|
|
@ -218,25 +218,6 @@ tests += [
|
|||
'sources' : files('test-boot-timestamps.c'),
|
||||
'condition' : 'ENABLE_EFI',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-bpf-devices.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-bpf-firewall.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-bpf-foreign-programs.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-bpf-lsm.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-btrfs.c'),
|
||||
'type' : 'manual',
|
||||
|
@ -252,27 +233,10 @@ tests += [
|
|||
'sources' : files('test-capability.c'),
|
||||
'dependencies' : libcap,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-cgroup-cpu.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-cgroup-mask.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-cgroup-unit-default.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-chase-manual.c'),
|
||||
'type' : 'manual',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-chown-rec.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-compress-benchmark.c'),
|
||||
'link_with' : [
|
||||
|
@ -298,27 +262,12 @@ tests += [
|
|||
'sources' : files('test-dlopen-so.c'),
|
||||
'dependencies' : libp11kit_cflags
|
||||
},
|
||||
{
|
||||
'sources' : files('test-emergency-action.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-engine.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : [
|
||||
files('test-errno-list.c'),
|
||||
generated_gperf_headers,
|
||||
],
|
||||
},
|
||||
{
|
||||
'sources' : files('test-execute.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
'timeout' : 360,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-fd-util.c'),
|
||||
'dependencies' : libseccomp,
|
||||
|
@ -331,11 +280,6 @@ tests += [
|
|||
],
|
||||
'timeout' : 180,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-install.c'),
|
||||
'base' : test_core_base,
|
||||
'type' : 'manual',
|
||||
},
|
||||
{
|
||||
'sources' : [
|
||||
files('test-ip-protocol-list.c'),
|
||||
|
@ -346,11 +290,6 @@ tests += [
|
|||
'sources' : files('test-ipcrm.c'),
|
||||
'type' : 'unsafe',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-job-type.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-json.c'),
|
||||
'dependencies' : libm,
|
||||
|
@ -367,25 +306,10 @@ tests += [
|
|||
threads,
|
||||
],
|
||||
},
|
||||
{
|
||||
'sources' : files('test-load-fragment.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-loop-block.c'),
|
||||
'dependencies' : [threads, libblkid],
|
||||
'base' : test_core_base,
|
||||
'parallel' : false,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-loopback.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-manager.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-math-util.c'),
|
||||
'dependencies' : libm,
|
||||
|
@ -394,26 +318,12 @@ tests += [
|
|||
'sources' : files('test-mempress.c'),
|
||||
'dependencies' : threads,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-namespace.c'),
|
||||
'dependencies' : [
|
||||
libblkid,
|
||||
threads,
|
||||
],
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-netlink-manual.c'),
|
||||
'dependencies' : libkmod,
|
||||
'condition' : 'HAVE_KMOD',
|
||||
'type' : 'manual',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-ns.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
'type' : 'manual',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-nscd-flush.c'),
|
||||
'condition' : 'ENABLE_NSCD',
|
||||
|
@ -440,12 +350,6 @@ tests += [
|
|||
'sources' : files('test-parse-util.c'),
|
||||
'dependencies' : libm,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-path.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
'timeout' : 120,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-process-util.c'),
|
||||
'dependencies' : threads,
|
||||
|
@ -464,11 +368,6 @@ tests += [
|
|||
'condition' : 'ENABLE_BOOTLOADER',
|
||||
'c_args' : '-I@0@'.format(efi_config_h_dir),
|
||||
},
|
||||
{
|
||||
'sources' : files('test-sched-prio.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-seccomp.c'),
|
||||
'dependencies' : libseccomp,
|
||||
|
@ -526,6 +425,133 @@ tests += [
|
|||
'includes' : udev_includes,
|
||||
'type' : 'manual',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-utmp.c'),
|
||||
'condition' : 'ENABLE_UTMP',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-varlink.c'),
|
||||
'dependencies' : threads,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-watchdog.c'),
|
||||
'type' : 'unsafe',
|
||||
},
|
||||
|
||||
|
||||
# Tests that link to libcore, i.e. tests for pid1 code.
|
||||
{
|
||||
'sources' : files('test-bpf-devices.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-bpf-firewall.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-bpf-foreign-programs.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-bpf-lsm.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-cgroup-cpu.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-cgroup-mask.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-cgroup-unit-default.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-chown-rec.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-core-unit.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-emergency-action.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-engine.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-execute.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
'timeout' : 360,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-install.c'),
|
||||
'base' : test_core_base,
|
||||
'type' : 'manual',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-job-type.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-load-fragment.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-loop-block.c'),
|
||||
'dependencies' : [threads, libblkid],
|
||||
'base' : test_core_base,
|
||||
'parallel' : false,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-manager.c'),
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-namespace.c'),
|
||||
'dependencies' : [
|
||||
libblkid,
|
||||
threads,
|
||||
],
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-ns.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
'type' : 'manual',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-path.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
'timeout' : 120,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-sched-prio.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-socket-bind.c'),
|
||||
'dependencies' : libdl,
|
||||
'condition' : 'BPF_FRAMEWORK',
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-unit-name.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
|
@ -536,30 +562,13 @@ tests += [
|
|||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-utmp.c'),
|
||||
'condition' : 'ENABLE_UTMP',
|
||||
},
|
||||
{
|
||||
'sources' : files('test-varlink.c'),
|
||||
'dependencies' : threads,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-watch-pid.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'base' : test_core_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-watchdog.c'),
|
||||
'type' : 'unsafe',
|
||||
},
|
||||
]
|
||||
|
||||
############################################################
|
||||
|
||||
# define some tests here, because the link_with deps were not defined earlier
|
||||
|
||||
tests += [
|
||||
# Tests from other directories that have link_with deps that were not defined earlier
|
||||
{
|
||||
'sources' : files('../libsystemd/sd-bus/test-bus-error.c'),
|
||||
'link_with' : [
|
||||
|
@ -577,10 +586,4 @@ tests += [
|
|||
'link_with' : libudev,
|
||||
'dependencies' : threads,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-socket-bind.c'),
|
||||
'dependencies' : libdl,
|
||||
'condition' : 'BPF_FRAMEWORK',
|
||||
'base' : test_core_base,
|
||||
},
|
||||
]
|
||||
|
|
120
src/test/test-core-unit.c
Normal file
120
src/test/test-core-unit.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "escape.h"
|
||||
#include "tests.h"
|
||||
#include "unit.h"
|
||||
|
||||
static void test_unit_escape_setting_one(
|
||||
const char *s,
|
||||
const char *expected_exec_env,
|
||||
const char *expected_exec,
|
||||
const char *expected_c) {
|
||||
|
||||
_cleanup_free_ char *a = NULL, *b, *c, *d,
|
||||
*s_esc, *a_esc, *b_esc, *c_esc, *d_esc;
|
||||
const char *t;
|
||||
|
||||
if (!expected_exec_env)
|
||||
expected_exec_env = s;
|
||||
if (!expected_exec)
|
||||
expected_exec = expected_exec_env;
|
||||
if (!expected_c)
|
||||
expected_c = expected_exec;
|
||||
assert_se(s_esc = cescape(s));
|
||||
|
||||
assert_se(t = unit_escape_setting(s, 0, &a));
|
||||
assert_se(a_esc = cescape(t));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, a_esc);
|
||||
assert_se(a == NULL);
|
||||
assert_se(t == s);
|
||||
|
||||
assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_EXEC_SYNTAX_ENV, &b));
|
||||
assert_se(b_esc = cescape(t));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
|
||||
assert_se(b == NULL || streq(b, t));
|
||||
assert_se(streq(t, expected_exec_env));
|
||||
|
||||
assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_EXEC_SYNTAX, &c));
|
||||
assert_se(c_esc = cescape(t));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
|
||||
assert_se(c == NULL || streq(c, t));
|
||||
assert_se(streq(t, expected_exec));
|
||||
|
||||
assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_C, &d));
|
||||
assert_se(d_esc = cescape(t));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
|
||||
assert_se(d == NULL || streq(d, t));
|
||||
assert_se(streq(t, expected_c));
|
||||
}
|
||||
|
||||
TEST(unit_escape_setting) {
|
||||
test_unit_escape_setting_one("/sbin/sbash", NULL, NULL, NULL);
|
||||
test_unit_escape_setting_one("$", "$$", "$", "$");
|
||||
test_unit_escape_setting_one("$$", "$$$$", "$$", "$$");
|
||||
test_unit_escape_setting_one("'", "'", NULL, "\\'");
|
||||
test_unit_escape_setting_one("\"", "\\\"", NULL, NULL);
|
||||
test_unit_escape_setting_one("\t", "\\t", NULL, NULL);
|
||||
test_unit_escape_setting_one(" ", NULL, NULL, NULL);
|
||||
test_unit_escape_setting_one("$;'\"\t\n", "$$;'\\\"\\t\\n", "$;'\\\"\\t\\n", "$;\\'\\\"\\t\\n");
|
||||
}
|
||||
|
||||
static void test_unit_concat_strv_one(
|
||||
char **s,
|
||||
const char *expected_none,
|
||||
const char *expected_exec_env,
|
||||
const char *expected_exec,
|
||||
const char *expected_c) {
|
||||
|
||||
_cleanup_free_ char *a, *b, *c, *d,
|
||||
*s_ser, *s_esc, *a_esc, *b_esc, *c_esc, *d_esc;
|
||||
|
||||
assert_se(s_ser = strv_join(s, "_"));
|
||||
assert_se(s_esc = cescape(s_ser));
|
||||
if (!expected_exec_env)
|
||||
expected_exec_env = expected_none;
|
||||
if (!expected_exec)
|
||||
expected_exec = expected_none;
|
||||
if (!expected_c)
|
||||
expected_c = expected_none;
|
||||
|
||||
assert_se(a = unit_concat_strv(s, 0));
|
||||
assert_se(a_esc = cescape(a));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, a_esc);
|
||||
assert_se(streq(a, expected_none));
|
||||
|
||||
assert_se(b = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX_ENV));
|
||||
assert_se(b_esc = cescape(b));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
|
||||
assert_se(streq(b, expected_exec_env));
|
||||
|
||||
assert_se(c = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX));
|
||||
assert_se(c_esc = cescape(c));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
|
||||
assert_se(streq(c, expected_exec));
|
||||
|
||||
assert_se(d = unit_concat_strv(s, UNIT_ESCAPE_C));
|
||||
assert_se(d_esc = cescape(d));
|
||||
log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
|
||||
assert_se(streq(d, expected_c));
|
||||
}
|
||||
|
||||
TEST(unit_concat_strv) {
|
||||
test_unit_concat_strv_one(STRV_MAKE("a", "b", "c"),
|
||||
"\"a\" \"b\" \"c\"",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
test_unit_concat_strv_one(STRV_MAKE("a", " ", "$", "$$", ""),
|
||||
"\"a\" \" \" \"$\" \"$$\" \"\"",
|
||||
"\"a\" \" \" \"$$\" \"$$$$\" \"\"",
|
||||
NULL,
|
||||
NULL);
|
||||
test_unit_concat_strv_one(STRV_MAKE("\n", " ", "\t"),
|
||||
"\"\n\" \" \" \"\t\"",
|
||||
"\"\\n\" \" \" \"\\t\"",
|
||||
"\"\\n\" \" \" \"\\t\"",
|
||||
"\"\\n\" \" \" \"\\t\"");
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
|
@ -71,4 +71,25 @@ TEST(log2i) {
|
|||
assert_se(log2i(INT_MAX) == sizeof(int)*8-2);
|
||||
}
|
||||
|
||||
TEST(popcount) {
|
||||
uint16_t u16a = 0x0000;
|
||||
uint16_t u16b = 0xFFFF;
|
||||
uint32_t u32a = 0x00000010;
|
||||
uint32_t u32b = 0xFFFFFFFF;
|
||||
uint64_t u64a = 0x0000000000000010;
|
||||
uint64_t u64b = 0x0100000000100010;
|
||||
|
||||
assert_se(popcount(u16a) == 0);
|
||||
assert_se(popcount(u16b) == 16);
|
||||
assert_se(popcount(u32a) == 1);
|
||||
assert_se(popcount(u32b) == 32);
|
||||
assert_se(popcount(u64a) == 1);
|
||||
assert_se(popcount(u64b) == 3);
|
||||
|
||||
/* This would fail:
|
||||
* error: ‘_Generic’ selector of type ‘int’ is not compatible with any association
|
||||
* assert_se(popcount(0x10) == 1);
|
||||
*/
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
|
Loading…
Reference in New Issue
Block a user