• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13210122091

07 Feb 2025 10:56PM UTC coverage: 71.748% (-0.07%) from 71.813%
13210122091

push

github

web-flow
network: allow to configure routing policy rule even if requesting interface is not activated yet (#36257)

Fixes a regression caused by 4f8b153d9
(v257).
Fixes #36244.

16 of 16 new or added lines in 2 files covered. (100.0%)

3079 existing lines in 54 files now uncovered.

292917 of 408258 relevant lines covered (71.75%)

714241.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.37
/src/run/run.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <getopt.h>
4
#include <stdio.h>
5
#include <fcntl.h>
6
#include <sys/stat.h>
7
#include <sys/types.h>
8

9
#include "sd-bus.h"
10
#include "sd-event.h"
11
#include "sd-json.h"
12

13
#include "alloc-util.h"
14
#include "build.h"
15
#include "bus-error.h"
16
#include "bus-locator.h"
17
#include "bus-map-properties.h"
18
#include "bus-message-util.h"
19
#include "bus-unit-util.h"
20
#include "bus-wait-for-jobs.h"
21
#include "calendarspec.h"
22
#include "capsule-util.h"
23
#include "chase.h"
24
#include "env-util.h"
25
#include "escape.h"
26
#include "event-util.h"
27
#include "exec-util.h"
28
#include "exit-status.h"
29
#include "fd-util.h"
30
#include "format-util.h"
31
#include "fs-util.h"
32
#include "hostname-util.h"
33
#include "main-func.h"
34
#include "parse-argument.h"
35
#include "parse-util.h"
36
#include "path-util.h"
37
#include "polkit-agent.h"
38
#include "pretty-print.h"
39
#include "process-util.h"
40
#include "ptyfwd.h"
41
#include "signal-util.h"
42
#include "special.h"
43
#include "string-table.h"
44
#include "strv.h"
45
#include "terminal-util.h"
46
#include "uid-classification.h"
47
#include "unit-def.h"
48
#include "unit-name.h"
49
#include "user-util.h"
50

51
static bool arg_ask_password = true;
52
static bool arg_scope = false;
53
static bool arg_remain_after_exit = false;
54
static bool arg_no_block = false;
55
static bool arg_wait = false;
56
static const char *arg_unit = NULL;
57
static char *arg_description = NULL;
58
static const char *arg_slice = NULL;
59
static bool arg_slice_inherit = false;
60
static bool arg_expand_environment = true;
61
static bool arg_send_sighup = false;
62
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
63
static const char *arg_host = NULL;
64
static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
65
static const char *arg_service_type = NULL;
66
static const char *arg_exec_user = NULL;
67
static const char *arg_exec_group = NULL;
68
static int arg_nice = 0;
69
static bool arg_nice_set = false;
70
static char **arg_environment = NULL;
71
static char **arg_property = NULL;
72
static enum {
73
        ARG_STDIO_NONE,      /* The default, as it is for normal services, stdin connected to /dev/null, and stdout+stderr to the journal */
74
        ARG_STDIO_PTY,       /* Interactive behaviour, requested by --pty: we allocate a pty and connect it to the TTY we are invoked from */
75
        ARG_STDIO_DIRECT,    /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */
76
        ARG_STDIO_AUTO,      /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */
77
} arg_stdio = ARG_STDIO_NONE;
78
static char **arg_path_property = NULL;
79
static char **arg_socket_property = NULL;
80
static char **arg_timer_property = NULL;
81
static bool arg_with_timer = false;
82
static bool arg_quiet = false;
83
static bool arg_aggressive_gc = false;
84
static char *arg_working_directory = NULL;
85
static bool arg_shell = false;
86
static JobMode arg_job_mode = JOB_FAIL;
87
static char **arg_cmdline = NULL;
88
static char *arg_exec_path = NULL;
89
static bool arg_ignore_failure = false;
90
static char *arg_background = NULL;
91
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
92
static char *arg_shell_prompt_prefix = NULL;
93
static int arg_lightweight = -1;
94
static char *arg_area = NULL;
95

96
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
1,088✔
97
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
1,088✔
98
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
1,088✔
99
STATIC_DESTRUCTOR_REGISTER(arg_path_property, strv_freep);
1,088✔
100
STATIC_DESTRUCTOR_REGISTER(arg_socket_property, strv_freep);
1,088✔
101
STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
1,088✔
102
STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
1,088✔
103
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
1,088✔
104
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
1,088✔
105
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
1,088✔
106
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
1,088✔
107
STATIC_DESTRUCTOR_REGISTER(arg_area, freep);
1,088✔
108

109
static int help(void) {
1✔
110
        _cleanup_free_ char *link = NULL;
1✔
111
        int r;
1✔
112

113
        r = terminal_urlify_man("systemd-run", "1", &link);
1✔
114
        if (r < 0)
1✔
UNCOV
115
                return log_oom();
×
116

117
        printf("%1$s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
2✔
118
               "\n%5$sRun the specified command in a transient scope or service.%6$s\n\n"
119
               "  -h --help                       Show this help\n"
120
               "     --version                    Show package version\n"
121
               "     --no-ask-password            Do not prompt for password\n"
122
               "     --user                       Run as user unit\n"
123
               "  -H --host=[USER@]HOST           Operate on remote host\n"
124
               "  -M --machine=CONTAINER          Operate on local container\n"
125
               "     --scope                      Run this as scope rather than service\n"
126
               "  -u --unit=UNIT                  Run under the specified unit name\n"
127
               "  -p --property=NAME=VALUE        Set service or scope unit property\n"
128
               "     --description=TEXT           Description for unit\n"
129
               "     --slice=SLICE                Run in the specified slice\n"
130
               "     --slice-inherit              Inherit the slice from the caller\n"
131
               "     --expand-environment=BOOL    Control expansion of environment variables\n"
132
               "     --no-block                   Do not wait until operation finished\n"
133
               "  -r --remain-after-exit          Leave service around until explicitly stopped\n"
134
               "     --wait                       Wait until service stopped again\n"
135
               "     --send-sighup                Send SIGHUP when terminating\n"
136
               "     --service-type=TYPE          Service type\n"
137
               "     --uid=USER                   Run as system user\n"
138
               "     --gid=GROUP                  Run as system group\n"
139
               "     --nice=NICE                  Nice level\n"
140
               "     --working-directory=PATH     Set working directory\n"
141
               "  -d --same-dir                   Inherit working directory from caller\n"
142
               "  -E --setenv=NAME[=VALUE]        Set environment variable\n"
143
               "  -t --pty                        Run service on pseudo TTY as STDIN/STDOUT/\n"
144
               "                                  STDERR\n"
145
               "  -P --pipe                       Pass STDIN/STDOUT/STDERR directly to service\n"
146
               "  -q --quiet                      Suppress information messages during runtime\n"
147
               "     --json=pretty|short|off      Print unit name and invocation id as JSON\n"
148
               "  -G --collect                    Unload unit after it ran, even when failed\n"
149
               "  -S --shell                      Invoke a $SHELL interactively\n"
150
               "     --job-mode=MODE              Specify how to deal with already queued jobs,\n"
151
               "                                  when queueing a new job\n"
152
               "     --ignore-failure             Ignore the exit status of the invoked process\n"
153
               "     --background=COLOR           Set ANSI color for background\n"
154
               "\n%3$sPath options:%4$s\n"
155
               "     --path-property=NAME=VALUE   Set path unit property\n"
156
               "\n%3$sSocket options:%4$s\n"
157
               "     --socket-property=NAME=VALUE Set socket unit property\n"
158
               "\n%3$sTimer options:%4$s\n"
159
               "     --on-active=SECONDS          Run after SECONDS delay\n"
160
               "     --on-boot=SECONDS            Run SECONDS after machine was booted up\n"
161
               "     --on-startup=SECONDS         Run SECONDS after systemd activation\n"
162
               "     --on-unit-active=SECONDS     Run SECONDS after the last activation\n"
163
               "     --on-unit-inactive=SECONDS   Run SECONDS after the last deactivation\n"
164
               "     --on-calendar=SPEC           Realtime timer\n"
165
               "     --on-timezone-change         Run when the timezone changes\n"
166
               "     --on-clock-change            Run when the realtime clock jumps\n"
167
               "     --timer-property=NAME=VALUE  Set timer unit property\n"
168
               "\nSee the %2$s for details.\n",
169
               program_invocation_short_name,
170
               link,
171
               ansi_underline(), ansi_normal(),
172
               ansi_highlight(), ansi_normal());
173

174
        return 0;
175
}
176

177
static int help_sudo_mode(void) {
×
UNCOV
178
        _cleanup_free_ char *link = NULL;
×
179
        int r;
×
180

181
        r = terminal_urlify_man("run0", "1", &link);
×
UNCOV
182
        if (r < 0)
×
UNCOV
183
                return log_oom();
×
184

185
        /* NB: Let's not go overboard with short options: we try to keep a modicum of compatibility with
186
         * sudo's short switches, hence please do not introduce new short switches unless they have a roughly
187
         * equivalent purpose on sudo. Use long options for everything private to run0. */
188

UNCOV
189
        printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
×
190
               "\n%sElevate privileges interactively.%s\n\n"
191
               "  -h --help                       Show this help\n"
192
               "  -V --version                    Show package version\n"
193
               "     --no-ask-password            Do not prompt for password\n"
194
               "     --machine=CONTAINER          Operate on local container\n"
195
               "     --unit=UNIT                  Run under the specified unit name\n"
196
               "     --property=NAME=VALUE        Set service or scope unit property\n"
197
               "     --description=TEXT           Description for unit\n"
198
               "     --slice=SLICE                Run in the specified slice\n"
199
               "     --slice-inherit              Inherit the slice\n"
200
               "  -u --user=USER                  Run as system user\n"
201
               "  -g --group=GROUP                Run as system group\n"
202
               "     --nice=NICE                  Nice level\n"
203
               "  -D --chdir=PATH                 Set working directory\n"
204
               "     --setenv=NAME[=VALUE]        Set environment variable\n"
205
               "     --background=COLOR           Set ANSI color for background\n"
206
               "     --pty                        Request allocation of a pseudo TTY for stdio\n"
207
               "     --pipe                       Request direct pipe for stdio\n"
208
               "     --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
209
               "     --lightweight=BOOLEAN        Control whether to register a session with service manager\n"
210
               "                                  or without\n"
211
               "  -a --area=AREA                  Home area to log into\n"
212
               "\nSee the %s for details.\n",
213
               program_invocation_short_name,
214
               ansi_highlight(),
215
               ansi_normal(),
216
               link);
217

218
        return 0;
219
}
220

221
static bool privileged_execution(void) {
97✔
222
        if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
97✔
223
                return false;
97✔
224

225
        return !arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0");
106✔
226
}
227

228
static int add_timer_property(const char *name, const char *val) {
23✔
229
        char *p;
23✔
230

231
        assert(name);
23✔
232
        assert(val);
23✔
233

234
        p = strjoin(name, "=", val);
23✔
235
        if (!p)
23✔
236
                return log_oom();
×
237

238
        if (strv_consume(&arg_timer_property, p) < 0)
23✔
UNCOV
239
                return log_oom();
×
240

241
        return 0;
242
}
243

244
static char **make_login_shell_cmdline(const char *shell) {
×
UNCOV
245
        _cleanup_free_ char *argv0 = NULL;
×
246

247
        assert(shell);
×
248

UNCOV
249
        argv0 = strjoin("-", shell); /* The - is how shells determine if they shall be consider login shells */
×
250
        if (!argv0)
×
251
                return NULL;
252

UNCOV
253
        return strv_new(argv0);
×
254
}
255

256
static int parse_argv(int argc, char *argv[]) {
993✔
257

258
        enum {
993✔
259
                ARG_VERSION = 0x100,
260
                ARG_USER,
261
                ARG_SYSTEM,
262
                ARG_SCOPE,
263
                ARG_DESCRIPTION,
264
                ARG_SLICE,
265
                ARG_SLICE_INHERIT,
266
                ARG_EXPAND_ENVIRONMENT,
267
                ARG_SEND_SIGHUP,
268
                ARG_SERVICE_TYPE,
269
                ARG_EXEC_USER,
270
                ARG_EXEC_GROUP,
271
                ARG_NICE,
272
                ARG_ON_ACTIVE,
273
                ARG_ON_BOOT,
274
                ARG_ON_STARTUP,
275
                ARG_ON_UNIT_ACTIVE,
276
                ARG_ON_UNIT_INACTIVE,
277
                ARG_ON_CALENDAR,
278
                ARG_ON_TIMEZONE_CHANGE,
279
                ARG_ON_CLOCK_CHANGE,
280
                ARG_TIMER_PROPERTY,
281
                ARG_PATH_PROPERTY,
282
                ARG_SOCKET_PROPERTY,
283
                ARG_NO_BLOCK,
284
                ARG_NO_ASK_PASSWORD,
285
                ARG_WAIT,
286
                ARG_WORKING_DIRECTORY,
287
                ARG_SHELL,
288
                ARG_JOB_MODE,
289
                ARG_IGNORE_FAILURE,
290
                ARG_BACKGROUND,
291
                ARG_JSON,
292
        };
293

294
        static const struct option options[] = {
993✔
295
                { "help",               no_argument,       NULL, 'h'                    },
296
                { "version",            no_argument,       NULL, ARG_VERSION            },
297
                { "user",               no_argument,       NULL, ARG_USER               },
298
                { "system",             no_argument,       NULL, ARG_SYSTEM             },
299
                { "capsule",            required_argument, NULL, 'C'                    },
300
                { "scope",              no_argument,       NULL, ARG_SCOPE              },
301
                { "unit",               required_argument, NULL, 'u'                    },
302
                { "description",        required_argument, NULL, ARG_DESCRIPTION        },
303
                { "slice",              required_argument, NULL, ARG_SLICE              },
304
                { "slice-inherit",      no_argument,       NULL, ARG_SLICE_INHERIT      },
305
                { "remain-after-exit",  no_argument,       NULL, 'r'                    },
306
                { "expand-environment", required_argument, NULL, ARG_EXPAND_ENVIRONMENT },
307
                { "send-sighup",        no_argument,       NULL, ARG_SEND_SIGHUP        },
308
                { "host",               required_argument, NULL, 'H'                    },
309
                { "machine",            required_argument, NULL, 'M'                    },
310
                { "service-type",       required_argument, NULL, ARG_SERVICE_TYPE       },
311
                { "wait",               no_argument,       NULL, ARG_WAIT               },
312
                { "uid",                required_argument, NULL, ARG_EXEC_USER          },
313
                { "gid",                required_argument, NULL, ARG_EXEC_GROUP         },
314
                { "nice",               required_argument, NULL, ARG_NICE               },
315
                { "setenv",             required_argument, NULL, 'E'                    },
316
                { "property",           required_argument, NULL, 'p'                    },
317
                { "tty",                no_argument,       NULL, 't'                    }, /* deprecated alias */
318
                { "pty",                no_argument,       NULL, 't'                    },
319
                { "pipe",               no_argument,       NULL, 'P'                    },
320
                { "quiet",              no_argument,       NULL, 'q'                    },
321
                { "on-active",          required_argument, NULL, ARG_ON_ACTIVE          },
322
                { "on-boot",            required_argument, NULL, ARG_ON_BOOT            },
323
                { "on-startup",         required_argument, NULL, ARG_ON_STARTUP         },
324
                { "on-unit-active",     required_argument, NULL, ARG_ON_UNIT_ACTIVE     },
325
                { "on-unit-inactive",   required_argument, NULL, ARG_ON_UNIT_INACTIVE   },
326
                { "on-calendar",        required_argument, NULL, ARG_ON_CALENDAR        },
327
                { "on-timezone-change", no_argument,       NULL, ARG_ON_TIMEZONE_CHANGE },
328
                { "on-clock-change",    no_argument,       NULL, ARG_ON_CLOCK_CHANGE    },
329
                { "timer-property",     required_argument, NULL, ARG_TIMER_PROPERTY     },
330
                { "path-property",      required_argument, NULL, ARG_PATH_PROPERTY      },
331
                { "socket-property",    required_argument, NULL, ARG_SOCKET_PROPERTY    },
332
                { "no-block",           no_argument,       NULL, ARG_NO_BLOCK           },
333
                { "no-ask-password",    no_argument,       NULL, ARG_NO_ASK_PASSWORD    },
334
                { "collect",            no_argument,       NULL, 'G'                    },
335
                { "working-directory",  required_argument, NULL, ARG_WORKING_DIRECTORY  },
336
                { "same-dir",           no_argument,       NULL, 'd'                    },
337
                { "shell",              no_argument,       NULL, 'S'                    },
338
                { "job-mode",           required_argument, NULL, ARG_JOB_MODE           },
339
                { "ignore-failure",     no_argument,       NULL, ARG_IGNORE_FAILURE     },
340
                { "background",         required_argument, NULL, ARG_BACKGROUND         },
341
                { "json",               required_argument, NULL, ARG_JSON               },
342
                {},
343
        };
344

345
        bool with_trigger = false;
993✔
346
        int r, c;
993✔
347

348
        assert(argc >= 0);
993✔
349
        assert(argv);
993✔
350

351
        /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
352
         * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
353
        optind = 0;
993✔
354
        while ((c = getopt_long(argc, argv, "+hrC:H:M:E:p:tPqGdSu:", options, NULL)) >= 0)
5,369✔
355

356
                switch (c) {
4,387✔
357

358
                case 'h':
1✔
359
                        return help();
11✔
360

361
                case ARG_VERSION:
1✔
362
                        return version();
1✔
363

364
                case ARG_NO_ASK_PASSWORD:
1✔
365
                        arg_ask_password = false;
1✔
366
                        break;
4,376✔
367

368
                case ARG_USER:
61✔
369
                        arg_runtime_scope = RUNTIME_SCOPE_USER;
61✔
370
                        break;
61✔
371

372
                case ARG_SYSTEM:
2✔
373
                        arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
2✔
374
                        break;
2✔
375

376
                case 'C':
1✔
377
                        r = capsule_name_is_valid(optarg);
1✔
378
                        if (r < 0)
1✔
UNCOV
379
                                return log_error_errno(r, "Unable to validate capsule name '%s': %m", optarg);
×
380
                        if (r == 0)
1✔
UNCOV
381
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capsule name: %s", optarg);
×
382

383
                        arg_host = optarg;
1✔
384
                        arg_transport = BUS_TRANSPORT_CAPSULE;
1✔
385
                        arg_runtime_scope = RUNTIME_SCOPE_USER;
1✔
386
                        break;
1✔
387

388
                case ARG_SCOPE:
19✔
389
                        arg_scope = true;
19✔
390
                        break;
19✔
391

392
                case 'u':
519✔
393
                        arg_unit = optarg;
519✔
394
                        break;
519✔
395

396
                case ARG_DESCRIPTION:
2✔
397
                        r = free_and_strdup_warn(&arg_description, optarg);
2✔
398
                        if (r < 0)
2✔
399
                                return r;
400
                        break;
401

402
                case ARG_SLICE:
30✔
403
                        arg_slice = optarg;
30✔
404
                        break;
30✔
405

406
                case ARG_SLICE_INHERIT:
4✔
407
                        arg_slice_inherit = true;
4✔
408
                        break;
4✔
409

410
                case ARG_EXPAND_ENVIRONMENT:
1✔
411
                        r = parse_boolean_argument("--expand-environment=", optarg, &arg_expand_environment);
1✔
412
                        if (r < 0)
1✔
413
                                return r;
414
                        break;
415

416
                case ARG_SEND_SIGHUP:
1✔
417
                        arg_send_sighup = true;
1✔
418
                        break;
1✔
419

420
                case 'r':
26✔
421
                        arg_remain_after_exit = true;
26✔
422
                        break;
26✔
423

424
                case 'H':
×
UNCOV
425
                        arg_transport = BUS_TRANSPORT_REMOTE;
×
UNCOV
426
                        arg_host = optarg;
×
UNCOV
427
                        break;
×
428

429
                case 'M':
36✔
430
                        arg_transport = BUS_TRANSPORT_MACHINE;
36✔
431
                        arg_host = optarg;
36✔
432
                        break;
36✔
433

434
                case ARG_SERVICE_TYPE:
23✔
435
                        arg_service_type = optarg;
23✔
436
                        break;
23✔
437

438
                case ARG_EXEC_USER:
11✔
439
                        arg_exec_user = optarg;
11✔
440
                        break;
11✔
441

442
                case ARG_EXEC_GROUP:
2✔
443
                        arg_exec_group = optarg;
2✔
444
                        break;
2✔
445

446
                case ARG_NICE:
3✔
447
                        r = parse_nice(optarg, &arg_nice);
3✔
448
                        if (r < 0)
3✔
449
                                return log_error_errno(r, "Failed to parse nice value: %s", optarg);
2✔
450

451
                        arg_nice_set = true;
1✔
452
                        break;
1✔
453

454
                case 'E':
8✔
455
                        r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
8✔
456
                        if (r < 0)
8✔
457
                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
2✔
458

459
                        break;
460

461
                case 'p':
2,259✔
462
                        if (strv_extend(&arg_property, optarg) < 0)
2,259✔
UNCOV
463
                                return log_oom();
×
464

465
                        break;
466

467
                case 't': /* --pty */
8✔
468
                        if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
8✔
UNCOV
469
                                arg_stdio = ARG_STDIO_AUTO;
×
470
                        else
471
                                arg_stdio = ARG_STDIO_PTY;
8✔
472
                        break;
473

474
                case 'P': /* --pipe */
226✔
475
                        if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
226✔
UNCOV
476
                                arg_stdio = ARG_STDIO_AUTO;
×
477
                        else
478
                                arg_stdio = ARG_STDIO_DIRECT;
226✔
479
                        break;
480

481
                case 'q':
339✔
482
                        arg_quiet = true;
339✔
483
                        break;
339✔
484

485
                case ARG_ON_ACTIVE:
4✔
486
                        r = add_timer_property("OnActiveSec", optarg);
4✔
487
                        if (r < 0)
4✔
488
                                return r;
489

490
                        arg_with_timer = true;
4✔
491
                        break;
4✔
492

493
                case ARG_ON_BOOT:
3✔
494
                        r = add_timer_property("OnBootSec", optarg);
3✔
495
                        if (r < 0)
3✔
496
                                return r;
497

498
                        arg_with_timer = true;
3✔
499
                        break;
3✔
500

501
                case ARG_ON_STARTUP:
3✔
502
                        r = add_timer_property("OnStartupSec", optarg);
3✔
503
                        if (r < 0)
3✔
504
                                return r;
505

506
                        arg_with_timer = true;
3✔
507
                        break;
3✔
508

509
                case ARG_ON_UNIT_ACTIVE:
3✔
510
                        r = add_timer_property("OnUnitActiveSec", optarg);
3✔
511
                        if (r < 0)
3✔
512
                                return r;
513

514
                        arg_with_timer = true;
3✔
515
                        break;
3✔
516

517
                case ARG_ON_UNIT_INACTIVE:
3✔
518
                        r = add_timer_property("OnUnitInactiveSec", optarg);
3✔
519
                        if (r < 0)
3✔
520
                                return r;
521

522
                        arg_with_timer = true;
3✔
523
                        break;
3✔
524

525
                case ARG_ON_CALENDAR: {
4✔
526
                        _cleanup_(calendar_spec_freep) CalendarSpec *cs = NULL;
2✔
527

528
                        r = calendar_spec_from_string(optarg, &cs);
4✔
529
                        if (r < 0)
4✔
530
                                return log_error_errno(r, "Failed to parse calendar event specification: %m");
2✔
531

532
                        /* Let's make sure the given calendar event is not in the past */
533
                        r = calendar_spec_next_usec(cs, now(CLOCK_REALTIME), NULL);
2✔
534
                        if (r == -ENOENT)
2✔
535
                                /* The calendar event is in the past — let's warn about this, but install it
536
                                 * anyway as is. The service manager will trigger the service right away.
537
                                 * Moreover, the server side might have a different clock or timezone than we
538
                                 * do, hence it should decide when or whether to run something. */
UNCOV
539
                                log_warning("Specified calendar expression is in the past, proceeding anyway.");
×
540
                        else if (r < 0)
2✔
UNCOV
541
                                return log_error_errno(r, "Failed to calculate next time calendar expression elapses: %m");
×
542

543
                        r = add_timer_property("OnCalendar", optarg);
2✔
544
                        if (r < 0)
2✔
545
                                return r;
546

547
                        arg_with_timer = true;
2✔
548
                        break;
2✔
549
                }
550

551
                case ARG_ON_TIMEZONE_CHANGE:
2✔
552
                        r = add_timer_property("OnTimezoneChange", "yes");
2✔
553
                        if (r < 0)
2✔
554
                                return r;
555

556
                        arg_with_timer = true;
2✔
557
                        break;
2✔
558

559
                case ARG_ON_CLOCK_CHANGE:
3✔
560
                        r = add_timer_property("OnClockChange", "yes");
3✔
561
                        if (r < 0)
3✔
562
                                return r;
563

564
                        arg_with_timer = true;
3✔
565
                        break;
3✔
566

567
                case ARG_TIMER_PROPERTY:
2✔
568

569
                        if (strv_extend(&arg_timer_property, optarg) < 0)
2✔
UNCOV
570
                                return log_oom();
×
571

572
                        arg_with_timer = arg_with_timer ||
3✔
573
                                STARTSWITH_SET(optarg,
1✔
574
                                               "OnActiveSec=",
575
                                               "OnBootSec=",
576
                                               "OnStartupSec=",
577
                                               "OnUnitActiveSec=",
578
                                               "OnUnitInactiveSec=",
579
                                               "OnCalendar=");
580
                        break;
2✔
581

582
                case ARG_PATH_PROPERTY:
3✔
583

584
                        if (strv_extend(&arg_path_property, optarg) < 0)
3✔
UNCOV
585
                                return log_oom();
×
586

587
                        break;
588

589
                case ARG_SOCKET_PROPERTY:
4✔
590

591
                        if (strv_extend(&arg_socket_property, optarg) < 0)
4✔
UNCOV
592
                                return log_oom();
×
593

594
                        break;
595

596
                case ARG_NO_BLOCK:
4✔
597
                        arg_no_block = true;
4✔
598
                        break;
4✔
599

600
                case ARG_WAIT:
750✔
601
                        arg_wait = true;
750✔
602
                        break;
750✔
603

604
                case ARG_WORKING_DIRECTORY:
2✔
605
                        r = parse_path_argument(optarg, true, &arg_working_directory);
2✔
606
                        if (r < 0)
2✔
607
                                return r;
608

609
                        break;
610

611
                case 'd': {
1✔
612
                        _cleanup_free_ char *p = NULL;
1✔
613

614
                        r = safe_getcwd(&p);
1✔
615
                        if (r < 0)
1✔
UNCOV
616
                                return log_error_errno(r, "Failed to get current working directory: %m");
×
617

618
                        if (empty_or_root(p))
1✔
619
                                arg_working_directory = mfree(arg_working_directory);
1✔
620
                        else
UNCOV
621
                                free_and_replace(arg_working_directory, p);
×
622
                        break;
1✔
623
                }
624

625
                case 'G':
2✔
626
                        arg_aggressive_gc = true;
2✔
627
                        break;
2✔
628

629
                case 'S':
3✔
630
                        arg_shell = true;
3✔
631
                        break;
3✔
632

633
                case ARG_JOB_MODE:
4✔
634
                        if (streq(optarg, "help"))
4✔
635
                                return DUMP_STRING_TABLE(job_mode, JobMode, _JOB_MODE_MAX);
10✔
636

637
                        r = job_mode_from_string(optarg);
3✔
638
                        if (r < 0)
3✔
639
                                return log_error_errno(r, "Invalid job mode: %s", optarg);
1✔
640

641
                        arg_job_mode = r;
2✔
642
                        break;
2✔
643

UNCOV
644
                case ARG_IGNORE_FAILURE:
×
645
                        arg_ignore_failure = true;
×
646
                        break;
×
647

UNCOV
648
                case ARG_BACKGROUND:
×
UNCOV
649
                        r = free_and_strdup_warn(&arg_background, optarg);
×
UNCOV
650
                        if (r < 0)
×
651
                                return r;
652
                        break;
653

654
                case ARG_JSON:
2✔
655
                        r = parse_json_argument(optarg, &arg_json_format_flags);
2✔
656
                        if (r <= 0)
2✔
657
                                return r;
658
                        break;
659

660
                case '?':
661
                        return -EINVAL;
662

UNCOV
663
                default:
×
UNCOV
664
                        assert_not_reached();
×
665
                }
666

667
        /* If we are talking to the per-user instance PolicyKit isn't going to help */
668
        if (arg_runtime_scope == RUNTIME_SCOPE_USER)
982✔
669
                arg_ask_password = false;
62✔
670

671
        with_trigger = !!arg_path_property || !!arg_socket_property || arg_with_timer;
982✔
672

673
        /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
674
        if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) arg_with_timer > 1)
982✔
UNCOV
675
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
676
                                       "Only single trigger (path, socket, timer) unit can be created.");
677

678
        if (arg_shell) {
982✔
679
                /* If --shell is imply --pty --pipe --same-dir --service-type=exec --wait --collect, unless otherwise
680
                 * specified. */
681

682
                if (!arg_scope) {
3✔
683
                        if (arg_stdio == ARG_STDIO_NONE)
2✔
684
                                arg_stdio = ARG_STDIO_AUTO;
2✔
685

686
                        if (!arg_working_directory) {
2✔
687
                                r = safe_getcwd(&arg_working_directory);
2✔
688
                                if (r < 0)
2✔
UNCOV
689
                                        return log_error_errno(r, "Failed to get current working directory: %m");
×
690
                        }
691

692
                        if (!arg_service_type)
2✔
693
                                arg_service_type = "exec";
2✔
694

695
                        arg_wait = true;
2✔
696
                }
697

698
                arg_aggressive_gc = true;
3✔
699
        }
700

701
        if (arg_stdio == ARG_STDIO_AUTO)
982✔
702
                /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
703
                 * to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
704
                 * pipeline, but we are neatly interactive with tty-level isolation otherwise. */
705
                arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ?
4✔
706
                        ARG_STDIO_PTY :
2✔
707
                        ARG_STDIO_DIRECT;
708

709
        if (argc > optind) {
982✔
710
                char **l;
978✔
711

712
                if (arg_shell)
978✔
713
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "If --shell is used, no command line is expected.");
993✔
714

715
                l = strv_copy(argv + optind);
978✔
716
                if (!l)
978✔
UNCOV
717
                        return log_oom();
×
718

719
                strv_free_and_replace(arg_cmdline, l);
978✔
720

721
        } else if (arg_shell) {
4✔
722
                _cleanup_free_ char *s = NULL;
3✔
723
                char **l;
3✔
724

725
                r = get_shell(&s);
3✔
726
                if (r < 0)
3✔
UNCOV
727
                        return log_error_errno(r, "Failed to determine shell: %m");
×
728

729
                l = strv_new(s);
3✔
730
                if (!l)
3✔
UNCOV
731
                        return log_oom();
×
732

733
                strv_free_and_replace(arg_cmdline, l);
3✔
734

735
        } else if (!arg_unit || !with_trigger)
1✔
736
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Command line to execute required.");
1✔
737

738
        if (arg_runtime_scope == RUNTIME_SCOPE_USER && arg_transport == BUS_TRANSPORT_REMOTE)
981✔
UNCOV
739
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
740
                                       "Execution in user context is not supported on remote systems.");
741

742
        if (arg_scope && arg_transport == BUS_TRANSPORT_REMOTE)
981✔
UNCOV
743
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
744
                                       "Scope execution is not supported on remote systems.");
745

746
        if (arg_scope && (arg_remain_after_exit || arg_service_type))
981✔
UNCOV
747
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
748
                                       "--remain-after-exit and --service-type= are not supported in --scope mode.");
749

750
        if (arg_stdio != ARG_STDIO_NONE && (with_trigger || arg_scope))
981✔
UNCOV
751
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
752
                                       "--pty/--pipe is not compatible in timer or --scope mode.");
753

754
        if (arg_stdio != ARG_STDIO_NONE && arg_transport == BUS_TRANSPORT_REMOTE)
981✔
UNCOV
755
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
756
                                       "--pty/--pipe is only supported when connecting to the local system or containers.");
757

758
        if (arg_stdio != ARG_STDIO_NONE && arg_no_block)
981✔
UNCOV
759
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
760
                                       "--pty/--pipe is not compatible with --no-block.");
761

762
        if (arg_scope && with_trigger)
981✔
UNCOV
763
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
764
                                       "Path, socket or timer options are not supported in --scope mode.");
765

766
        if (arg_timer_property && !arg_with_timer)
981✔
UNCOV
767
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
768
                                       "--timer-property= has no effect without any other timer options.");
769

770
        if (arg_wait) {
981✔
771
                if (arg_no_block)
752✔
UNCOV
772
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
773
                                               "--wait may not be combined with --no-block.");
774

775
                if (with_trigger)
752✔
UNCOV
776
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
777
                                               "--wait may not be combined with path, socket or timer operations.");
778

779
                if (arg_scope)
752✔
UNCOV
780
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
781
                                               "--wait may not be combined with --scope.");
782
        }
783

784
        return 1;
785
}
786

787
static int parse_argv_sudo_mode(int argc, char *argv[]) {
112✔
788

789
        enum {
112✔
790
                ARG_NO_ASK_PASSWORD = 0x100,
791
                ARG_HOST,
792
                ARG_MACHINE,
793
                ARG_UNIT,
794
                ARG_PROPERTY,
795
                ARG_DESCRIPTION,
796
                ARG_SLICE,
797
                ARG_SLICE_INHERIT,
798
                ARG_NICE,
799
                ARG_SETENV,
800
                ARG_BACKGROUND,
801
                ARG_PTY,
802
                ARG_PIPE,
803
                ARG_SHELL_PROMPT_PREFIX,
804
                ARG_LIGHTWEIGHT,
805
        };
806

807
        /* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
808
         * though (but limit the extension to long options). */
809

810
        static const struct option options[] = {
112✔
811
                { "help",                no_argument,       NULL, 'h'                     },
812
                { "version",             no_argument,       NULL, 'V'                     },
813
                { "no-ask-password",     no_argument,       NULL, ARG_NO_ASK_PASSWORD     },
814
                { "machine",             required_argument, NULL, ARG_MACHINE             },
815
                { "unit",                required_argument, NULL, ARG_UNIT                },
816
                { "property",            required_argument, NULL, ARG_PROPERTY            },
817
                { "description",         required_argument, NULL, ARG_DESCRIPTION         },
818
                { "slice",               required_argument, NULL, ARG_SLICE               },
819
                { "slice-inherit",       no_argument,       NULL, ARG_SLICE_INHERIT       },
820
                { "user",                required_argument, NULL, 'u'                     },
821
                { "group",               required_argument, NULL, 'g'                     },
822
                { "nice",                required_argument, NULL, ARG_NICE                },
823
                { "chdir",               required_argument, NULL, 'D'                     },
824
                { "setenv",              required_argument, NULL, ARG_SETENV              },
825
                { "background",          required_argument, NULL, ARG_BACKGROUND          },
826
                { "pty",                 no_argument,       NULL, ARG_PTY                 },
827
                { "pipe",                no_argument,       NULL, ARG_PIPE                },
828
                { "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
829
                { "lightweight",         required_argument, NULL, ARG_LIGHTWEIGHT         },
830
                { "area",                required_argument, NULL, 'a'                     },
831
                {},
832
        };
833

834
        int r, c;
112✔
835

836
        assert(argc >= 0);
112✔
837
        assert(argv);
112✔
838

839
        /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
840
         * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
841
        optind = 0;
112✔
842
        while ((c = getopt_long(argc, argv, "+hVu:g:D:a:", options, NULL)) >= 0)
229✔
843

844
                switch (c) {
117✔
845

846
                case 'h':
×
847
                        return help_sudo_mode();
112✔
848

849
                case 'V':
×
850
                        return version();
×
851

852
                case ARG_NO_ASK_PASSWORD:
×
853
                        arg_ask_password = false;
×
854
                        break;
×
855

UNCOV
856
                case ARG_MACHINE:
×
UNCOV
857
                        arg_transport = BUS_TRANSPORT_MACHINE;
×
UNCOV
858
                        arg_host = optarg;
×
UNCOV
859
                        break;
×
860

861
                case ARG_UNIT:
4✔
862
                        arg_unit = optarg;
4✔
863
                        break;
4✔
864

865
                case ARG_PROPERTY:
26✔
866
                        if (strv_extend(&arg_property, optarg) < 0)
26✔
867
                                return log_oom();
×
868

869
                        break;
870

UNCOV
871
                case ARG_DESCRIPTION:
×
UNCOV
872
                        r = free_and_strdup_warn(&arg_description, optarg);
×
873
                        if (r < 0)
×
874
                                return r;
875
                        break;
876

877
                case ARG_SLICE:
×
878
                        arg_slice = optarg;
×
879
                        break;
×
880

UNCOV
881
                case ARG_SLICE_INHERIT:
×
UNCOV
882
                        arg_slice_inherit = true;
×
UNCOV
883
                        break;
×
884

885
                case 'u':
53✔
886
                        arg_exec_user = optarg;
53✔
887
                        break;
53✔
888

889
                case 'g':
×
890
                        arg_exec_group = optarg;
×
891
                        break;
×
892

UNCOV
893
                case ARG_NICE:
×
894
                        r = parse_nice(optarg, &arg_nice);
×
895
                        if (r < 0)
×
UNCOV
896
                                return log_error_errno(r, "Failed to parse nice value: %s", optarg);
×
897

UNCOV
898
                        arg_nice_set = true;
×
UNCOV
899
                        break;
×
900

901
                case 'D':
4✔
902
                        /* Root will be manually suppressed later. */
903
                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_working_directory);
4✔
904
                        if (r < 0)
4✔
905
                                return r;
906

907
                        break;
908

UNCOV
909
                case ARG_SETENV:
×
UNCOV
910
                        r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
×
UNCOV
911
                        if (r < 0)
×
912
                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
×
913

914
                        break;
915

UNCOV
916
                case ARG_BACKGROUND:
×
UNCOV
917
                        r = free_and_strdup_warn(&arg_background, optarg);
×
UNCOV
918
                        if (r < 0)
×
919
                                return r;
920

921
                        break;
922

923
                case ARG_PTY:
3✔
924
                        if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
3✔
925
                                arg_stdio = ARG_STDIO_AUTO;
1✔
926
                        else
927
                                arg_stdio = ARG_STDIO_PTY;
2✔
928
                        break;
929

930
                case ARG_PIPE:
8✔
931
                        if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
8✔
UNCOV
932
                                arg_stdio = ARG_STDIO_AUTO;
×
933
                        else
934
                                arg_stdio = ARG_STDIO_DIRECT;
8✔
935
                        break;
936

UNCOV
937
                case ARG_SHELL_PROMPT_PREFIX:
×
UNCOV
938
                        r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg);
×
UNCOV
939
                        if (r < 0)
×
940
                                return r;
941
                        break;
942

943
                case ARG_LIGHTWEIGHT:
2✔
944
                        r = parse_tristate_argument("--lightweight=", optarg, &arg_lightweight);
2✔
945
                        if (r < 0)
2✔
946
                                return r;
947
                        break;
948

949
                case 'a':
17✔
950
                        /* We allow an empty --area= specification to allow logging into the primary home directory */
951
                        if (!isempty(optarg) && !filename_is_valid(optarg))
34✔
UNCOV
952
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid area name, refusing: %s", optarg);
×
953

954
                        r = free_and_strdup_warn(&arg_area, optarg);
17✔
955
                        if (r < 0)
17✔
956
                                return r;
957

958
                        break;
959

960
                case '?':
961
                        return -EINVAL;
962

UNCOV
963
                default:
×
UNCOV
964
                        assert_not_reached();
×
965
                }
966

967
        if (!arg_exec_user && arg_area) {
112✔
968
                /* If the user specifies --area= but not --user= then consider this an area switch request,
969
                 * and default to logging into our own account */
UNCOV
970
                arg_exec_user = getusername_malloc();
×
UNCOV
971
                if (!arg_exec_user)
×
UNCOV
972
                        return log_oom();
×
973
        }
974

975
        if (!arg_working_directory) {
112✔
976
                if (arg_exec_user) {
108✔
977
                        /* When switching to a specific user, also switch to its home directory. */
978
                        arg_working_directory = strdup("~");
51✔
979
                        if (!arg_working_directory)
51✔
UNCOV
980
                                return log_oom();
×
981
                } else {
982
                        /* When switching to root without this being specified, then stay in the current directory */
983
                        r = safe_getcwd(&arg_working_directory);
57✔
984
                        if (r < 0)
57✔
UNCOV
985
                                return log_error_errno(r, "Failed to get current working directory: %m");
×
986
                }
987
        } else {
988
                /* Root was not suppressed earlier, to allow the above check to work properly. */
989
                if (empty_or_root(arg_working_directory))
4✔
990
                        arg_working_directory = mfree(arg_working_directory);
2✔
991
        }
992

993
        arg_service_type = "exec";
112✔
994
        arg_quiet = true;
112✔
995
        arg_wait = true;
112✔
996
        arg_aggressive_gc = true;
112✔
997

998
        if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO))
112✔
999
                arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
103✔
1000

1001
        log_debug("Using %s stdio mode.", arg_stdio == ARG_STDIO_PTY ? "pty" : "direct");
166✔
1002

1003
        arg_expand_environment = false;
112✔
1004
        arg_send_sighup = true;
112✔
1005

1006
        _cleanup_strv_free_ char **l = NULL;
112✔
1007
        if (argc > optind)
112✔
1008
                l = strv_copy(argv + optind);
112✔
1009
        else {
UNCOV
1010
                const char *e;
×
1011

UNCOV
1012
                e = strv_env_get(arg_environment, "SHELL");
×
1013
                if (e)
×
UNCOV
1014
                        arg_exec_path = strdup(e);
×
1015
                else {
UNCOV
1016
                        if (arg_transport == BUS_TRANSPORT_LOCAL) {
×
UNCOV
1017
                                r = get_shell(&arg_exec_path);
×
UNCOV
1018
                                if (r < 0)
×
1019
                                        return log_error_errno(r, "Failed to determine shell: %m");
×
1020
                        } else
UNCOV
1021
                                arg_exec_path = strdup("/bin/sh");
×
1022
                }
UNCOV
1023
                if (!arg_exec_path)
×
1024
                        return log_oom();
×
1025

UNCOV
1026
                l = make_login_shell_cmdline(arg_exec_path);
×
1027
        }
1028
        if (!l)
112✔
UNCOV
1029
                return log_oom();
×
1030

1031
        strv_free_and_replace(arg_cmdline, l);
112✔
1032

1033
        if (!arg_slice) {
112✔
1034
                arg_slice = strdup(SPECIAL_USER_SLICE);
112✔
1035
                if (!arg_slice)
112✔
UNCOV
1036
                        return log_oom();
×
1037
        }
1038

1039
        _cleanup_free_ char *un = NULL;
112✔
1040
        un = getusername_malloc();
112✔
1041
        if (!un)
112✔
UNCOV
1042
                return log_oom();
×
1043

1044
        /* Set a bunch of environment variables in a roughly sudo-compatible way */
1045
        r = strv_env_assign(&arg_environment, "SUDO_USER", un);
112✔
1046
        if (r < 0)
112✔
UNCOV
1047
                return log_error_errno(r, "Failed to set $SUDO_USER environment variable: %m");
×
1048

1049
        r = strv_env_assignf(&arg_environment, "SUDO_UID", UID_FMT, getuid());
112✔
1050
        if (r < 0)
112✔
UNCOV
1051
                return log_error_errno(r, "Failed to set $SUDO_UID environment variable: %m");
×
1052

1053
        r = strv_env_assignf(&arg_environment, "SUDO_GID", GID_FMT, getgid());
112✔
1054
        if (r < 0)
112✔
UNCOV
1055
                return log_error_errno(r, "Failed to set $SUDO_GID environment variable: %m");
×
1056

1057
        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_UID=" UID_FMT, getuid()) < 0)
112✔
UNCOV
1058
                return log_oom();
×
1059

1060
        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_GID=" GID_FMT, getgid()) < 0)
112✔
UNCOV
1061
                return log_oom();
×
1062

1063
        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_USER=%s", un) < 0)
112✔
1064
                return log_oom();
×
1065

1066
        if (strv_extend(&arg_property, "PAMName=systemd-run0") < 0)
112✔
1067
                return log_oom();
×
1068

1069
        if (!arg_background && arg_stdio == ARG_STDIO_PTY && shall_tint_background()) {
112✔
1070
                double hue;
2✔
1071

1072
                if (privileged_execution())
2✔
1073
                        hue = 0; /* red */
1074
                else
1075
                        hue = 60 /* yellow */;
×
1076

1077
                r = terminal_tint_color(hue, &arg_background);
2✔
1078
                if (r < 0)
2✔
1079
                        log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
2✔
1080
        }
1081

1082
        if (!arg_shell_prompt_prefix) {
112✔
1083
                const char *e = secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
112✔
1084
                if (e) {
112✔
UNCOV
1085
                        arg_shell_prompt_prefix = strdup(e);
×
UNCOV
1086
                        if (!arg_shell_prompt_prefix)
×
UNCOV
1087
                                return log_oom();
×
1088
                } else if (emoji_enabled()) {
112✔
UNCOV
1089
                        arg_shell_prompt_prefix = strjoin(special_glyph(privileged_execution() ? SPECIAL_GLYPH_SUPERHERO : SPECIAL_GLYPH_IDCARD), " ");
×
UNCOV
1090
                        if (!arg_shell_prompt_prefix)
×
UNCOV
1091
                                return log_oom();
×
1092
                }
1093
        }
1094

1095
        if (!isempty(arg_shell_prompt_prefix)) {
112✔
UNCOV
1096
                r = strv_env_assign(&arg_environment, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix);
×
1097
                if (r < 0)
×
UNCOV
1098
                        return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
×
1099
        }
1100

1101
        if (!strv_env_get(arg_environment, "XDG_SESSION_CLASS")) {
112✔
1102

1103
                /* If logging into an area, imply lightweight mode */
1104
                if (arg_lightweight < 0 && !isempty(arg_area))
112✔
1105
                        arg_lightweight = true;
17✔
1106

1107
                /* When using run0 to acquire privileges temporarily, let's not pull in session manager by
1108
                 * default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege
1109
                 * escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session
1110
                 * manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do
1111
                 * this for root though, under the assumption that if a regular user temporarily transitions into
1112
                 * another regular user it's a better default that the full user environment is uniformly
1113
                 * available. */
1114
                if (arg_lightweight < 0 && privileged_execution())
112✔
1115
                        arg_lightweight = true;
68✔
1116

1117
                if (arg_lightweight >= 0) {
112✔
1118
                        const char *class =
174✔
1119
                                arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") :
87✔
1120
                                                  (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background");
1✔
1121

1122
                        log_debug("Setting XDG_SESSION_CLASS to '%s'.", class);
87✔
1123

1124
                        r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class);
87✔
1125
                        if (r < 0)
87✔
1126
                                return log_error_errno(r, "Failed to set $XDG_SESSION_CLASS environment variable: %m");
×
1127
                }
1128
        }
1129

1130
        if (arg_area) {
112✔
1131
                r = strv_env_assign(&arg_environment, "XDG_AREA", arg_area);
17✔
1132
                if (r < 0)
17✔
UNCOV
1133
                        return log_error_errno(r, "Failed to set $XDG_AREA environment variable: %m");
×
1134
        }
1135

1136
        return 1;
1137
}
1138

1139
static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
1,101✔
1140
        int r;
1,101✔
1141

1142
        assert(m);
1,101✔
1143

1144
        r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
1,101✔
1145
        if (r < 0)
1,101✔
UNCOV
1146
                return bus_log_create_error(r);
×
1147

1148
        if (arg_aggressive_gc) {
1,101✔
1149
                r = sd_bus_message_append(m, "(sv)", "CollectMode", "s", "inactive-or-failed");
117✔
1150
                if (r < 0)
117✔
UNCOV
1151
                        return bus_log_create_error(r);
×
1152
        }
1153

1154
        r = sd_bus_is_bus_client(sd_bus_message_get_bus(m));
1,101✔
1155
        if (r < 0)
1,101✔
UNCOV
1156
                return log_error_errno(r, "Can't determine if bus connection is direct or to broker: %m");
×
1157
        if (r > 0) {
1,101✔
1158
                /* Pin the object as least as long as we are around. Note that AddRef (currently) only works
1159
                 * if we talk via the bus though. */
1160
                r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
927✔
1161
                if (r < 0)
927✔
UNCOV
1162
                        return bus_log_create_error(r);
×
1163
        }
1164

1165
        return bus_append_unit_property_assignment_many(m, t, properties);
1,101✔
1166
}
1167

1168
static int transient_cgroup_set_properties(sd_bus_message *m) {
1,079✔
1169
        _cleanup_free_ char *name = NULL;
2,158✔
1170
        _cleanup_free_ char *slice = NULL;
1,079✔
1171
        int r;
1,079✔
1172

1173
        assert(m);
1,079✔
1174

1175
        if (arg_slice_inherit) {
1,079✔
1176
                char *end;
4✔
1177

1178
                switch (arg_runtime_scope) {
4✔
1179

UNCOV
1180
                case RUNTIME_SCOPE_USER:
×
UNCOV
1181
                        r = cg_pid_get_user_slice(0, &name);
×
1182
                        break;
1183

1184
                case RUNTIME_SCOPE_SYSTEM:
4✔
1185
                        r = cg_pid_get_slice(0, &name);
4✔
1186
                        break;
1187

UNCOV
1188
                default:
×
UNCOV
1189
                        assert_not_reached();
×
1190
                }
1191

1192
                if (r < 0)
4✔
UNCOV
1193
                        return log_error_errno(r, "Failed to get PID slice: %m");
×
1194

1195
                end = endswith(name, ".slice");
4✔
1196
                if (!end)
4✔
1197
                        return -ENXIO;
1198
                *end = 0;
4✔
1199
        }
1200

1201
        if (!isempty(arg_slice) && !strextend_with_separator(&name, "-", arg_slice))
1,221✔
UNCOV
1202
                return log_oom();
×
1203

1204
        if (!name)
1,079✔
1205
                return 0;
1206

1207
        r = unit_name_mangle_with_suffix(name, "as slice",
144✔
1208
                                         arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
144✔
1209
                                         ".slice", &slice);
1210
        if (r < 0)
144✔
UNCOV
1211
                return log_error_errno(r, "Failed to mangle name '%s': %m", arg_slice);
×
1212

1213
        r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
144✔
1214
        if (r < 0)
144✔
UNCOV
1215
                return bus_log_create_error(r);
×
1216

1217
        return 0;
1218
}
1219

1220
static int transient_kill_set_properties(sd_bus_message *m) {
1,079✔
1221
        int r;
1,079✔
1222

1223
        assert(m);
1,079✔
1224

1225
        if (arg_send_sighup) {
1,079✔
1226
                r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
113✔
1227
                if (r < 0)
113✔
UNCOV
1228
                        return bus_log_create_error(r);
×
1229
        }
1230

1231
        return 0;
1232
}
1233

1234
static int transient_service_set_properties(sd_bus_message *m, const char *pty_path, int pty_fd) {
1,064✔
1235
        bool send_term = false;
1,064✔
1236
        int r;
1,064✔
1237

1238
        /* We disable environment expansion on the server side via ExecStartEx=:.
1239
         * ExecStartEx was added relatively recently (v243), and some bugs were fixed only later.
1240
         * So use that feature only if required. It will fail with older systemds. */
1241
        bool use_ex_prop = !arg_expand_environment;
1,064✔
1242

1243
        assert(m);
1,064✔
1244
        assert((!!pty_path) == (pty_fd >= 0));
1,064✔
1245

1246
        r = transient_unit_set_properties(m, UNIT_SERVICE, arg_property);
1,064✔
1247
        if (r < 0)
1,064✔
1248
                return r;
1249

1250
        r = transient_kill_set_properties(m);
1,060✔
1251
        if (r < 0)
1,060✔
1252
                return r;
1253

1254
        r = transient_cgroup_set_properties(m);
1,060✔
1255
        if (r < 0)
1,060✔
1256
                return r;
1257

1258
        if (arg_remain_after_exit) {
1,060✔
1259
                r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
26✔
1260
                if (r < 0)
26✔
UNCOV
1261
                        return bus_log_create_error(r);
×
1262
        }
1263

1264
        if (arg_service_type) {
1,060✔
1265
                r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
136✔
1266
                if (r < 0)
136✔
UNCOV
1267
                        return bus_log_create_error(r);
×
1268
        }
1269

1270
        if (arg_exec_user) {
1,060✔
1271
                r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
63✔
1272
                if (r < 0)
63✔
UNCOV
1273
                        return bus_log_create_error(r);
×
1274
        }
1275

1276
        if (arg_exec_group) {
1,060✔
1277
                r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
2✔
1278
                if (r < 0)
2✔
UNCOV
1279
                        return bus_log_create_error(r);
×
1280
        }
1281

1282
        if (arg_nice_set) {
1,060✔
1283
                r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
1✔
1284
                if (r < 0)
1✔
UNCOV
1285
                        return bus_log_create_error(r);
×
1286
        }
1287

1288
        if (arg_working_directory) {
1,060✔
1289
                r = sd_bus_message_append(m, "(sv)", "WorkingDirectory", "s", arg_working_directory);
113✔
1290
                if (r < 0)
113✔
UNCOV
1291
                        return bus_log_create_error(r);
×
1292
        }
1293

1294
        if (pty_path) {
1,060✔
1295
                r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
10✔
1296
                                          "TTYPath", "s", pty_path,
1297
                                          "StandardInputFileDescriptor", "h", pty_fd,
1298
                                          "StandardOutputFileDescriptor", "h", pty_fd,
1299
                                          "StandardErrorFileDescriptor", "h", pty_fd);
1300
                if (r < 0)
10✔
UNCOV
1301
                        return bus_log_create_error(r);
×
1302

1303
                send_term = true;
1304

1305
        } else if (arg_stdio == ARG_STDIO_DIRECT) {
1,050✔
1306
                r = sd_bus_message_append(m,
334✔
1307
                                          "(sv)(sv)(sv)",
1308
                                          "StandardInputFileDescriptor", "h", STDIN_FILENO,
1309
                                          "StandardOutputFileDescriptor", "h", STDOUT_FILENO,
1310
                                          "StandardErrorFileDescriptor", "h", STDERR_FILENO);
1311
                if (r < 0)
334✔
UNCOV
1312
                        return bus_log_create_error(r);
×
1313

1314
                send_term = isatty_safe(STDIN_FILENO) || isatty_safe(STDOUT_FILENO) || isatty_safe(STDERR_FILENO);
334✔
1315
        }
1316

1317
        if (send_term) {
1318
                const char *e;
10✔
1319

1320
                e = getenv("TERM");
10✔
1321
                if (e) {
10✔
1322
                        _cleanup_free_ char *n = NULL;
10✔
1323

1324
                        n = strjoin("TERM=", e);
10✔
1325
                        if (!n)
10✔
UNCOV
1326
                                return log_oom();
×
1327

1328
                        r = sd_bus_message_append(m,
10✔
1329
                                                  "(sv)",
1330
                                                  "Environment", "as", 1, n);
1331
                        if (r < 0)
10✔
UNCOV
1332
                                return bus_log_create_error(r);
×
1333
                }
1334
        }
1335

1336
        if (!strv_isempty(arg_environment)) {
1,060✔
1337
                r = sd_bus_message_open_container(m, 'r', "sv");
114✔
1338
                if (r < 0)
114✔
UNCOV
1339
                        return bus_log_create_error(r);
×
1340

1341
                r = sd_bus_message_append(m, "s", "Environment");
114✔
1342
                if (r < 0)
114✔
UNCOV
1343
                        return bus_log_create_error(r);
×
1344

1345
                r = sd_bus_message_open_container(m, 'v', "as");
114✔
1346
                if (r < 0)
114✔
UNCOV
1347
                        return bus_log_create_error(r);
×
1348

1349
                r = sd_bus_message_append_strv(m, arg_environment);
114✔
1350
                if (r < 0)
114✔
UNCOV
1351
                        return bus_log_create_error(r);
×
1352

1353
                r = sd_bus_message_close_container(m);
114✔
1354
                if (r < 0)
114✔
UNCOV
1355
                        return bus_log_create_error(r);
×
1356

1357
                r = sd_bus_message_close_container(m);
114✔
1358
                if (r < 0)
114✔
UNCOV
1359
                        return bus_log_create_error(r);
×
1360
        }
1361

1362
        /* Exec container */
1363
        if (!strv_isempty(arg_cmdline)) {
2,124✔
1364
                r = sd_bus_message_open_container(m, 'r', "sv");
1,060✔
1365
                if (r < 0)
1,060✔
UNCOV
1366
                        return bus_log_create_error(r);
×
1367

1368
                r = sd_bus_message_append(m, "s",
2,007✔
1369
                                          use_ex_prop ? "ExecStartEx" : "ExecStart");
1370
                if (r < 0)
1,060✔
UNCOV
1371
                        return bus_log_create_error(r);
×
1372

1373
                r = sd_bus_message_open_container(m, 'v',
2,007✔
1374
                                                  use_ex_prop ? "a(sasas)" : "a(sasb)");
1375
                if (r < 0)
1,060✔
UNCOV
1376
                        return bus_log_create_error(r);
×
1377

1378
                r = sd_bus_message_open_container(m, 'a',
2,007✔
1379
                                                  use_ex_prop ? "(sasas)" : "(sasb)");
1380
                if (r < 0)
1,060✔
1381
                        return bus_log_create_error(r);
×
1382

1383
                r = sd_bus_message_open_container(m, 'r',
2,007✔
1384
                                                  use_ex_prop ? "sasas" : "sasb");
1385
                if (r < 0)
1,060✔
UNCOV
1386
                        return bus_log_create_error(r);
×
1387

1388
                r = sd_bus_message_append(m, "s", arg_exec_path ?: arg_cmdline[0]);
1,060✔
1389
                if (r < 0)
1,060✔
UNCOV
1390
                        return bus_log_create_error(r);
×
1391

1392
                r = sd_bus_message_append_strv(m, arg_cmdline);
1,060✔
1393
                if (r < 0)
1,060✔
UNCOV
1394
                        return bus_log_create_error(r);
×
1395

1396
                if (use_ex_prop) {
1,060✔
UNCOV
1397
                        _cleanup_strv_free_ char **opts = NULL;
×
1398

1399
                        r = exec_command_flags_to_strv(
339✔
1400
                                        (arg_expand_environment ? 0 : EXEC_COMMAND_NO_ENV_EXPAND)|(arg_ignore_failure ? EXEC_COMMAND_IGNORE_FAILURE : 0),
226✔
1401
                                        &opts);
1402
                        if (r < 0)
113✔
UNCOV
1403
                                return log_error_errno(r, "Failed to format execute flags: %m");
×
1404

1405
                        r = sd_bus_message_append_strv(m, opts);
113✔
1406
                } else
1407
                        r = sd_bus_message_append(m, "b", arg_ignore_failure);
947✔
1408
                if (r < 0)
1,060✔
UNCOV
1409
                        return bus_log_create_error(r);
×
1410

1411
                r = sd_bus_message_close_container(m);
1,060✔
1412
                if (r < 0)
1,060✔
UNCOV
1413
                        return bus_log_create_error(r);
×
1414

1415
                r = sd_bus_message_close_container(m);
1,060✔
1416
                if (r < 0)
1,060✔
UNCOV
1417
                        return bus_log_create_error(r);
×
1418

1419
                r = sd_bus_message_close_container(m);
1,060✔
1420
                if (r < 0)
1,060✔
UNCOV
1421
                        return bus_log_create_error(r);
×
1422

1423
                r = sd_bus_message_close_container(m);
1,060✔
1424
                if (r < 0)
1,060✔
UNCOV
1425
                        return bus_log_create_error(r);
×
1426
        }
1427

1428
        return 0;
1429
}
1430

1431
static int transient_scope_set_properties(sd_bus_message *m, bool allow_pidfd) {
19✔
1432
        int r;
19✔
1433

1434
        assert(m);
19✔
1435

1436
        r = transient_unit_set_properties(m, UNIT_SCOPE, arg_property);
19✔
1437
        if (r < 0)
19✔
1438
                return r;
19✔
1439

1440
        r = transient_kill_set_properties(m);
19✔
1441
        if (r < 0)
19✔
1442
                return r;
1443

1444
        r = transient_cgroup_set_properties(m);
19✔
1445
        if (r < 0)
19✔
1446
                return r;
1447

1448
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
19✔
1449

1450
        r = pidref_set_self(&pidref);
19✔
1451
        if (r < 0)
19✔
1452
                return r;
1453

1454
        r = bus_append_scope_pidref(m, &pidref, allow_pidfd);
19✔
1455
        if (r < 0)
19✔
1456
                return bus_log_create_error(r);
×
1457

1458
        return 0;
1459
}
1460

1461
static int transient_timer_set_properties(sd_bus_message *m) {
15✔
1462
        int r;
15✔
1463

1464
        assert(m);
15✔
1465

1466
        r = transient_unit_set_properties(m, UNIT_TIMER, arg_timer_property);
15✔
1467
        if (r < 0)
15✔
1468
                return r;
1469

1470
        /* Automatically clean up our transient timers */
1471
        r = sd_bus_message_append(m, "(sv)", "RemainAfterElapse", "b", false);
10✔
1472
        if (r < 0)
10✔
UNCOV
1473
                return bus_log_create_error(r);
×
1474

1475
        return 0;
1476
}
1477

1478
static int make_unit_name(UnitType t, char **ret) {
565✔
1479
        int r;
565✔
1480

1481
        assert(t >= 0);
565✔
1482
        assert(t < _UNIT_TYPE_MAX);
565✔
1483
        assert(ret);
565✔
1484

1485
        /* Preferably use our PID + pidfd ID as identifier, if available. It's a boot time unique identifier
1486
         * managed by the kernel. Unfortunately only new kernels support this, hence we keep some fallback
1487
         * logic in place. */
1488

1489
        _cleanup_(pidref_done) PidRef self = PIDREF_NULL;
565✔
1490
        r = pidref_set_self(&self);
565✔
1491
        if (r < 0)
565✔
UNCOV
1492
                return log_error_errno(r, "Failed to get reference to my own process: %m");
×
1493

1494
        r = pidref_acquire_pidfd_id(&self);
565✔
1495
        if (r < 0) {
565✔
1496
                log_debug_errno(r, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
451✔
1497

1498
                /* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
1499
                sd_id128_t rnd;
451✔
1500
                r = sd_id128_randomize(&rnd);
451✔
1501
                if (r < 0)
451✔
UNCOV
1502
                        return log_error_errno(r, "Failed to generate random run unit name: %m");
×
1503

1504
                r = asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t));
451✔
1505
        } else
1506
                r = asprintf(ret, "run-p" PID_FMT "-i%" PRIu64 ".%s", self.pid, self.fd_id, unit_type_to_string(t));
114✔
1507
        if (r < 0)
565✔
UNCOV
1508
                return log_oom();
×
1509

1510
        return 0;
1511
}
1512

1513
static int connect_bus(sd_bus **ret) {
1,092✔
1514
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1,092✔
1515
        int r;
1,092✔
1516

1517
        assert(ret);
1,092✔
1518

1519
        /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
1520
         * limited direct connection */
1521
        if (arg_wait ||
1,092✔
1522
            arg_stdio != ARG_STDIO_NONE ||
228✔
1523
            (arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
163✔
1524
                r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
931✔
1525
        else
1526
                r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
161✔
1527
        if (r < 0)
1,092✔
1528
                return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
1,092✔
1529

1530
        (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1,088✔
1531

1532
        *ret = TAKE_PTR(bus);
1,088✔
1533
        return 0;
1,088✔
1534
}
1535

1536
typedef struct RunContext {
1537
        sd_event *event;
1538
        PTYForward *forward;
1539
        char *service;
1540
        char *bus_path;
1541

1542
        /* Bus objects */
1543
        sd_bus *bus;
1544
        sd_bus_slot *match_properties_changed;
1545
        sd_bus_slot *match_disconnected;
1546
        sd_event_source *retry_timer;
1547

1548
        /* Current state of the unit */
1549
        char *active_state;
1550
        bool has_job;
1551

1552
        /* The exit data of the unit */
1553
        uint64_t inactive_exit_usec;
1554
        uint64_t inactive_enter_usec;
1555
        char *result;
1556
        uint64_t cpu_usage_nsec;
1557
        uint64_t memory_peak;
1558
        uint64_t memory_swap_peak;
1559
        uint64_t ip_ingress_bytes;
1560
        uint64_t ip_egress_bytes;
1561
        uint64_t io_read_bytes;
1562
        uint64_t io_write_bytes;
1563
        uint32_t exit_code;
1564
        uint32_t exit_status;
1565
} RunContext;
1566

1567
static int run_context_update(RunContext *c);
1568
static int run_context_attach_bus(RunContext *c, sd_bus *bus);
1569
static void run_context_detach_bus(RunContext *c);
1570
static int run_context_reconnect(RunContext *c);
1571

1572
static void run_context_done(RunContext *c) {
910✔
1573
        assert(c);
910✔
1574

1575
        run_context_detach_bus(c);
910✔
1576

1577
        c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
910✔
1578
        c->forward = pty_forward_free(c->forward);
910✔
1579
        c->event = sd_event_unref(c->event);
910✔
1580

1581
        free(c->active_state);
910✔
1582
        free(c->result);
910✔
1583
        free(c->bus_path);
910✔
1584
        free(c->service);
910✔
1585
}
910✔
1586

UNCOV
1587
static int on_retry_timer(sd_event_source *s, uint64_t usec, void *userdata) {
×
1588
        RunContext *c = ASSERT_PTR(userdata);
×
1589

1590
        c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
×
1591

UNCOV
1592
        return run_context_reconnect(c);
×
1593
}
1594

UNCOV
1595
static int run_context_reconnect(RunContext *c) {
×
1596
        int r;
×
1597

UNCOV
1598
        assert(c);
×
1599

UNCOV
1600
        run_context_detach_bus(c);
×
1601

UNCOV
1602
        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
×
UNCOV
1603
        r = connect_bus(&bus);
×
UNCOV
1604
        if (r < 0) {
×
UNCOV
1605
                log_warning_errno(r, "Failed to reconnect, retrying in 2s: %m");
×
1606

UNCOV
1607
                r = event_reset_time_relative(
×
1608
                                c->event,
1609
                                &c->retry_timer,
1610
                                CLOCK_MONOTONIC,
1611
                                2 * USEC_PER_SEC, /* accuracy= */ 0,
1612
                                on_retry_timer, c,
1613
                                SD_EVENT_PRIORITY_NORMAL,
1614
                                "retry-timeout",
1615
                                /* force_reset= */ false);
UNCOV
1616
                if (r < 0) {
×
UNCOV
1617
                        (void) sd_event_exit(c->event, EXIT_FAILURE);
×
UNCOV
1618
                        return log_error_errno(r, "Failed to install retry timer: %m");
×
1619
                }
1620

1621
                return 0;
1622
        }
1623

UNCOV
1624
        r = run_context_attach_bus(c, bus);
×
UNCOV
1625
        if (r < 0) {
×
UNCOV
1626
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
1627
                return r;
1628
        }
1629

UNCOV
1630
        log_info("Reconnected to bus.");
×
1631

UNCOV
1632
        return run_context_update(c);
×
1633
}
1634

1635
static void run_context_check_done(RunContext *c) {
5,509✔
1636
        assert(c);
5,509✔
1637

1638
        bool done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
5,509✔
1639

1640
        if (done && c->forward) /* If the service is gone, it's time to drain the output */
920✔
1641
                done = pty_forward_drain(c->forward);
20✔
1642

1643
        if (done)
920✔
1644
                (void) sd_event_exit(c->event, EXIT_SUCCESS);
920✔
1645
}
5,509✔
1646

1647
static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
5,499✔
1648
        bool *b = userdata;
5,499✔
1649
        const char *job;
5,499✔
1650
        uint32_t id;
5,499✔
1651
        int r;
5,499✔
1652

1653
        r = sd_bus_message_read(m, "(uo)", &id, &job);
5,499✔
1654
        if (r < 0)
5,499✔
1655
                return r;
5,499✔
1656

1657
        *b = id != 0 || !streq(job, "/");
5,499✔
1658
        return 0;
5,499✔
1659
}
1660

1661
static int run_context_update(RunContext *c) {
5,499✔
1662

1663
        static const struct bus_properties_map map[] = {
5,499✔
1664
                { "ActiveState",                     "s",    NULL,    offsetof(RunContext, active_state)        },
1665
                { "InactiveExitTimestampMonotonic",  "t",    NULL,    offsetof(RunContext, inactive_exit_usec)  },
1666
                { "InactiveEnterTimestampMonotonic", "t",    NULL,    offsetof(RunContext, inactive_enter_usec) },
1667
                { "Result",                          "s",    NULL,    offsetof(RunContext, result)              },
1668
                { "ExecMainCode",                    "i",    NULL,    offsetof(RunContext, exit_code)           },
1669
                { "ExecMainStatus",                  "i",    NULL,    offsetof(RunContext, exit_status)         },
1670
                { "CPUUsageNSec",                    "t",    NULL,    offsetof(RunContext, cpu_usage_nsec)      },
1671
                { "MemoryPeak",                      "t",    NULL,    offsetof(RunContext, memory_peak)         },
1672
                { "MemorySwapPeak",                  "t",    NULL,    offsetof(RunContext, memory_swap_peak)    },
1673
                { "IPIngressBytes",                  "t",    NULL,    offsetof(RunContext, ip_ingress_bytes)    },
1674
                { "IPEgressBytes",                   "t",    NULL,    offsetof(RunContext, ip_egress_bytes)     },
1675
                { "IOReadBytes",                     "t",    NULL,    offsetof(RunContext, io_read_bytes)       },
1676
                { "IOWriteBytes",                    "t",    NULL,    offsetof(RunContext, io_write_bytes)      },
1677
                { "Job",                             "(uo)", map_job, offsetof(RunContext, has_job)             },
1678
                {}
1679
        };
1680

1681
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5,499✔
1682
        int r;
5,499✔
1683

1684
        assert(c);
5,499✔
1685
        assert(c->bus);
5,499✔
1686

1687
        r = bus_map_all_properties(
10,998✔
1688
                        c->bus,
1689
                        "org.freedesktop.systemd1",
1690
                        c->bus_path,
5,499✔
1691
                        map,
1692
                        BUS_MAP_STRDUP,
1693
                        &error,
1694
                        NULL,
1695
                        c);
1696
        if (r < 0) {
5,499✔
1697
                /* If this is a connection error, then try to reconnect. This might be because the service
1698
                 * manager is being restarted. Handle this gracefully. */
UNCOV
1699
                if (sd_bus_error_has_names(
×
1700
                                    &error,
1701
                                    SD_BUS_ERROR_NO_REPLY,
1702
                                    SD_BUS_ERROR_DISCONNECTED,
1703
                                    SD_BUS_ERROR_TIMED_OUT,
1704
                                    SD_BUS_ERROR_SERVICE_UNKNOWN,
1705
                                    SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
1706

UNCOV
1707
                        log_info("Bus call failed due to connection problems. Trying to reconnect...");
×
1708
                        /* Not propagating error, because we handled it already, by reconnecting. */
UNCOV
1709
                        return run_context_reconnect(c);
×
1710
                }
1711

UNCOV
1712
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
UNCOV
1713
                return log_error_errno(r, "Failed to query unit state: %s", bus_error_message(&error, r));
×
1714
        }
1715

1716
        run_context_check_done(c);
5,499✔
1717
        return 0;
1718
}
1719

1720
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
4,589✔
1721
        return run_context_update(ASSERT_PTR(userdata));
4,589✔
1722
}
1723

UNCOV
1724
static int on_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
×
1725
        /* If our connection gets terminated, then try to reconnect. This might be because the service
1726
         * manager is being restarted. Handle this gracefully. */
1727
        log_info("Got disconnected from bus connection. Trying to reconnect...");
×
UNCOV
1728
        return run_context_reconnect(ASSERT_PTR(userdata));
×
1729
}
1730

1731
static int run_context_attach_bus(RunContext *c, sd_bus *bus) {
910✔
1732
        int r;
910✔
1733

1734
        assert(c);
910✔
1735
        assert(bus);
910✔
1736

1737
        assert(!c->bus);
910✔
1738
        assert(!c->match_properties_changed);
910✔
1739
        assert(!c->match_disconnected);
910✔
1740

1741
        c->bus = sd_bus_ref(bus);
910✔
1742

1743
        r = sd_bus_match_signal_async(
1,820✔
1744
                        c->bus,
1745
                        &c->match_properties_changed,
1746
                        "org.freedesktop.systemd1",
1747
                        c->bus_path,
910✔
1748
                        "org.freedesktop.DBus.Properties",
1749
                        "PropertiesChanged",
1750
                        on_properties_changed, NULL, c);
1751
        if (r < 0)
910✔
UNCOV
1752
                return log_error_errno(r, "Failed to request PropertiesChanged signal match: %m");
×
1753

1754
        r = sd_bus_match_signal_async(
910✔
1755
                        bus,
1756
                        &c->match_disconnected,
1757
                        "org.freedesktop.DBus.Local",
1758
                        /* path= */ NULL,
1759
                        "org.freedesktop.DBus.Local",
1760
                        "Disconnected",
1761
                        on_disconnected, NULL, c);
1762
        if (r < 0)
910✔
UNCOV
1763
                return log_error_errno(r, "Failed to request Disconnected signal match: %m");
×
1764

1765
        r = sd_bus_attach_event(c->bus, c->event, SD_EVENT_PRIORITY_NORMAL);
910✔
1766
        if (r < 0)
910✔
UNCOV
1767
                return log_error_errno(r, "Failed to attach bus to event loop: %m");
×
1768

1769
        return 0;
1770
}
1771

1772
static void run_context_detach_bus(RunContext *c) {
910✔
1773
        assert(c);
910✔
1774

1775
        if (c->bus) {
910✔
1776
                (void) sd_bus_detach_event(c->bus);
910✔
1777
                c->bus = sd_bus_unref(c->bus);
910✔
1778
        }
1779

1780
        c->match_properties_changed = sd_bus_slot_unref(c->match_properties_changed);
910✔
1781
        c->match_disconnected = sd_bus_slot_unref(c->match_disconnected);
910✔
1782
}
910✔
1783

1784
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
10✔
1785
        RunContext *c = ASSERT_PTR(userdata);
10✔
1786

1787
        assert(f);
10✔
1788

1789
        if (rcode == -ECANCELED) {
10✔
1790
                log_debug_errno(rcode, "PTY forwarder disconnected.");
×
UNCOV
1791
                if (!arg_wait)
×
UNCOV
1792
                        return sd_event_exit(c->event, EXIT_SUCCESS);
×
1793

1794
                /* If --wait is specified, we'll only exit the pty forwarding, but will continue to wait
1795
                 * for the service to end. If the user hits ^C we'll exit too. */
1796
        } else if (rcode < 0) {
10✔
UNCOV
1797
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
UNCOV
1798
                return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
×
1799
        }
1800

1801
        run_context_check_done(c);
10✔
1802
        return 0;
10✔
1803
}
1804

1805
static int make_transient_service_unit(
1,051✔
1806
                sd_bus *bus,
1807
                sd_bus_message **message,
1808
                const char *service,
1809
                const char *pty_path,
1810
                int pty_fd) {
1811

1812
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1,051✔
1813
        int r;
1,051✔
1814

1815
        assert(bus);
1,051✔
1816
        assert(message);
1,051✔
1817
        assert(service);
1,051✔
1818

1819
        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
1,051✔
1820
        if (r < 0)
1,051✔
UNCOV
1821
                return bus_log_create_error(r);
×
1822

1823
        /* Name and mode */
1824
        r = sd_bus_message_append(m, "ss", service, job_mode_to_string(arg_job_mode));
1,051✔
1825
        if (r < 0)
1,051✔
UNCOV
1826
                return bus_log_create_error(r);
×
1827

1828
        /* Properties */
1829
        r = sd_bus_message_open_container(m, 'a', "(sv)");
1,051✔
1830
        if (r < 0)
1,051✔
1831
                return bus_log_create_error(r);
×
1832

1833
        r = transient_service_set_properties(m, pty_path, pty_fd);
1,051✔
1834
        if (r < 0)
1,051✔
1835
                return r;
1836

1837
        r = sd_bus_message_close_container(m);
1,047✔
1838
        if (r < 0)
1,047✔
UNCOV
1839
                return bus_log_create_error(r);
×
1840

1841
        /* Auxiliary units */
1842
        r = sd_bus_message_append(m, "a(sa(sv))", 0);
1,047✔
1843
        if (r < 0)
1,047✔
UNCOV
1844
                return bus_log_create_error(r);
×
1845

1846
        *message = TAKE_PTR(m);
1,047✔
1847
        return 0;
1,047✔
1848
}
1849

1850
static int bus_call_with_hint(
1,060✔
1851
                sd_bus *bus,
1852
                sd_bus_message *message,
1853
                const char *name,
1854
                sd_bus_message **reply) {
1855

UNCOV
1856
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1857
        int r;
1,060✔
1858

1859
        r = sd_bus_call(bus, message, 0, &error, reply);
1,060✔
1860
        if (r < 0) {
1,060✔
1861
                log_error_errno(r, "Failed to start transient %s unit: %s", name, bus_error_message(&error, r));
18✔
1862

1863
                if (!arg_expand_environment &&
18✔
UNCOV
1864
                    sd_bus_error_has_names(&error,
×
1865
                                           SD_BUS_ERROR_UNKNOWN_PROPERTY,
1866
                                           SD_BUS_ERROR_PROPERTY_READ_ONLY))
UNCOV
1867
                        log_notice_errno(r, "Hint: --expand-environment=no is not supported by old systemd");
×
1868
        }
1869

1870
        return r;
1,060✔
1871
}
1872

1873
static int acquire_invocation_id(sd_bus *bus, const char *unit, sd_id128_t *ret) {
596✔
UNCOV
1874
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1875
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
596✔
1876
        _cleanup_free_ char *object = NULL;
596✔
1877
        int r;
596✔
1878

1879
        assert(bus);
596✔
1880
        assert(ret);
596✔
1881

1882
        if (unit) {
596✔
1883
                object = unit_dbus_path_from_name(unit);
579✔
1884
                if (!object)
579✔
UNCOV
1885
                        return log_oom();
×
1886
        }
1887

1888
        r = sd_bus_get_property(bus,
596✔
1889
                                "org.freedesktop.systemd1",
1890
                                object ?: "/org/freedesktop/systemd1/unit/self",
1891
                                "org.freedesktop.systemd1.Unit",
1892
                                "InvocationID",
1893
                                &error,
1894
                                &reply,
1895
                                "ay");
1896
        if (r < 0)
596✔
1897
                return log_error_errno(r, "Failed to request invocation ID for unit: %s", bus_error_message(&error, r));
×
1898

1899
        r = bus_message_read_id128(reply, ret);
596✔
1900
        if (r < 0)
596✔
1901
                return bus_log_parse_error(r);
×
1902

1903
        return r; /* Return true when we get a non-null invocation ID. */
1904
}
1905

1906
static void set_window_title(PTYForward *f) {
10✔
1907
        _cleanup_free_ char *hn = NULL, *cl = NULL, *dot = NULL;
10✔
1908

1909
        assert(f);
10✔
1910

1911
        if (!shall_set_terminal_title())
10✔
1912
                return;
1913

1914
        if (!arg_host)
10✔
1915
                (void) gethostname_strict(&hn);
9✔
1916

1917
        cl = strv_join(arg_cmdline, " ");
10✔
1918
        if (!cl)
10✔
UNCOV
1919
                return (void) log_oom();
×
1920

1921
        if (emoji_enabled())
10✔
UNCOV
1922
                dot = strjoin(special_glyph(privileged_execution() ? SPECIAL_GLYPH_RED_CIRCLE : SPECIAL_GLYPH_YELLOW_CIRCLE), " ");
×
1923

1924
        if (arg_host || hn)
10✔
1925
                (void) pty_forward_set_titlef(f, "%s%s on %s", strempty(dot), cl, arg_host ?: hn);
20✔
1926
        else
UNCOV
1927
                (void) pty_forward_set_titlef(f, "%s%s", strempty(dot), cl);
×
1928

1929
        (void) pty_forward_set_title_prefix(f, dot);
10✔
1930
}
1931

UNCOV
1932
static int fchown_to_capsule(int fd, const char *capsule) {
×
UNCOV
1933
        _cleanup_free_ char *p = NULL;
×
UNCOV
1934
        int r;
×
1935

UNCOV
1936
        assert(fd >= 0);
×
UNCOV
1937
        assert(capsule);
×
1938

UNCOV
1939
        p = path_join("/run/capsules/", capsule);
×
UNCOV
1940
        if (!p)
×
1941
                return -ENOMEM;
1942

UNCOV
1943
        struct stat st;
×
UNCOV
1944
        r = chase_and_stat(p, /* root= */ NULL, CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS, /* ret_path= */ NULL, &st);
×
UNCOV
1945
        if (r < 0)
×
1946
                return r;
1947

UNCOV
1948
        if (uid_is_system(st.st_uid) || gid_is_system(st.st_gid)) /* paranoid safety check */
×
UNCOV
1949
                return -EPERM;
×
1950

UNCOV
1951
        return fchmod_and_chown(fd, 0600, st.st_uid, st.st_gid);
×
1952
}
1953

1954
static int print_unit_invocation(const char *unit, sd_id128_t invocation_id) {
596✔
1955
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
596✔
1956
        int r;
596✔
1957

1958
        assert(unit);
596✔
1959

1960
        if (!sd_json_format_enabled(arg_json_format_flags)) {
596✔
1961
                if (sd_id128_is_null(invocation_id))
594✔
1962
                        log_info("Running as unit: %s", unit);
96✔
1963
                else
1964
                        log_info("Running as unit: %s; invocation ID: " SD_ID128_FORMAT_STR, unit, SD_ID128_FORMAT_VAL(invocation_id));
498✔
1965
                return 0;
594✔
1966
        }
1967

1968
        r = sd_json_variant_set_field_string(&v, "unit", unit);
2✔
1969
        if (r < 0)
2✔
1970
                return r;
1971

1972
        if (!sd_id128_is_null(invocation_id)) {
2✔
1973
                r = sd_json_variant_set_field_id128(&v, "invocation_id", invocation_id);
2✔
1974
                if (r < 0)
2✔
1975
                        return r;
1976
        }
1977

1978
        return sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
2✔
1979
}
1980

1981
typedef struct JobDoneContext {
1982
        char *unit;
1983
        char *start_job;
1984
        sd_bus_slot *match;
1985
} JobDoneContext;
1986

1987
static void job_done_context_done(JobDoneContext *c) {
1,323✔
1988
        assert(c);
1,323✔
1989

1990
        c->unit = mfree(c->unit);
1,323✔
1991
        c->start_job = mfree(c->start_job);
1,323✔
1992
        c->match = sd_bus_slot_unref(c->match);
1,323✔
1993
}
1,323✔
1994

1995
static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
398✔
1996
        JobDoneContext *c = ASSERT_PTR(userdata);
398✔
1997
        const char *path;
398✔
1998
        int r;
398✔
1999

2000
        assert(m);
398✔
2001

2002
        r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, /* unit= */ NULL, /* result= */ NULL);
398✔
2003
        if (r < 0) {
398✔
UNCOV
2004
                bus_log_parse_error(r);
×
UNCOV
2005
                return 0;
×
2006
        }
2007

2008
        if (!streq_ptr(path, c->start_job))
398✔
2009
                return 0;
2010

2011
        /* Notify our caller that the service is now running, just in case. */
2012
        (void) sd_notifyf(/* unset_environment= */ false,
272✔
2013
                          "READY=1\n"
2014
                          "RUN_UNIT=%s",
2015
                          c->unit);
2016

2017
        job_done_context_done(c);
272✔
2018
        return 0;
2019
}
2020

2021
static int start_transient_service(sd_bus *bus) {
1,051✔
2022
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
2,098✔
2023
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1,051✔
UNCOV
2024
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
2025
        _cleanup_free_ char *service = NULL, *pty_path = NULL;
1,051✔
2026
        _cleanup_close_ int pty_fd = -EBADF, peer_fd = -EBADF;
2,102✔
2027
        int r;
1,051✔
2028

2029
        assert(bus);
1,051✔
2030

2031
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1,051✔
2032

2033
        if (arg_stdio == ARG_STDIO_PTY) {
1,051✔
2034

2035
                if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)) {
10✔
2036
                        pty_fd = openpt_allocate(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK, &pty_path);
9✔
2037
                        if (pty_fd < 0)
9✔
2038
                                return log_error_errno(pty_fd, "Failed to acquire pseudo tty: %m");
×
2039

2040
                        peer_fd = pty_open_peer(pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
9✔
2041
                        if (peer_fd < 0)
9✔
2042
                                return log_error_errno(peer_fd, "Failed to open pty peer: %m");
×
2043

2044
                        if (arg_transport == BUS_TRANSPORT_CAPSULE) {
9✔
2045
                                /* If we are in capsule mode, we must give the capsule UID/GID access to the PTY we just allocated first. */
2046

UNCOV
2047
                                r = fchown_to_capsule(peer_fd, arg_host);
×
UNCOV
2048
                                if (r < 0)
×
UNCOV
2049
                                        return log_error_errno(r, "Failed to chown tty to capsule UID/GID: %m");
×
2050
                        }
2051

2052
                } else if (arg_transport == BUS_TRANSPORT_MACHINE) {
1✔
2053
                        _cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL;
1✔
2054
                        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pty_reply = NULL;
1✔
2055
                        const char *s;
1✔
2056

2057
                        r = sd_bus_default_system(&system_bus);
1✔
2058
                        if (r < 0)
1✔
UNCOV
2059
                                return log_error_errno(r, "Failed to connect to system bus: %m");
×
2060

2061
                        (void) sd_bus_set_allow_interactive_authorization(system_bus, arg_ask_password);
1✔
2062

2063
                        r = bus_call_method(system_bus,
1✔
2064
                                            bus_machine_mgr,
2065
                                            "OpenMachinePTY",
2066
                                            &error,
2067
                                            &pty_reply,
2068
                                            "s", arg_host);
2069
                        if (r < 0)
1✔
UNCOV
2070
                                return log_error_errno(r, "Failed to get machine PTY: %s", bus_error_message(&error, r));
×
2071

2072
                        r = sd_bus_message_read(pty_reply, "hs", &pty_fd, &s);
1✔
2073
                        if (r < 0)
1✔
UNCOV
2074
                                return bus_log_parse_error(r);
×
2075

2076
                        pty_fd = fcntl(pty_fd, F_DUPFD_CLOEXEC, 3);
1✔
2077
                        if (pty_fd < 0)
1✔
UNCOV
2078
                                return log_error_errno(errno, "Failed to duplicate master fd: %m");
×
2079

2080
                        pty_path = strdup(s);
1✔
2081
                        if (!pty_path)
1✔
UNCOV
2082
                                return log_oom();
×
2083

2084
                        peer_fd = pty_open_peer(pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
1✔
2085
                        if (peer_fd < 0)
1✔
UNCOV
2086
                                return log_error_errno(peer_fd, "Failed to open PTY peer: %m");
×
2087
                } else
UNCOV
2088
                        assert_not_reached();
×
2089
        }
2090

2091
        if (arg_unit) {
1,051✔
2092
                r = unit_name_mangle_with_suffix(arg_unit, "as unit",
511✔
2093
                                                 arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
511✔
2094
                                                 ".service", &service);
2095
                if (r < 0)
511✔
UNCOV
2096
                        return log_error_errno(r, "Failed to mangle unit name: %m");
×
2097
        } else {
2098
                r = make_unit_name(UNIT_SERVICE, &service);
540✔
2099
                if (r < 0)
540✔
2100
                        return r;
2101
        }
2102

2103
        /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin
2104
         * lets skip this however, because we should start that already when the start job is running, and
2105
         * there's little point in waiting for the start job to complete in that case anyway, as we'll wait
2106
         * for EOF anyway, which is going to be much later. */
2107
        _cleanup_(job_done_context_done) JobDoneContext job_done_context = {};
1,051✔
2108
        if (!arg_no_block) {
1,051✔
2109
                if (arg_stdio == ARG_STDIO_NONE) {
1,047✔
2110
                        r = bus_wait_for_jobs_new(bus, &w);
703✔
2111
                        if (r < 0)
703✔
UNCOV
2112
                                return log_error_errno(r, "Could not watch jobs: %m");
×
2113
                } else {
2114
                        job_done_context.unit = strdup(service);
344✔
2115
                        if (!job_done_context.unit)
344✔
UNCOV
2116
                                return log_oom();
×
2117

2118
                        /* When we are a bus client we match by sender. Direct connections OTOH have no
2119
                         * initialized sender field, and hence we ignore the sender then */
2120
                        r = sd_bus_match_signal_async(
344✔
2121
                                        bus,
2122
                                        &job_done_context.match,
2123
                                        sd_bus_is_bus_client(bus) ? "org.freedesktop.systemd1" : NULL,
344✔
2124
                                        "/org/freedesktop/systemd1",
2125
                                        "org.freedesktop.systemd1.Manager",
2126
                                        "JobRemoved",
2127
                                        match_job_removed, NULL, &job_done_context);
2128
                        if (r < 0)
344✔
2129
                                return log_error_errno(r, "Failed to install JobRemove match: %m");
×
2130
                }
2131
        }
2132

2133
        r = make_transient_service_unit(bus, &m, service, pty_path, peer_fd);
1,051✔
2134
        if (r < 0)
1,051✔
2135
                return r;
2136
        peer_fd = safe_close(peer_fd);
1,047✔
2137

2138
        r = bus_call_with_hint(bus, m, "service", &reply);
1,047✔
2139
        if (r < 0)
1,047✔
2140
                return r;
2141

2142
        const char *object;
1,034✔
2143
        r = sd_bus_message_read(reply, "o", &object);
1,034✔
2144
        if (r < 0)
1,034✔
UNCOV
2145
                return bus_log_parse_error(r);
×
2146

2147
        if (w) {
1,034✔
2148
                r = bus_wait_for_jobs_one(w,
2,061✔
2149
                                          object,
2150
                                          arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
687✔
2151
                                          arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
687✔
2152
                if (r < 0)
687✔
2153
                        return r;
8✔
2154
        } else if (job_done_context.match) {
347✔
2155
                job_done_context.start_job = strdup(object);
343✔
2156
                if (!job_done_context.start_job)
343✔
UNCOV
2157
                        return log_oom();
×
2158
        }
2159

2160
        if (!arg_quiet) {
1,026✔
2161
                sd_id128_t invocation_id;
579✔
2162

2163
                r = acquire_invocation_id(bus, service, &invocation_id);
579✔
2164
                if (r < 0)
579✔
UNCOV
2165
                        return r;
×
2166

2167
                r = print_unit_invocation(service, invocation_id);
579✔
2168
                if (r < 0)
579✔
2169
                        return r;
2170
        }
2171

2172
        if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
1,026✔
2173
                _cleanup_(run_context_done) RunContext c = {
910✔
2174
                        .cpu_usage_nsec = NSEC_INFINITY,
2175
                        .memory_peak = UINT64_MAX,
2176
                        .memory_swap_peak = UINT64_MAX,
2177
                        .ip_ingress_bytes = UINT64_MAX,
2178
                        .ip_egress_bytes = UINT64_MAX,
2179
                        .io_read_bytes = UINT64_MAX,
2180
                        .io_write_bytes = UINT64_MAX,
2181
                        .inactive_exit_usec = USEC_INFINITY,
2182
                        .inactive_enter_usec = USEC_INFINITY,
2183
                };
2184

2185
                r = sd_event_default(&c.event);
910✔
2186
                if (r < 0)
910✔
UNCOV
2187
                        return log_error_errno(r, "Failed to get event loop: %m");
×
2188

2189
                c.service = strdup(service);
910✔
2190
                if (!c.service)
910✔
UNCOV
2191
                        return log_oom();
×
2192

2193
                c.bus_path = unit_dbus_path_from_name(service);
910✔
2194
                if (!c.bus_path)
910✔
UNCOV
2195
                        return log_oom();
×
2196

2197
                if (pty_fd >= 0) {
910✔
2198
                        (void) sd_event_set_signal_exit(c.event, true);
10✔
2199

2200
                        if (!arg_quiet)
10✔
2201
                                log_info("Press ^] three times within 1s to disconnect TTY.");
7✔
2202

2203
                        r = pty_forward_new(c.event, pty_fd, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &c.forward);
10✔
2204
                        if (r < 0)
10✔
UNCOV
2205
                                return log_error_errno(r, "Failed to create PTY forwarder: %m");
×
2206

2207
                        pty_forward_set_handler(c.forward, pty_forward_handler, &c);
10✔
2208

2209
                        /* Make sure to process any TTY events before we process bus events */
2210
                        (void) pty_forward_set_priority(c.forward, SD_EVENT_PRIORITY_IMPORTANT);
10✔
2211

2212
                        if (!isempty(arg_background))
10✔
UNCOV
2213
                                (void) pty_forward_set_background_color(c.forward, arg_background);
×
2214

2215
                        set_window_title(c.forward);
10✔
2216
                }
2217

2218
                r = run_context_attach_bus(&c, bus);
910✔
2219
                if (r < 0)
910✔
2220
                        return r;
2221

2222
                r = run_context_update(&c);
910✔
2223
                if (r < 0)
910✔
2224
                        return r;
2225

2226
                r = sd_event_loop(c.event);
910✔
2227
                if (r < 0)
910✔
UNCOV
2228
                        return log_error_errno(r, "Failed to run event loop: %m");
×
2229

2230
                if (arg_wait && !arg_quiet) {
910✔
2231

2232
                        if (!isempty(c.result))
401✔
2233
                                log_info("Finished with result: %s", strna(c.result));
401✔
2234

2235
                        if (c.exit_code > 0)
401✔
2236
                                log_info("Main processes terminated with: code=%s, status=%u/%s",
800✔
2237
                                         sigchld_code_to_string(c.exit_code),
2238
                                         c.exit_status,
2239
                                         strna(c.exit_code == CLD_EXITED ?
2240
                                               exit_status_to_string(c.exit_status, EXIT_STATUS_FULL) :
2241
                                               signal_to_string(c.exit_status)));
2242

2243
                        if (timestamp_is_set(c.inactive_enter_usec) &&
401✔
2244
                            timestamp_is_set(c.inactive_exit_usec) &&
401✔
2245
                            c.inactive_enter_usec > c.inactive_exit_usec)
2246
                                log_info("Service runtime: %s",
401✔
2247
                                         FORMAT_TIMESPAN(c.inactive_enter_usec - c.inactive_exit_usec, USEC_PER_MSEC));
2248

2249
                        if (c.cpu_usage_nsec != NSEC_INFINITY)
401✔
2250
                                log_info("CPU time consumed: %s",
401✔
2251
                                         FORMAT_TIMESPAN(DIV_ROUND_UP(c.cpu_usage_nsec, NSEC_PER_USEC), USEC_PER_MSEC));
2252

2253
                        if (c.memory_peak != UINT64_MAX) {
401✔
2254
                                const char *swap;
401✔
2255

2256
                                if (c.memory_swap_peak != UINT64_MAX)
401✔
2257
                                        swap = strjoina(" (swap: ", FORMAT_BYTES(c.memory_swap_peak), ")");
2,807✔
2258
                                else
2259
                                        swap = "";
2260

2261
                                log_info("Memory peak: %s%s", FORMAT_BYTES(c.memory_peak), swap);
401✔
2262
                        }
2263

2264
                        const char *ip_ingress = NULL, *ip_egress = NULL;
401✔
2265

2266
                        if (!IN_SET(c.ip_ingress_bytes, 0, UINT64_MAX))
401✔
UNCOV
2267
                                ip_ingress = strjoina(" received: ", FORMAT_BYTES(c.ip_ingress_bytes));
×
2268
                        if (!IN_SET(c.ip_egress_bytes, 0, UINT64_MAX))
401✔
UNCOV
2269
                                ip_egress = strjoina(" sent: ", FORMAT_BYTES(c.ip_egress_bytes));
×
2270

2271
                        if (ip_ingress || ip_egress)
401✔
UNCOV
2272
                                log_info("IP traffic%s%s", strempty(ip_ingress), strempty(ip_egress));
×
2273

2274
                        const char *io_read = NULL, *io_write = NULL;
401✔
2275

2276
                        if (!IN_SET(c.io_read_bytes, 0, UINT64_MAX))
401✔
UNCOV
2277
                                io_read = strjoina(" read: ", FORMAT_BYTES(c.io_read_bytes));
×
2278
                        if (!IN_SET(c.io_write_bytes, 0, UINT64_MAX))
401✔
UNCOV
2279
                                io_write = strjoina(" written: ", FORMAT_BYTES(c.io_write_bytes));
×
2280

2281
                        if (io_read || io_write)
401✔
UNCOV
2282
                                log_info("IO bytes%s%s", strempty(io_read), strempty(io_write));
×
2283
                }
2284

2285
                /* Try to propagate the service's return value. But if the service defines
2286
                 * e.g. SuccessExitStatus, honour this, and return 0 to mean "success". */
2287
                if (streq_ptr(c.result, "success"))
910✔
2288
                        return EXIT_SUCCESS;
2289
                if (streq_ptr(c.result, "exit-code") && c.exit_status > 0)
104✔
2290
                        return c.exit_status;
102✔
2291
                if (streq_ptr(c.result, "signal"))
2✔
2292
                        return EXIT_EXCEPTION;
2293
                return EXIT_FAILURE;
1✔
2294
        }
2295

2296
        return EXIT_SUCCESS;
2297
}
2298

2299
static int start_transient_scope(sd_bus *bus) {
19✔
2300
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2✔
2301
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
2✔
2302
        _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
2✔
2303
        _cleanup_free_ char *scope = NULL;
2✔
2304
        const char *object = NULL;
19✔
2305
        sd_id128_t invocation_id;
19✔
2306
        bool allow_pidfd = true;
19✔
2307
        int r;
19✔
2308

2309
        assert(bus);
19✔
2310
        assert(!strv_isempty(arg_cmdline));
19✔
2311

2312
        r = bus_wait_for_jobs_new(bus, &w);
19✔
2313
        if (r < 0)
19✔
UNCOV
2314
                return log_error_errno(r, "Could not watch jobs: %m");
×
2315

2316
        if (arg_unit) {
19✔
2317
                r = unit_name_mangle_with_suffix(arg_unit, "as unit",
6✔
2318
                                                 arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
6✔
2319
                                                 ".scope", &scope);
2320
                if (r < 0)
6✔
UNCOV
2321
                        return log_error_errno(r, "Failed to mangle scope name: %m");
×
2322
        } else {
2323
                r = make_unit_name(UNIT_SCOPE, &scope);
13✔
2324
                if (r < 0)
13✔
2325
                        return r;
2326
        }
2327

2328
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
19✔
2329

2330
        for (;;) {
×
UNCOV
2331
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
2332
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
19✔
2333

2334
                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
19✔
2335
                if (r < 0)
19✔
UNCOV
2336
                        return bus_log_create_error(r);
×
2337

2338
                /* Name and Mode */
2339
                r = sd_bus_message_append(m, "ss", scope, job_mode_to_string(arg_job_mode));
19✔
2340
                if (r < 0)
19✔
2341
                        return bus_log_create_error(r);
×
2342

2343
                /* Properties */
2344
                r = sd_bus_message_open_container(m, 'a', "(sv)");
19✔
2345
                if (r < 0)
19✔
UNCOV
2346
                        return bus_log_create_error(r);
×
2347

2348
                r = transient_scope_set_properties(m, allow_pidfd);
19✔
2349
                if (r < 0)
19✔
2350
                        return r;
2351

2352
                r = sd_bus_message_close_container(m);
19✔
2353
                if (r < 0)
19✔
UNCOV
2354
                        return bus_log_create_error(r);
×
2355

2356
                /* Auxiliary units */
2357
                r = sd_bus_message_append(m, "a(sa(sv))", 0);
19✔
2358
                if (r < 0)
19✔
2359
                        return bus_log_create_error(r);
×
2360

2361
                r = sd_bus_call(bus, m, 0, &error, &reply);
19✔
2362
                if (r < 0) {
19✔
2363
                        if (sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY) && allow_pidfd) {
2✔
2364
                                log_debug("Retrying with classic PIDs.");
×
UNCOV
2365
                                allow_pidfd = false;
×
2366
                                continue;
×
2367
                        }
2368

2369
                        return log_error_errno(r, "Failed to start transient scope unit: %s", bus_error_message(&error, r));
2✔
2370
                }
2371

2372
                break;
17✔
2373
        }
2374

2375
        r = sd_bus_message_read(reply, "o", &object);
17✔
2376
        if (r < 0)
17✔
UNCOV
2377
                return bus_log_parse_error(r);
×
2378

2379
        r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
17✔
2380
                                  arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
17✔
2381
        if (r < 0)
17✔
2382
                return r;
2383

2384
        r = acquire_invocation_id(bus, NULL, &invocation_id);
17✔
2385
        if (r < 0)
17✔
2386
                return r;
2387
        if (r == 0)
17✔
UNCOV
2388
                log_debug("No invocation ID set.");
×
2389
        else {
2390
                if (strv_extendf(&user_env, "INVOCATION_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(invocation_id)) < 0)
17✔
UNCOV
2391
                        return log_oom();
×
2392
        }
2393

2394
        if (arg_nice_set) {
17✔
UNCOV
2395
                if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
×
UNCOV
2396
                        return log_error_errno(errno, "Failed to set nice level: %m");
×
2397
        }
2398

2399
        if (arg_exec_group) {
17✔
UNCOV
2400
                gid_t gid;
×
2401

2402
                r = get_group_creds(&arg_exec_group, &gid, 0);
×
UNCOV
2403
                if (r < 0)
×
UNCOV
2404
                        return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
×
2405

2406
                if (setresgid(gid, gid, gid) < 0)
×
UNCOV
2407
                        return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
×
2408
        }
2409

2410
        if (arg_exec_user) {
17✔
2411
                const char *home, *shell;
1✔
2412
                uid_t uid;
1✔
2413
                gid_t gid;
1✔
2414

2415
                r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell,
1✔
2416
                                   USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS);
2417
                if (r < 0)
1✔
2418
                        return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
×
2419

2420
                if (home) {
1✔
2421
                        r = strv_extendf(&user_env, "HOME=%s", home);
1✔
2422
                        if (r < 0)
1✔
UNCOV
2423
                                return log_oom();
×
2424
                }
2425

2426
                if (shell) {
1✔
2427
                        r = strv_extendf(&user_env, "SHELL=%s", shell);
1✔
2428
                        if (r < 0)
1✔
UNCOV
2429
                                return log_oom();
×
2430
                }
2431

2432
                r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
1✔
2433
                if (r < 0)
1✔
UNCOV
2434
                        return log_oom();
×
2435

2436
                r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
1✔
2437
                if (r < 0)
1✔
UNCOV
2438
                        return log_oom();
×
2439

2440
                if (!arg_exec_group) {
1✔
2441
                        if (setresgid(gid, gid, gid) < 0)
1✔
2442
                                return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
×
2443
                }
2444

2445
                if (setresuid(uid, uid, uid) < 0)
1✔
UNCOV
2446
                        return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
×
2447
        }
2448

2449
        if (arg_working_directory && chdir(arg_working_directory) < 0)
17✔
UNCOV
2450
                return log_error_errno(errno, "Failed to change directory to '%s': %m", arg_working_directory);
×
2451

2452
        env = strv_env_merge(environ, user_env, arg_environment);
17✔
2453
        if (!env)
17✔
UNCOV
2454
                return log_oom();
×
2455

2456
        if (!arg_quiet) {
17✔
2457
                r = print_unit_invocation(scope, invocation_id);
17✔
2458
                if (r < 0)
17✔
2459
                        return r;
2460
        }
2461

2462
        if (arg_expand_environment) {
17✔
2463
                _cleanup_strv_free_ char **expanded_cmdline = NULL, **unset_variables = NULL, **bad_variables = NULL;
17✔
2464

2465
                r = replace_env_argv(arg_cmdline, env, &expanded_cmdline, &unset_variables, &bad_variables);
17✔
2466
                if (r < 0)
17✔
UNCOV
2467
                        return log_error_errno(r, "Failed to expand environment variables: %m");
×
2468

2469
                free_and_replace(arg_cmdline, expanded_cmdline);
17✔
2470

2471
                if (!strv_isempty(unset_variables)) {
17✔
UNCOV
2472
                        _cleanup_free_ char *ju = strv_join(unset_variables, ", ");
×
UNCOV
2473
                        log_warning("Referenced but unset environment variable evaluates to an empty string: %s", strna(ju));
×
2474
                }
2475

2476
                if (!strv_isempty(bad_variables)) {
17✔
UNCOV
2477
                        _cleanup_free_ char *jb = strv_join(bad_variables, ", ");
×
UNCOV
2478
                        log_warning("Invalid environment variable name evaluates to an empty string: %s", strna(jb));
×
2479
                }
2480
        }
2481

2482
        execvpe(arg_cmdline[0], arg_cmdline, env);
17✔
2483

UNCOV
2484
        return log_error_errno(errno, "Failed to execute: %m");
×
2485
}
2486

2487
static int make_transient_trigger_unit(
18✔
2488
                sd_bus *bus,
2489
                sd_bus_message **message,
2490
                const char *suffix,
2491
                const char *trigger,
2492
                const char *service) {
2493

2494
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
18✔
2495
        int r;
18✔
2496

2497
        assert(bus);
18✔
2498
        assert(message);
18✔
2499
        assert(suffix);
18✔
2500
        assert(trigger);
18✔
2501
        assert(service);
18✔
2502

2503
        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
18✔
2504
        if (r < 0)
18✔
UNCOV
2505
                return bus_log_create_error(r);
×
2506

2507
        /* Name and Mode */
2508
        r = sd_bus_message_append(m, "ss", trigger, job_mode_to_string(arg_job_mode));
18✔
2509
        if (r < 0)
18✔
UNCOV
2510
                return bus_log_create_error(r);
×
2511

2512
        /* Properties */
2513
        r = sd_bus_message_open_container(m, 'a', "(sv)");
18✔
2514
        if (r < 0)
18✔
UNCOV
2515
                return bus_log_create_error(r);
×
2516

2517
        if (streq(suffix, ".path"))
18✔
2518
                r = transient_unit_set_properties(m, UNIT_PATH, arg_path_property);
1✔
2519
        else if (streq(suffix, ".socket"))
17✔
2520
                r = transient_unit_set_properties(m, UNIT_SOCKET, arg_socket_property);
2✔
2521
        else if (streq(suffix, ".timer"))
15✔
2522
                r = transient_timer_set_properties(m);
15✔
2523
        else
UNCOV
2524
                assert_not_reached();
×
2525
        if (r < 0)
18✔
2526
                return r;
2527

2528
        r = sd_bus_message_close_container(m);
13✔
2529
        if (r < 0)
13✔
UNCOV
2530
                return bus_log_create_error(r);
×
2531

2532
        r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
13✔
2533
        if (r < 0)
13✔
UNCOV
2534
                return bus_log_create_error(r);
×
2535

2536
        if (!strv_isempty(arg_cmdline)) {
13✔
2537
                r = sd_bus_message_open_container(m, 'r', "sa(sv)");
13✔
2538
                if (r < 0)
13✔
UNCOV
2539
                        return bus_log_create_error(r);
×
2540

2541
                r = sd_bus_message_append(m, "s", service);
13✔
2542
                if (r < 0)
13✔
UNCOV
2543
                        return bus_log_create_error(r);
×
2544

2545
                r = sd_bus_message_open_container(m, 'a', "(sv)");
13✔
2546
                if (r < 0)
13✔
UNCOV
2547
                        return bus_log_create_error(r);
×
2548

2549
                r = transient_service_set_properties(m, /* pty_path = */ NULL, /* pty_fd = */ -EBADF);
13✔
2550
                if (r < 0)
13✔
2551
                        return r;
2552

2553
                r = sd_bus_message_close_container(m);
13✔
2554
                if (r < 0)
13✔
UNCOV
2555
                        return bus_log_create_error(r);
×
2556

2557
                r = sd_bus_message_close_container(m);
13✔
2558
                if (r < 0)
13✔
UNCOV
2559
                        return bus_log_create_error(r);
×
2560
        }
2561

2562
        r = sd_bus_message_close_container(m);
13✔
2563
        if (r < 0)
13✔
2564
                return bus_log_create_error(r);
×
2565

2566
        *message = TAKE_PTR(m);
13✔
2567
        return 0;
13✔
2568
}
2569

2570
static int start_transient_trigger(sd_bus *bus, const char *suffix) {
18✔
2571
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
31✔
UNCOV
2572
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
2573
        _cleanup_free_ char *trigger = NULL, *service = NULL;
18✔
2574
        const char *object = NULL;
18✔
2575
        int r;
18✔
2576

2577
        assert(bus);
18✔
2578
        assert(suffix);
18✔
2579

2580
        r = bus_wait_for_jobs_new(bus, &w);
18✔
2581
        if (r < 0)
18✔
2582
                return log_error_errno(r, "Could not watch jobs: %m");
×
2583

2584
        if (arg_unit) {
18✔
2585
                switch (unit_name_to_type(arg_unit)) {
6✔
2586

2587
                case UNIT_SERVICE:
1✔
2588
                        service = strdup(arg_unit);
1✔
2589
                        if (!service)
1✔
UNCOV
2590
                                return log_oom();
×
2591

2592
                        r = unit_name_change_suffix(service, suffix, &trigger);
1✔
2593
                        if (r < 0)
1✔
UNCOV
2594
                                return log_error_errno(r, "Failed to change unit suffix: %m");
×
2595
                        break;
2596

UNCOV
2597
                case UNIT_TIMER:
×
UNCOV
2598
                        trigger = strdup(arg_unit);
×
UNCOV
2599
                        if (!trigger)
×
UNCOV
2600
                                return log_oom();
×
2601

UNCOV
2602
                        r = unit_name_change_suffix(trigger, ".service", &service);
×
UNCOV
2603
                        if (r < 0)
×
UNCOV
2604
                                return log_error_errno(r, "Failed to change unit suffix: %m");
×
2605
                        break;
2606

2607
                default:
5✔
2608
                        r = unit_name_mangle_with_suffix(arg_unit, "as unit",
5✔
2609
                                                         arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
5✔
2610
                                                         ".service", &service);
2611
                        if (r < 0)
5✔
UNCOV
2612
                                return log_error_errno(r, "Failed to mangle unit name: %m");
×
2613

2614
                        r = unit_name_mangle_with_suffix(arg_unit, "as trigger",
5✔
2615
                                                         arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
5✔
2616
                                                         suffix, &trigger);
2617
                        if (r < 0)
5✔
UNCOV
2618
                                return log_error_errno(r, "Failed to mangle unit name: %m");
×
2619

2620
                        break;
2621
                }
2622
        } else {
2623
                r = make_unit_name(UNIT_SERVICE, &service);
12✔
2624
                if (r < 0)
12✔
2625
                        return r;
2626

2627
                r = unit_name_change_suffix(service, suffix, &trigger);
12✔
2628
                if (r < 0)
12✔
UNCOV
2629
                        return log_error_errno(r, "Failed to change unit suffix: %m");
×
2630
        }
2631

2632
        r = make_transient_trigger_unit(bus, &m, suffix, trigger, service);
18✔
2633
        if (r < 0)
18✔
2634
                return r;
2635

2636
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
13✔
2637

2638
        r = bus_call_with_hint(bus, m, suffix + 1, &reply);
13✔
2639
        if (r < 0)
13✔
2640
                return r;
2641

2642
        r = sd_bus_message_read(reply, "o", &object);
8✔
2643
        if (r < 0)
8✔
UNCOV
2644
                return bus_log_parse_error(r);
×
2645

2646
        r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
8✔
2647
                                  arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
8✔
2648
        if (r < 0)
8✔
2649
                return r;
2650

2651
        if (!arg_quiet) {
8✔
2652
                log_info("Running %s as unit: %s", suffix + 1, trigger);
8✔
2653
                if (!strv_isempty(arg_cmdline))
8✔
2654
                        log_info("Will run service as unit: %s", service);
18✔
2655
        }
2656

2657
        return EXIT_SUCCESS;
2658
}
2659

2660
static bool shall_make_executable_absolute(void) {
1,093✔
2661
        if (arg_exec_path)
1,093✔
2662
                return false;
2663
        if (strv_isempty(arg_cmdline))
1,093✔
2664
                return false;
2665
        if (arg_transport != BUS_TRANSPORT_LOCAL)
1,093✔
2666
                return false;
2667

2668
        FOREACH_STRING(f, "RootDirectory=", "RootImage=", "ExecSearchPath=", "MountImages=", "ExtensionImages=")
6,068✔
2669
                if (strv_find_startswith(arg_property, f))
5,083✔
2670
                        return false;
71✔
2671

2672
        return true;
985✔
2673
}
2674

2675
static int run(int argc, char* argv[]) {
1,105✔
2676
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1,088✔
2677
        int r;
1,105✔
2678

2679
        log_setup();
1,105✔
2680

2681
        if (invoked_as(argv, "run0"))
1,105✔
2682
                r = parse_argv_sudo_mode(argc, argv);
112✔
2683
        else
2684
                r = parse_argv(argc, argv);
993✔
2685
        if (r <= 0)
1,105✔
2686
                return r;
2687

2688
        if (shall_make_executable_absolute()) {
1,093✔
2689
                /* Patch in an absolute path to fail early for user convenience, but only when we can do it
2690
                 * (i.e. we will be running from the same file system). This also uses the user's $PATH,
2691
                 * while we use a fixed search path in the manager. */
2692

2693
                _cleanup_free_ char *command = NULL;
985✔
2694
                r = find_executable(arg_cmdline[0], &command);
985✔
2695
                if (ERRNO_IS_NEG_PRIVILEGE(r))
985✔
2696
                        log_debug_errno(r, "Failed to find executable '%s' due to permission problems, leaving path as is: %m", arg_cmdline[0]);
984✔
2697
                else if (r < 0)
985✔
2698
                        return log_error_errno(r, "Failed to find executable %s: %m", arg_cmdline[0]);
1✔
2699
                else
2700
                        free_and_replace(arg_cmdline[0], command);
984✔
2701
        }
2702

2703
        if (!arg_description) {
1,092✔
2704
                _cleanup_free_ char *t = NULL;
1,091✔
2705

2706
                if (strv_isempty(arg_cmdline))
1,091✔
UNCOV
2707
                        t = strdup(arg_unit);
×
2708
                else if (startswith(arg_cmdline[0], "-")) {
1,091✔
2709
                        /* Drop the login shell marker from the command line when generating the description,
2710
                         * in order to minimize user confusion. */
2711
                        _cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
1✔
2712
                        if (!l)
1✔
UNCOV
2713
                                return log_oom();
×
2714

2715
                        r = free_and_strdup_warn(l + 0, l[0] + 1);
1✔
2716
                        if (r < 0)
1✔
2717
                                return r;
2718

2719
                        t = quote_command_line(l, SHELL_ESCAPE_EMPTY);
1✔
2720
                } else
2721
                        t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY);
1,090✔
2722
                if (!t)
1,091✔
UNCOV
2723
                        return log_oom();
×
2724

2725
                arg_description = strjoin("[", program_invocation_short_name, "] ", t);
1,091✔
2726
                if (!arg_description)
1,091✔
UNCOV
2727
                        return log_oom();
×
2728
        }
2729

2730
        r = connect_bus(&bus);
1,092✔
2731
        if (r < 0)
1,092✔
2732
                return r;
2733

2734
        if (arg_scope)
1,088✔
2735
                return start_transient_scope(bus);
19✔
2736
        if (arg_path_property)
1,069✔
2737
                return start_transient_trigger(bus, ".path");
1✔
2738
        if (arg_socket_property)
1,068✔
2739
                return start_transient_trigger(bus, ".socket");
2✔
2740
        if (arg_with_timer)
1,066✔
2741
                return start_transient_trigger(bus, ".timer");
15✔
2742
        return start_transient_service(bus);
1,051✔
2743
}
2744

2745
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
2,210✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc