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

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

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

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

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

106
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
1,151✔
107
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
1,151✔
108
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
1,151✔
109
STATIC_DESTRUCTOR_REGISTER(arg_path_property, strv_freep);
1,151✔
110
STATIC_DESTRUCTOR_REGISTER(arg_socket_property, strv_freep);
1,151✔
111
STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
1,151✔
112
STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
1,151✔
113
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
1,151✔
114
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
1,151✔
115
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
1,151✔
116
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
1,151✔
117
STATIC_DESTRUCTOR_REGISTER(arg_area, freep);
1,151✔
118

119
static int help(void) {
1✔
120
        _cleanup_free_ char *link = NULL;
1✔
121
        int r;
1✔
122

123
        r = terminal_urlify_man("systemd-run", "1", &link);
1✔
124
        if (r < 0)
1✔
125
                return log_oom();
×
126

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

186
        return 0;
187
}
188

189
static int help_sudo_mode(void) {
×
190
        _cleanup_free_ char *link = NULL;
×
191
        int r;
×
192

193
        r = terminal_urlify_man("run0", "1", &link);
×
194
        if (r < 0)
×
195
                return log_oom();
×
196

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

201
        printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
×
202
               "\n%sElevate privileges interactively.%s\n\n"
203
               "  -h --help                       Show this help\n"
204
               "  -V --version                    Show package version\n"
205
               "     --no-ask-password            Do not prompt for password\n"
206
               "     --machine=CONTAINER          Operate on local container\n"
207
               "     --unit=UNIT                  Run under the specified unit name\n"
208
               "     --property=NAME=VALUE        Set service or scope unit property\n"
209
               "     --description=TEXT           Description for unit\n"
210
               "     --slice=SLICE                Run in the specified slice\n"
211
               "     --slice-inherit              Inherit the slice\n"
212
               "  -u --user=USER                  Run as system user\n"
213
               "  -g --group=GROUP                Run as system group\n"
214
               "     --nice=NICE                  Nice level\n"
215
               "  -D --chdir=PATH                 Set working directory\n"
216
               "     --setenv=NAME[=VALUE]        Set environment variable\n"
217
               "     --background=COLOR           Set ANSI color for background\n"
218
               "     --pty                        Request allocation of a pseudo TTY for stdio\n"
219
               "     --pty-late                   Just like --pty, but leave TTY access to agents\n"
220
               "                                  until unit is started up\n"
221
               "     --pipe                       Request direct pipe for stdio\n"
222
               "     --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
223
               "     --lightweight=BOOLEAN        Control whether to register a session with service manager\n"
224
               "                                  or without\n"
225
               "  -a --area=AREA                  Home area to log into\n"
226
               "\nSee the %s for details.\n",
227
               program_invocation_short_name,
228
               ansi_highlight(),
229
               ansi_normal(),
230
               link);
231

232
        return 0;
233
}
234

235
static bool privileged_execution(void) {
112✔
236
        if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
112✔
237
                return false;
112✔
238

239
        return !arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0");
123✔
240
}
241

242
static int add_timer_property(const char *name, const char *val) {
23✔
243
        char *p;
23✔
244

245
        assert(name);
23✔
246
        assert(val);
23✔
247

248
        p = strjoin(name, "=", val);
23✔
249
        if (!p)
23✔
250
                return log_oom();
×
251

252
        if (strv_consume(&arg_timer_property, p) < 0)
23✔
253
                return log_oom();
×
254

255
        return 0;
256
}
257

258
static char **make_login_shell_cmdline(const char *shell) {
×
259
        _cleanup_free_ char *argv0 = NULL;
×
260

261
        assert(shell);
×
262

263
        argv0 = strjoin("-", shell); /* The - is how shells determine if they shall be consider login shells */
×
264
        if (!argv0)
×
265
                return NULL;
266

267
        return strv_new(argv0);
×
268
}
269

270
static int parse_argv(int argc, char *argv[]) {
1,022✔
271

272
        enum {
1,022✔
273
                ARG_VERSION = 0x100,
274
                ARG_USER,
275
                ARG_SYSTEM,
276
                ARG_SCOPE,
277
                ARG_DESCRIPTION,
278
                ARG_SLICE,
279
                ARG_SLICE_INHERIT,
280
                ARG_EXPAND_ENVIRONMENT,
281
                ARG_SEND_SIGHUP,
282
                ARG_SERVICE_TYPE,
283
                ARG_EXEC_USER,
284
                ARG_EXEC_GROUP,
285
                ARG_NICE,
286
                ARG_ON_ACTIVE,
287
                ARG_ON_BOOT,
288
                ARG_ON_STARTUP,
289
                ARG_ON_UNIT_ACTIVE,
290
                ARG_ON_UNIT_INACTIVE,
291
                ARG_ON_CALENDAR,
292
                ARG_ON_TIMEZONE_CHANGE,
293
                ARG_ON_CLOCK_CHANGE,
294
                ARG_TIMER_PROPERTY,
295
                ARG_PATH_PROPERTY,
296
                ARG_SOCKET_PROPERTY,
297
                ARG_NO_BLOCK,
298
                ARG_NO_ASK_PASSWORD,
299
                ARG_WAIT,
300
                ARG_WORKING_DIRECTORY,
301
                ARG_SHELL,
302
                ARG_JOB_MODE,
303
                ARG_IGNORE_FAILURE,
304
                ARG_BACKGROUND,
305
                ARG_JSON,
306
        };
307

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

360
        bool with_trigger = false;
1,022✔
361
        int r, c;
1,022✔
362

363
        assert(argc >= 0);
1,022✔
364
        assert(argv);
1,022✔
365

366
        /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
367
         * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
368
        optind = 0;
1,022✔
369
        while ((c = getopt_long(argc, argv, "+hrC:H:M:E:p:tTPqGdSu:", options, NULL)) >= 0)
5,525✔
370

371
                switch (c) {
4,514✔
372

373
                case 'h':
1✔
374
                        return help();
11✔
375

376
                case ARG_VERSION:
1✔
377
                        return version();
1✔
378

379
                case ARG_NO_ASK_PASSWORD:
1✔
380
                        arg_ask_password = false;
1✔
381
                        break;
4,503✔
382

383
                case ARG_USER:
65✔
384
                        arg_runtime_scope = RUNTIME_SCOPE_USER;
65✔
385
                        break;
65✔
386

387
                case ARG_SYSTEM:
2✔
388
                        arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
2✔
389
                        break;
2✔
390

391
                case 'C':
1✔
392
                        r = capsule_name_is_valid(optarg);
1✔
393
                        if (r < 0)
1✔
394
                                return log_error_errno(r, "Unable to validate capsule name '%s': %m", optarg);
×
395
                        if (r == 0)
1✔
396
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capsule name: %s", optarg);
×
397

398
                        arg_host = optarg;
1✔
399
                        arg_transport = BUS_TRANSPORT_CAPSULE;
1✔
400
                        arg_runtime_scope = RUNTIME_SCOPE_USER;
1✔
401
                        break;
1✔
402

403
                case ARG_SCOPE:
19✔
404
                        arg_scope = true;
19✔
405
                        break;
19✔
406

407
                case 'u':
523✔
408
                        arg_unit = optarg;
523✔
409
                        break;
523✔
410

411
                case ARG_DESCRIPTION:
2✔
412
                        r = free_and_strdup_warn(&arg_description, optarg);
2✔
413
                        if (r < 0)
2✔
414
                                return r;
415
                        break;
416

417
                case ARG_SLICE:
30✔
418
                        arg_slice = optarg;
30✔
419
                        break;
30✔
420

421
                case ARG_SLICE_INHERIT:
4✔
422
                        arg_slice_inherit = true;
4✔
423
                        break;
4✔
424

425
                case ARG_EXPAND_ENVIRONMENT:
1✔
426
                        r = parse_boolean_argument("--expand-environment=", optarg, &arg_expand_environment);
1✔
427
                        if (r < 0)
1✔
428
                                return r;
429
                        break;
430

431
                case ARG_SEND_SIGHUP:
1✔
432
                        arg_send_sighup = true;
1✔
433
                        break;
1✔
434

435
                case 'r':
26✔
436
                        arg_remain_after_exit = true;
26✔
437
                        break;
26✔
438

439
                case 'H':
×
440
                        arg_transport = BUS_TRANSPORT_REMOTE;
×
441
                        arg_host = optarg;
×
442
                        break;
×
443

444
                case 'M':
43✔
445
                        arg_transport = BUS_TRANSPORT_MACHINE;
43✔
446
                        arg_host = optarg;
43✔
447
                        break;
43✔
448

449
                case ARG_SERVICE_TYPE:
31✔
450
                        arg_service_type = optarg;
31✔
451
                        break;
31✔
452

453
                case ARG_EXEC_USER:
11✔
454
                        arg_exec_user = optarg;
11✔
455
                        break;
11✔
456

457
                case ARG_EXEC_GROUP:
2✔
458
                        arg_exec_group = optarg;
2✔
459
                        break;
2✔
460

461
                case ARG_NICE:
3✔
462
                        r = parse_nice(optarg, &arg_nice);
3✔
463
                        if (r < 0)
3✔
464
                                return log_error_errno(r, "Failed to parse nice value: %s", optarg);
2✔
465

466
                        arg_nice_set = true;
1✔
467
                        break;
1✔
468

469
                case 'E':
8✔
470
                        r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
8✔
471
                        if (r < 0)
8✔
472
                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
2✔
473

474
                        break;
475

476
                case 'p':
2,326✔
477
                        if (strv_extend(&arg_property, optarg) < 0)
2,326✔
478
                                return log_oom();
×
479

480
                        break;
481

482
                case 'T': /* --pty-late */
8✔
483
                case 't': /* --pty */
484
                        arg_stdio |= ARG_STDIO_PTY;
8✔
485
                        arg_pty_late = c == 'T';
8✔
486
                        break;
8✔
487

488
                case 'P': /* --pipe */
239✔
489
                        arg_stdio |= ARG_STDIO_DIRECT;
239✔
490
                        break;
239✔
491

492
                case 'q':
343✔
493
                        arg_quiet = true;
343✔
494
                        break;
343✔
495

496
                case ARG_ON_ACTIVE:
4✔
497
                        r = add_timer_property("OnActiveSec", optarg);
4✔
498
                        if (r < 0)
4✔
499
                                return r;
500

501
                        arg_with_timer = true;
4✔
502
                        break;
4✔
503

504
                case ARG_ON_BOOT:
3✔
505
                        r = add_timer_property("OnBootSec", optarg);
3✔
506
                        if (r < 0)
3✔
507
                                return r;
508

509
                        arg_with_timer = true;
3✔
510
                        break;
3✔
511

512
                case ARG_ON_STARTUP:
3✔
513
                        r = add_timer_property("OnStartupSec", optarg);
3✔
514
                        if (r < 0)
3✔
515
                                return r;
516

517
                        arg_with_timer = true;
3✔
518
                        break;
3✔
519

520
                case ARG_ON_UNIT_ACTIVE:
3✔
521
                        r = add_timer_property("OnUnitActiveSec", optarg);
3✔
522
                        if (r < 0)
3✔
523
                                return r;
524

525
                        arg_with_timer = true;
3✔
526
                        break;
3✔
527

528
                case ARG_ON_UNIT_INACTIVE:
3✔
529
                        r = add_timer_property("OnUnitInactiveSec", optarg);
3✔
530
                        if (r < 0)
3✔
531
                                return r;
532

533
                        arg_with_timer = true;
3✔
534
                        break;
3✔
535

536
                case ARG_ON_CALENDAR: {
4✔
537
                        _cleanup_(calendar_spec_freep) CalendarSpec *cs = NULL;
2✔
538

539
                        r = calendar_spec_from_string(optarg, &cs);
4✔
540
                        if (r < 0)
4✔
541
                                return log_error_errno(r, "Failed to parse calendar event specification: %m");
2✔
542

543
                        /* Let's make sure the given calendar event is not in the past */
544
                        r = calendar_spec_next_usec(cs, now(CLOCK_REALTIME), NULL);
2✔
545
                        if (r == -ENOENT)
2✔
546
                                /* The calendar event is in the past — let's warn about this, but install it
547
                                 * anyway as is. The service manager will trigger the service right away.
548
                                 * Moreover, the server side might have a different clock or timezone than we
549
                                 * do, hence it should decide when or whether to run something. */
550
                                log_warning("Specified calendar expression is in the past, proceeding anyway.");
×
551
                        else if (r < 0)
2✔
552
                                return log_error_errno(r, "Failed to calculate next time calendar expression elapses: %m");
×
553

554
                        r = add_timer_property("OnCalendar", optarg);
2✔
555
                        if (r < 0)
2✔
556
                                return r;
557

558
                        arg_with_timer = true;
2✔
559
                        break;
2✔
560
                }
561

562
                case ARG_ON_TIMEZONE_CHANGE:
2✔
563
                        r = add_timer_property("OnTimezoneChange", "yes");
2✔
564
                        if (r < 0)
2✔
565
                                return r;
566

567
                        arg_with_timer = true;
2✔
568
                        break;
2✔
569

570
                case ARG_ON_CLOCK_CHANGE:
3✔
571
                        r = add_timer_property("OnClockChange", "yes");
3✔
572
                        if (r < 0)
3✔
573
                                return r;
574

575
                        arg_with_timer = true;
3✔
576
                        break;
3✔
577

578
                case ARG_TIMER_PROPERTY:
2✔
579

580
                        if (strv_extend(&arg_timer_property, optarg) < 0)
2✔
581
                                return log_oom();
×
582

583
                        arg_with_timer = arg_with_timer ||
3✔
584
                                STARTSWITH_SET(optarg,
1✔
585
                                               "OnActiveSec=",
586
                                               "OnBootSec=",
587
                                               "OnStartupSec=",
588
                                               "OnUnitActiveSec=",
589
                                               "OnUnitInactiveSec=",
590
                                               "OnCalendar=");
591
                        break;
2✔
592

593
                case ARG_PATH_PROPERTY:
3✔
594

595
                        if (strv_extend(&arg_path_property, optarg) < 0)
3✔
596
                                return log_oom();
×
597

598
                        break;
599

600
                case ARG_SOCKET_PROPERTY:
4✔
601

602
                        if (strv_extend(&arg_socket_property, optarg) < 0)
4✔
603
                                return log_oom();
×
604

605
                        break;
606

607
                case ARG_NO_BLOCK:
4✔
608
                        arg_no_block = true;
4✔
609
                        break;
4✔
610

611
                case ARG_WAIT:
770✔
612
                        arg_wait = true;
770✔
613
                        break;
770✔
614

615
                case ARG_WORKING_DIRECTORY:
2✔
616
                        r = parse_path_argument(optarg, true, &arg_working_directory);
2✔
617
                        if (r < 0)
2✔
618
                                return r;
619

620
                        break;
621

622
                case 'd': {
1✔
623
                        _cleanup_free_ char *p = NULL;
1✔
624

625
                        r = safe_getcwd(&p);
1✔
626
                        if (r < 0)
1✔
627
                                return log_error_errno(r, "Failed to get current working directory: %m");
×
628

629
                        if (empty_or_root(p))
1✔
630
                                arg_working_directory = mfree(arg_working_directory);
1✔
631
                        else
632
                                free_and_replace(arg_working_directory, p);
×
633
                        break;
1✔
634
                }
635

636
                case 'G':
2✔
637
                        arg_aggressive_gc = true;
2✔
638
                        break;
2✔
639

640
                case 'S':
3✔
641
                        arg_shell = true;
3✔
642
                        break;
3✔
643

644
                case ARG_JOB_MODE:
4✔
645
                        if (streq(optarg, "help"))
4✔
646
                                return DUMP_STRING_TABLE(job_mode, JobMode, _JOB_MODE_MAX);
10✔
647

648
                        r = job_mode_from_string(optarg);
3✔
649
                        if (r < 0)
3✔
650
                                return log_error_errno(r, "Invalid job mode: %s", optarg);
1✔
651

652
                        arg_job_mode = r;
2✔
653
                        break;
2✔
654

655
                case ARG_IGNORE_FAILURE:
×
656
                        arg_ignore_failure = true;
×
657
                        break;
×
658

659
                case ARG_BACKGROUND:
×
660
                        r = free_and_strdup_warn(&arg_background, optarg);
×
661
                        if (r < 0)
×
662
                                return r;
663
                        break;
664

665
                case ARG_JSON:
2✔
666
                        r = parse_json_argument(optarg, &arg_json_format_flags);
2✔
667
                        if (r <= 0)
2✔
668
                                return r;
669
                        break;
670

671
                case '?':
672
                        return -EINVAL;
673

674
                default:
×
675
                        assert_not_reached();
×
676
                }
677

678
        /* If we are talking to the per-user instance PolicyKit isn't going to help */
679
        if (arg_runtime_scope == RUNTIME_SCOPE_USER)
1,011✔
680
                arg_ask_password = false;
66✔
681

682
        with_trigger = !!arg_path_property || !!arg_socket_property || arg_with_timer;
1,011✔
683

684
        /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
685
        if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) arg_with_timer > 1)
1,011✔
686
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
687
                                       "Only single trigger (path, socket, timer) unit can be created.");
688

689
        if (arg_shell) {
1,011✔
690
                /* If --shell is imply --pty --pipe --same-dir --service-type=exec --wait --collect, unless otherwise
691
                 * specified. */
692

693
                if (!arg_scope) {
3✔
694
                        if (arg_stdio == ARG_STDIO_NONE)
2✔
695
                                arg_stdio = ARG_STDIO_AUTO;
2✔
696

697
                        if (!arg_working_directory) {
2✔
698
                                r = safe_getcwd(&arg_working_directory);
2✔
699
                                if (r < 0)
2✔
700
                                        return log_error_errno(r, "Failed to get current working directory: %m");
×
701
                        }
702

703
                        if (!arg_service_type)
2✔
704
                                arg_service_type = "exec";
2✔
705

706
                        arg_wait = true;
2✔
707
                }
708

709
                arg_aggressive_gc = true;
3✔
710
        }
711

712
        if (arg_stdio == ARG_STDIO_AUTO)
1,011✔
713
                /* If we both --pty/--pty-late and --pipe are specified we'll automatically pick --pty/--pty-late if we
714
                 * are connected fully to a TTY and pick direct fd passing otherwise. This way, we
715
                 * automatically adapt to usage in a shell pipeline, but we are neatly interactive with
716
                 * tty-level isolation otherwise. */
717
                arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ?
4✔
718
                        ARG_STDIO_PTY :
2✔
719
                        ARG_STDIO_DIRECT;
720

721
        if (arg_pty_late < 0)
1,011✔
722
                arg_pty_late = false; /* For systemd-run this defaults to false, for compat reasons */
1,003✔
723

724
        if (argc > optind) {
1,011✔
725
                char **l;
1,007✔
726

727
                if (arg_shell)
1,007✔
728
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "If --shell is used, no command line is expected.");
1,022✔
729

730
                l = strv_copy(argv + optind);
1,007✔
731
                if (!l)
1,007✔
732
                        return log_oom();
×
733

734
                strv_free_and_replace(arg_cmdline, l);
1,007✔
735

736
        } else if (arg_shell) {
4✔
737
                _cleanup_free_ char *s = NULL;
3✔
738
                char **l;
3✔
739

740
                r = get_shell(&s);
3✔
741
                if (r < 0)
3✔
742
                        return log_error_errno(r, "Failed to determine shell: %m");
×
743

744
                l = strv_new(s);
3✔
745
                if (!l)
3✔
746
                        return log_oom();
×
747

748
                strv_free_and_replace(arg_cmdline, l);
3✔
749

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

753
        if (arg_runtime_scope == RUNTIME_SCOPE_USER && arg_transport == BUS_TRANSPORT_REMOTE)
1,010✔
754
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
755
                                       "Execution in user context is not supported on remote systems.");
756

757
        if (arg_scope && arg_transport == BUS_TRANSPORT_REMOTE)
1,010✔
758
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
759
                                       "Scope execution is not supported on remote systems.");
760

761
        if (arg_scope && (arg_remain_after_exit || arg_service_type))
1,010✔
762
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
763
                                       "--remain-after-exit and --service-type= are not supported in --scope mode.");
764

765
        if (arg_stdio != ARG_STDIO_NONE) {
1,010✔
766
                if (with_trigger || arg_scope)
249✔
767
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
768
                                               "--pty/--pty-late/--pipe is not compatible in trigger (path/socket/timer units) or --scope mode.");
769

770
                if (arg_transport == BUS_TRANSPORT_REMOTE)
249✔
771
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
772
                                               "--pty/--pty-late/--pipe is only supported when connecting to the local system or containers.");
773

774
                if (arg_no_block)
249✔
775
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
776
                                               "--pty/--pty-late/--pipe is not compatible with --no-block.");
777
        }
778

779
        if (arg_stdio == ARG_STDIO_PTY && arg_pty_late && streq_ptr(arg_service_type, "oneshot"))
1,010✔
780
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
781
                                       "--pty-late is not compatible with --service-type=oneshot.");
782

783
        if (arg_scope && with_trigger)
1,010✔
784
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
785
                                       "Path, socket or timer options are not supported in --scope mode.");
786

787
        if (arg_timer_property && !arg_with_timer)
1,010✔
788
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
789
                                       "--timer-property= has no effect without any other timer options.");
790

791
        if (arg_wait) {
1,010✔
792
                if (arg_no_block)
772✔
793
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
794
                                               "--wait may not be combined with --no-block.");
795

796
                if (with_trigger)
772✔
797
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
798
                                               "--wait may not be combined with path, socket or timer operations.");
799

800
                if (arg_scope)
772✔
801
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
802
                                               "--wait may not be combined with --scope.");
803
        }
804

805
        return 1;
806
}
807

808
static int parse_argv_sudo_mode(int argc, char *argv[]) {
146✔
809

810
        enum {
146✔
811
                ARG_NO_ASK_PASSWORD = 0x100,
812
                ARG_HOST,
813
                ARG_MACHINE,
814
                ARG_UNIT,
815
                ARG_PROPERTY,
816
                ARG_DESCRIPTION,
817
                ARG_SLICE,
818
                ARG_SLICE_INHERIT,
819
                ARG_NICE,
820
                ARG_SETENV,
821
                ARG_BACKGROUND,
822
                ARG_PTY,
823
                ARG_PTY_LATE,
824
                ARG_PIPE,
825
                ARG_SHELL_PROMPT_PREFIX,
826
                ARG_LIGHTWEIGHT,
827
        };
828

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

832
        static const struct option options[] = {
146✔
833
                { "help",                no_argument,       NULL, 'h'                     },
834
                { "version",             no_argument,       NULL, 'V'                     },
835
                { "no-ask-password",     no_argument,       NULL, ARG_NO_ASK_PASSWORD     },
836
                { "machine",             required_argument, NULL, ARG_MACHINE             },
837
                { "unit",                required_argument, NULL, ARG_UNIT                },
838
                { "property",            required_argument, NULL, ARG_PROPERTY            },
839
                { "description",         required_argument, NULL, ARG_DESCRIPTION         },
840
                { "slice",               required_argument, NULL, ARG_SLICE               },
841
                { "slice-inherit",       no_argument,       NULL, ARG_SLICE_INHERIT       },
842
                { "user",                required_argument, NULL, 'u'                     },
843
                { "group",               required_argument, NULL, 'g'                     },
844
                { "nice",                required_argument, NULL, ARG_NICE                },
845
                { "chdir",               required_argument, NULL, 'D'                     },
846
                { "setenv",              required_argument, NULL, ARG_SETENV              },
847
                { "background",          required_argument, NULL, ARG_BACKGROUND          },
848
                { "pty",                 no_argument,       NULL, ARG_PTY                 },
849
                { "pty-late",            no_argument,       NULL, ARG_PTY_LATE            },
850
                { "pipe",                no_argument,       NULL, ARG_PIPE                },
851
                { "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
852
                { "lightweight",         required_argument, NULL, ARG_LIGHTWEIGHT         },
853
                { "area",                required_argument, NULL, 'a'                     },
854
                {},
855
        };
856

857
        int r, c;
146✔
858

859
        assert(argc >= 0);
146✔
860
        assert(argv);
146✔
861

862
        /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
863
         * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
864
        optind = 0;
146✔
865
        while ((c = getopt_long(argc, argv, "+hVu:g:D:a:", options, NULL)) >= 0)
347✔
866

867
                switch (c) {
201✔
868

869
                case 'h':
×
870
                        return help_sudo_mode();
146✔
871

872
                case 'V':
×
873
                        return version();
×
874

875
                case ARG_NO_ASK_PASSWORD:
×
876
                        arg_ask_password = false;
×
877
                        break;
×
878

879
                case ARG_MACHINE:
×
880
                        arg_transport = BUS_TRANSPORT_MACHINE;
×
881
                        arg_host = optarg;
×
882
                        break;
×
883

884
                case ARG_UNIT:
13✔
885
                        arg_unit = optarg;
13✔
886
                        break;
13✔
887

888
                case ARG_PROPERTY:
48✔
889
                        if (strv_extend(&arg_property, optarg) < 0)
48✔
890
                                return log_oom();
×
891

892
                        break;
893

894
                case ARG_DESCRIPTION:
×
895
                        r = free_and_strdup_warn(&arg_description, optarg);
×
896
                        if (r < 0)
×
897
                                return r;
898
                        break;
899

900
                case ARG_SLICE:
×
901
                        arg_slice = optarg;
×
902
                        break;
×
903

904
                case ARG_SLICE_INHERIT:
×
905
                        arg_slice_inherit = true;
×
906
                        break;
×
907

908
                case 'u':
87✔
909
                        arg_exec_user = optarg;
87✔
910
                        break;
87✔
911

912
                case 'g':
×
913
                        arg_exec_group = optarg;
×
914
                        break;
×
915

916
                case ARG_NICE:
×
917
                        r = parse_nice(optarg, &arg_nice);
×
918
                        if (r < 0)
×
919
                                return log_error_errno(r, "Failed to parse nice value: %s", optarg);
×
920

921
                        arg_nice_set = true;
×
922
                        break;
×
923

924
                case 'D':
4✔
925
                        /* Root will be manually suppressed later. */
926
                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_working_directory);
4✔
927
                        if (r < 0)
4✔
928
                                return r;
929

930
                        break;
931

932
                case ARG_SETENV:
9✔
933
                        r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
9✔
934
                        if (r < 0)
9✔
935
                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
×
936

937
                        break;
938

939
                case ARG_BACKGROUND:
×
940
                        r = free_and_strdup_warn(&arg_background, optarg);
×
941
                        if (r < 0)
×
942
                                return r;
943

944
                        break;
945

946
                case ARG_PTY:
3✔
947
                case ARG_PTY_LATE:
948
                        arg_stdio |= ARG_STDIO_PTY;
3✔
949
                        arg_pty_late = c == ARG_PTY_LATE;
3✔
950
                        break;
3✔
951

952
                case ARG_PIPE:
8✔
953
                        arg_stdio |= ARG_STDIO_DIRECT;
8✔
954
                        break;
8✔
955

956
                case ARG_SHELL_PROMPT_PREFIX:
×
957
                        r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg);
×
958
                        if (r < 0)
×
959
                                return r;
960
                        break;
961

962
                case ARG_LIGHTWEIGHT:
2✔
963
                        r = parse_tristate_argument("--lightweight=", optarg, &arg_lightweight);
2✔
964
                        if (r < 0)
2✔
965
                                return r;
966
                        break;
967

968
                case 'a':
27✔
969
                        /* We allow an empty --area= specification to allow logging into the primary home directory */
970
                        if (!isempty(optarg) && !filename_is_valid(optarg))
54✔
971
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid area name, refusing: %s", optarg);
×
972

973
                        r = free_and_strdup_warn(&arg_area, optarg);
27✔
974
                        if (r < 0)
27✔
975
                                return r;
976

977
                        break;
978

979
                case '?':
980
                        return -EINVAL;
981

982
                default:
×
983
                        assert_not_reached();
×
984
                }
985

986
        if (!arg_exec_user && arg_area) {
146✔
987
                /* If the user specifies --area= but not --user= then consider this an area switch request,
988
                 * and default to logging into our own account */
989
                arg_exec_user = getusername_malloc();
×
990
                if (!arg_exec_user)
×
991
                        return log_oom();
×
992
        }
993

994
        if (!arg_working_directory) {
146✔
995
                if (arg_exec_user) {
142✔
996
                        /* When switching to a specific user, also switch to its home directory. */
997
                        arg_working_directory = strdup("~");
85✔
998
                        if (!arg_working_directory)
85✔
999
                                return log_oom();
×
1000
                } else {
1001
                        /* When switching to root without this being specified, then stay in the current directory */
1002
                        r = safe_getcwd(&arg_working_directory);
57✔
1003
                        if (r < 0)
57✔
1004
                                return log_error_errno(r, "Failed to get current working directory: %m");
×
1005
                }
1006
        } else {
1007
                /* Root was not suppressed earlier, to allow the above check to work properly. */
1008
                if (empty_or_root(arg_working_directory))
4✔
1009
                        arg_working_directory = mfree(arg_working_directory);
2✔
1010
        }
1011

1012
        arg_service_type = "exec";
146✔
1013
        arg_quiet = true;
146✔
1014
        arg_wait = true;
146✔
1015
        arg_aggressive_gc = true;
146✔
1016

1017
        if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO))
146✔
1018
                arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
137✔
1019
        log_debug("Using %s stdio mode.", arg_stdio == ARG_STDIO_PTY ? "pty" : "direct");
212✔
1020
        if (arg_pty_late < 0)
146✔
1021
                arg_pty_late = arg_ask_password; /* for run0 this defaults to on, except if --no-ask-pasword is used */
143✔
1022

1023
        arg_expand_environment = false;
146✔
1024
        arg_send_sighup = true;
146✔
1025

1026
        _cleanup_strv_free_ char **l = NULL;
146✔
1027
        if (argc > optind)
146✔
1028
                l = strv_copy(argv + optind);
146✔
1029
        else {
1030
                const char *e;
×
1031

1032
                e = strv_env_get(arg_environment, "SHELL");
×
1033
                if (e) {
×
1034
                        arg_exec_path = strdup(e);
×
1035
                        if (!arg_exec_path)
×
1036
                                return log_oom();
×
1037
                } else {
1038
                        if (arg_transport == BUS_TRANSPORT_LOCAL) {
×
1039
                                r = get_shell(&arg_exec_path);
×
1040
                                if (r < 0)
×
1041
                                        return log_error_errno(r, "Failed to determine shell: %m");
×
1042
                        } else {
1043
                                arg_exec_path = strdup("/bin/sh");
×
1044
                                if (!arg_exec_path)
×
1045
                                        return log_oom();
×
1046
                        }
1047

1048
                        r = strv_env_assign(&arg_environment, "SHELL", arg_exec_path);
×
1049
                        if (r < 0)
×
1050
                                return log_error_errno(r, "Failed to set $SHELL environment variable: %m");
×
1051
                }
1052

1053
                l = make_login_shell_cmdline(arg_exec_path);
×
1054
        }
1055
        if (!l)
146✔
1056
                return log_oom();
×
1057

1058
        strv_free_and_replace(arg_cmdline, l);
146✔
1059

1060
        if (!arg_slice) {
146✔
1061
                arg_slice = strdup(SPECIAL_USER_SLICE);
146✔
1062
                if (!arg_slice)
146✔
1063
                        return log_oom();
×
1064
        }
1065

1066
        _cleanup_free_ char *un = NULL;
146✔
1067
        un = getusername_malloc();
146✔
1068
        if (!un)
146✔
1069
                return log_oom();
×
1070

1071
        /* Set a bunch of environment variables in a roughly sudo-compatible way */
1072
        r = strv_env_assign(&arg_environment, "SUDO_USER", un);
146✔
1073
        if (r < 0)
146✔
1074
                return log_error_errno(r, "Failed to set $SUDO_USER environment variable: %m");
×
1075

1076
        r = strv_env_assignf(&arg_environment, "SUDO_UID", UID_FMT, getuid());
146✔
1077
        if (r < 0)
146✔
1078
                return log_error_errno(r, "Failed to set $SUDO_UID environment variable: %m");
×
1079

1080
        r = strv_env_assignf(&arg_environment, "SUDO_GID", GID_FMT, getgid());
146✔
1081
        if (r < 0)
146✔
1082
                return log_error_errno(r, "Failed to set $SUDO_GID environment variable: %m");
×
1083

1084
        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_UID=" UID_FMT, getuid()) < 0)
146✔
1085
                return log_oom();
×
1086

1087
        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_GID=" GID_FMT, getgid()) < 0)
146✔
1088
                return log_oom();
×
1089

1090
        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_USER=%s", un) < 0)
146✔
1091
                return log_oom();
×
1092

1093
        if (strv_extend(&arg_property, "PAMName=systemd-run0") < 0)
146✔
1094
                return log_oom();
×
1095

1096
        if (!arg_background && arg_stdio == ARG_STDIO_PTY && shall_tint_background()) {
146✔
1097
                double hue;
2✔
1098

1099
                if (privileged_execution())
2✔
1100
                        hue = 0; /* red */
1101
                else
1102
                        hue = 60 /* yellow */;
×
1103

1104
                r = terminal_tint_color(hue, &arg_background);
2✔
1105
                if (r < 0)
2✔
1106
                        log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
2✔
1107
        }
1108

1109
        if (!arg_shell_prompt_prefix) {
146✔
1110
                const char *e = secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
146✔
1111
                if (e) {
146✔
1112
                        arg_shell_prompt_prefix = strdup(e);
×
1113
                        if (!arg_shell_prompt_prefix)
×
1114
                                return log_oom();
×
1115
                } else if (emoji_enabled()) {
146✔
1116
                        arg_shell_prompt_prefix = strjoin(glyph(privileged_execution() ? GLYPH_SUPERHERO : GLYPH_IDCARD), " ");
×
1117
                        if (!arg_shell_prompt_prefix)
×
1118
                                return log_oom();
×
1119
                }
1120
        }
1121

1122
        if (!isempty(arg_shell_prompt_prefix)) {
146✔
1123
                r = strv_env_assign(&arg_environment, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix);
×
1124
                if (r < 0)
×
1125
                        return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
×
1126
        }
1127

1128
        if (!strv_env_get(arg_environment, "XDG_SESSION_CLASS")) {
146✔
1129

1130
                /* If logging into an area, imply lightweight mode */
1131
                if (arg_lightweight < 0 && !isempty(arg_area))
137✔
1132
                        arg_lightweight = true;
27✔
1133

1134
                /* When using run0 to acquire privileges temporarily, let's not pull in session manager by
1135
                 * default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege
1136
                 * escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session
1137
                 * manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do
1138
                 * this for root though, under the assumption that if a regular user temporarily transitions into
1139
                 * another regular user it's a better default that the full user environment is uniformly
1140
                 * available. */
1141
                if (arg_lightweight < 0 && privileged_execution())
137✔
1142
                        arg_lightweight = true;
70✔
1143

1144
                if (arg_lightweight >= 0) {
137✔
1145
                        const char *class =
198✔
1146
                                arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") :
99✔
1147
                                                  (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background");
1✔
1148

1149
                        log_debug("Setting XDG_SESSION_CLASS to '%s'.", class);
99✔
1150

1151
                        r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class);
99✔
1152
                        if (r < 0)
99✔
1153
                                return log_error_errno(r, "Failed to set $XDG_SESSION_CLASS environment variable: %m");
×
1154
                }
1155
        }
1156

1157
        if (arg_area) {
146✔
1158
                r = strv_env_assign(&arg_environment, "XDG_AREA", arg_area);
27✔
1159
                if (r < 0)
27✔
1160
                        return log_error_errno(r, "Failed to set $XDG_AREA environment variable: %m");
×
1161
        }
1162

1163
        return 1;
1164
}
1165

1166
static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
1,164✔
1167
        int r;
1,164✔
1168

1169
        assert(m);
1,164✔
1170

1171
        r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
1,164✔
1172
        if (r < 0)
1,164✔
1173
                return bus_log_create_error(r);
×
1174

1175
        if (arg_aggressive_gc) {
1,164✔
1176
                r = sd_bus_message_append(m, "(sv)", "CollectMode", "s", "inactive-or-failed");
151✔
1177
                if (r < 0)
151✔
1178
                        return bus_log_create_error(r);
×
1179
        }
1180

1181
        r = sd_bus_is_bus_client(sd_bus_message_get_bus(m));
1,164✔
1182
        if (r < 0)
1,164✔
1183
                return log_error_errno(r, "Can't determine if bus connection is direct or to broker: %m");
×
1184
        if (r > 0) {
1,164✔
1185
                /* Pin the object as least as long as we are around. Note that AddRef (currently) only works
1186
                 * if we talk via the bus though. */
1187
                r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
981✔
1188
                if (r < 0)
981✔
1189
                        return bus_log_create_error(r);
×
1190
        }
1191

1192
        return bus_append_unit_property_assignment_many(m, t, properties);
1,164✔
1193
}
1194

1195
static int transient_cgroup_set_properties(sd_bus_message *m) {
1,142✔
1196
        _cleanup_free_ char *name = NULL;
2,284✔
1197
        _cleanup_free_ char *slice = NULL;
1,142✔
1198
        int r;
1,142✔
1199

1200
        assert(m);
1,142✔
1201

1202
        if (arg_slice_inherit) {
1,142✔
1203
                char *end;
4✔
1204

1205
                switch (arg_runtime_scope) {
4✔
1206

1207
                case RUNTIME_SCOPE_USER:
×
1208
                        r = cg_pid_get_user_slice(0, &name);
×
1209
                        break;
1210

1211
                case RUNTIME_SCOPE_SYSTEM:
4✔
1212
                        r = cg_pid_get_slice(0, &name);
4✔
1213
                        break;
1214

1215
                default:
×
1216
                        assert_not_reached();
×
1217
                }
1218

1219
                if (r < 0)
4✔
1220
                        return log_error_errno(r, "Failed to get PID slice: %m");
×
1221

1222
                end = endswith(name, ".slice");
4✔
1223
                if (!end)
4✔
1224
                        return -ENXIO;
1225
                *end = 0;
4✔
1226
        }
1227

1228
        if (!isempty(arg_slice) && !strextend_with_separator(&name, "-", arg_slice))
1,318✔
1229
                return log_oom();
×
1230

1231
        if (!name)
1,142✔
1232
                return 0;
1233

1234
        r = unit_name_mangle_with_suffix(name, "as slice",
178✔
1235
                                         arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
178✔
1236
                                         ".slice", &slice);
1237
        if (r < 0)
178✔
1238
                return log_error_errno(r, "Failed to mangle name '%s': %m", arg_slice);
×
1239

1240
        r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
178✔
1241
        if (r < 0)
178✔
1242
                return bus_log_create_error(r);
×
1243

1244
        return 0;
1245
}
1246

1247
static int transient_kill_set_properties(sd_bus_message *m) {
1,142✔
1248
        int r;
1,142✔
1249

1250
        assert(m);
1,142✔
1251

1252
        if (arg_send_sighup) {
1,142✔
1253
                r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
147✔
1254
                if (r < 0)
147✔
1255
                        return bus_log_create_error(r);
×
1256
        }
1257

1258
        return 0;
1259
}
1260

1261
static int transient_service_set_properties(sd_bus_message *m, const char *pty_path, int pty_fd) {
1,127✔
1262
        int r, send_term; /* tri-state */
1,127✔
1263

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

1269
        assert(m);
1,127✔
1270
        assert((!!pty_path) == (pty_fd >= 0));
1,127✔
1271

1272
        r = transient_unit_set_properties(m, UNIT_SERVICE, arg_property);
1,127✔
1273
        if (r < 0)
1,127✔
1274
                return r;
1275

1276
        r = transient_kill_set_properties(m);
1,123✔
1277
        if (r < 0)
1,123✔
1278
                return r;
1279

1280
        r = transient_cgroup_set_properties(m);
1,123✔
1281
        if (r < 0)
1,123✔
1282
                return r;
1283

1284
        if (arg_remain_after_exit) {
1,123✔
1285
                r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
26✔
1286
                if (r < 0)
26✔
1287
                        return bus_log_create_error(r);
×
1288
        }
1289

1290
        if (arg_service_type) {
1,123✔
1291
                r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
178✔
1292
                if (r < 0)
178✔
1293
                        return bus_log_create_error(r);
×
1294
        }
1295

1296
        if (arg_exec_user) {
1,123✔
1297
                r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
97✔
1298
                if (r < 0)
97✔
1299
                        return bus_log_create_error(r);
×
1300
        }
1301

1302
        if (arg_exec_group) {
1,123✔
1303
                r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
2✔
1304
                if (r < 0)
2✔
1305
                        return bus_log_create_error(r);
×
1306
        }
1307

1308
        if (arg_nice_set) {
1,123✔
1309
                r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
1✔
1310
                if (r < 0)
1✔
1311
                        return bus_log_create_error(r);
×
1312
        }
1313

1314
        if (arg_working_directory) {
1,123✔
1315
                r = sd_bus_message_append(m, "(sv)", "WorkingDirectory", "s", arg_working_directory);
147✔
1316
                if (r < 0)
147✔
1317
                        return bus_log_create_error(r);
×
1318
        }
1319

1320
        if (pty_path) {
1,123✔
1321
                r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
10✔
1322
                                          "TTYPath", "s", pty_path,
1323
                                          "StandardInputFileDescriptor", "h", pty_fd,
1324
                                          "StandardOutputFileDescriptor", "h", pty_fd,
1325
                                          "StandardErrorFileDescriptor", "h", pty_fd);
1326
                if (r < 0)
10✔
1327
                        return bus_log_create_error(r);
×
1328

1329
                send_term = true;
1330

1331
        } else if (arg_stdio == ARG_STDIO_DIRECT) {
1,113✔
1332
                r = sd_bus_message_append(m,
381✔
1333
                                          "(sv)(sv)(sv)",
1334
                                          "StandardInputFileDescriptor", "h", STDIN_FILENO,
1335
                                          "StandardOutputFileDescriptor", "h", STDOUT_FILENO,
1336
                                          "StandardErrorFileDescriptor", "h", STDERR_FILENO);
1337
                if (r < 0)
381✔
1338
                        return bus_log_create_error(r);
×
1339

1340
                send_term = -1;
1341
        } else
1342
                send_term = false;
1343

1344
        if (send_term != 0) {
1345
                const char *e, *colorterm = NULL, *no_color = NULL;
391✔
1346

1347
                /* Propagate $TERM + $COLORTERM + $NO_COLOR if we are actually connected to a TTY */
1348
                if (isatty_safe(STDIN_FILENO) || isatty_safe(STDOUT_FILENO) || isatty_safe(STDERR_FILENO)) {
391✔
1349
                        e = strv_find_prefix(environ, "TERM=");
×
1350
                        send_term = !!e;
×
1351

1352
                        if (send_term) {
×
1353
                                /* If we send $TERM along, then also propagate $COLORTERM + $NO_COLOR right with it */
1354
                                colorterm = strv_find_prefix(environ, "COLORTERM=");
×
1355
                                no_color = strv_find_prefix(environ, "NO_COLOR=");
×
1356
                        }
1357
                } else
1358
                        /* If we are not connected to any TTY ourselves, then send TERM=dumb, but only if we
1359
                         * really need to (because we actually allocated a TTY for the service) */
1360
                        e = "TERM=dumb";
1361

1362
                if (send_term > 0) {
391✔
1363
                        r = sd_bus_message_append(
10✔
1364
                                        m,
1365
                                        "(sv)",
1366
                                        "Environment", "as", 1, e);
1367
                        if (r < 0)
10✔
1368
                                return bus_log_create_error(r);
×
1369

1370
                        if (colorterm) {
10✔
1371
                                r = sd_bus_message_append(
×
1372
                                                m,
1373
                                                "(sv)",
1374
                                                "Environment", "as", 1, colorterm);
1375
                                if (r < 0)
×
1376
                                        return bus_log_create_error(r);
×
1377
                        }
1378

1379
                        if (no_color) {
10✔
1380
                                r = sd_bus_message_append(
×
1381
                                                m,
1382
                                                "(sv)",
1383
                                                "Environment", "as", 1, no_color);
1384
                                if (r < 0)
×
1385
                                        return bus_log_create_error(r);
×
1386
                        }
1387
                }
1388
        }
1389

1390
        if (!strv_isempty(arg_environment)) {
1,123✔
1391
                r = sd_bus_message_open_container(m, 'r', "sv");
148✔
1392
                if (r < 0)
148✔
1393
                        return bus_log_create_error(r);
×
1394

1395
                r = sd_bus_message_append(m, "s", "Environment");
148✔
1396
                if (r < 0)
148✔
1397
                        return bus_log_create_error(r);
×
1398

1399
                r = sd_bus_message_open_container(m, 'v', "as");
148✔
1400
                if (r < 0)
148✔
1401
                        return bus_log_create_error(r);
×
1402

1403
                r = sd_bus_message_append_strv(m, arg_environment);
148✔
1404
                if (r < 0)
148✔
1405
                        return bus_log_create_error(r);
×
1406

1407
                r = sd_bus_message_close_container(m);
148✔
1408
                if (r < 0)
148✔
1409
                        return bus_log_create_error(r);
×
1410

1411
                r = sd_bus_message_close_container(m);
148✔
1412
                if (r < 0)
148✔
1413
                        return bus_log_create_error(r);
×
1414
        }
1415

1416
        /* Exec container */
1417
        if (!strv_isempty(arg_cmdline)) {
2,250✔
1418
                r = sd_bus_message_open_container(m, 'r', "sv");
1,123✔
1419
                if (r < 0)
1,123✔
1420
                        return bus_log_create_error(r);
×
1421

1422
                r = sd_bus_message_append(m, "s",
2,099✔
1423
                                          use_ex_prop ? "ExecStartEx" : "ExecStart");
1424
                if (r < 0)
1,123✔
1425
                        return bus_log_create_error(r);
×
1426

1427
                r = sd_bus_message_open_container(m, 'v',
2,099✔
1428
                                                  use_ex_prop ? "a(sasas)" : "a(sasb)");
1429
                if (r < 0)
1,123✔
1430
                        return bus_log_create_error(r);
×
1431

1432
                r = sd_bus_message_open_container(m, 'a',
2,099✔
1433
                                                  use_ex_prop ? "(sasas)" : "(sasb)");
1434
                if (r < 0)
1,123✔
1435
                        return bus_log_create_error(r);
×
1436

1437
                r = sd_bus_message_open_container(m, 'r',
2,099✔
1438
                                                  use_ex_prop ? "sasas" : "sasb");
1439
                if (r < 0)
1,123✔
1440
                        return bus_log_create_error(r);
×
1441

1442
                r = sd_bus_message_append(m, "s", arg_exec_path ?: arg_cmdline[0]);
1,123✔
1443
                if (r < 0)
1,123✔
1444
                        return bus_log_create_error(r);
×
1445

1446
                r = sd_bus_message_append_strv(m, arg_cmdline);
1,123✔
1447
                if (r < 0)
1,123✔
1448
                        return bus_log_create_error(r);
×
1449

1450
                if (use_ex_prop) {
1,123✔
1451
                        _cleanup_strv_free_ char **opts = NULL;
×
1452

1453
                        r = exec_command_flags_to_strv(
441✔
1454
                                        (arg_expand_environment ? 0 : EXEC_COMMAND_NO_ENV_EXPAND)|(arg_ignore_failure ? EXEC_COMMAND_IGNORE_FAILURE : 0),
294✔
1455
                                        &opts);
1456
                        if (r < 0)
147✔
1457
                                return log_error_errno(r, "Failed to format execute flags: %m");
×
1458

1459
                        r = sd_bus_message_append_strv(m, opts);
147✔
1460
                } else
1461
                        r = sd_bus_message_append(m, "b", arg_ignore_failure);
976✔
1462
                if (r < 0)
1,123✔
1463
                        return bus_log_create_error(r);
×
1464

1465
                r = sd_bus_message_close_container(m);
1,123✔
1466
                if (r < 0)
1,123✔
1467
                        return bus_log_create_error(r);
×
1468

1469
                r = sd_bus_message_close_container(m);
1,123✔
1470
                if (r < 0)
1,123✔
1471
                        return bus_log_create_error(r);
×
1472

1473
                r = sd_bus_message_close_container(m);
1,123✔
1474
                if (r < 0)
1,123✔
1475
                        return bus_log_create_error(r);
×
1476

1477
                r = sd_bus_message_close_container(m);
1,123✔
1478
                if (r < 0)
1,123✔
1479
                        return bus_log_create_error(r);
×
1480
        }
1481

1482
        return 0;
1483
}
1484

1485
static int transient_scope_set_properties(sd_bus_message *m, bool allow_pidfd) {
19✔
1486
        int r;
19✔
1487

1488
        assert(m);
19✔
1489

1490
        r = transient_unit_set_properties(m, UNIT_SCOPE, arg_property);
19✔
1491
        if (r < 0)
19✔
1492
                return r;
19✔
1493

1494
        r = transient_kill_set_properties(m);
19✔
1495
        if (r < 0)
19✔
1496
                return r;
1497

1498
        r = transient_cgroup_set_properties(m);
19✔
1499
        if (r < 0)
19✔
1500
                return r;
1501

1502
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
19✔
1503

1504
        r = pidref_set_self(&pidref);
19✔
1505
        if (r < 0)
19✔
1506
                return r;
1507

1508
        r = bus_append_scope_pidref(m, &pidref, allow_pidfd);
19✔
1509
        if (r < 0)
19✔
1510
                return bus_log_create_error(r);
×
1511

1512
        return 0;
1513
}
1514

1515
static int transient_timer_set_properties(sd_bus_message *m) {
15✔
1516
        int r;
15✔
1517

1518
        assert(m);
15✔
1519

1520
        r = transient_unit_set_properties(m, UNIT_TIMER, arg_timer_property);
15✔
1521
        if (r < 0)
15✔
1522
                return r;
1523

1524
        /* Automatically clean up our transient timers */
1525
        r = sd_bus_message_append(m, "(sv)", "RemainAfterElapse", "b", false);
10✔
1526
        if (r < 0)
10✔
1527
                return bus_log_create_error(r);
×
1528

1529
        return 0;
1530
}
1531

1532
static int make_unit_name(UnitType t, char **ret) {
615✔
1533
        int r;
615✔
1534

1535
        assert(t >= 0);
615✔
1536
        assert(t < _UNIT_TYPE_MAX);
615✔
1537
        assert(ret);
615✔
1538

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

1543
        _cleanup_(pidref_done) PidRef self = PIDREF_NULL;
615✔
1544
        r = pidref_set_self(&self);
615✔
1545
        if (r < 0)
615✔
1546
                return log_error_errno(r, "Failed to get reference to my own process: %m");
×
1547

1548
        r = pidref_acquire_pidfd_id(&self);
615✔
1549
        if (r < 0) {
615✔
1550
                log_debug_errno(r, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
×
1551

1552
                /* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
1553
                sd_id128_t rnd;
×
1554
                r = sd_id128_randomize(&rnd);
×
1555
                if (r < 0)
×
1556
                        return log_error_errno(r, "Failed to generate random run unit name: %m");
×
1557

1558
                r = asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t));
×
1559
        } else
1560
                r = asprintf(ret, "run-p" PID_FMT "-i%" PRIu64 ".%s", self.pid, self.fd_id, unit_type_to_string(t));
615✔
1561
        if (r < 0)
615✔
1562
                return log_oom();
×
1563

1564
        return 0;
1565
}
1566

1567
static int connect_bus(sd_bus **ret) {
1,155✔
1568
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1,155✔
1569
        int r;
1,155✔
1570

1571
        assert(ret);
1,155✔
1572

1573
        /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
1574
         * limited direct connection */
1575
        if (arg_wait ||
1,155✔
1576
            arg_stdio != ARG_STDIO_NONE ||
237✔
1577
            (arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
172✔
1578
                r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
985✔
1579
        else
1580
                r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
170✔
1581
        if (r < 0)
1,155✔
1582
                return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
1,155✔
1583

1584
        (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1,151✔
1585

1586
        *ret = TAKE_PTR(bus);
1,151✔
1587
        return 0;
1,151✔
1588
}
1589

1590
typedef struct RunContext {
1591
        sd_event *event;
1592
        PTYForward *forward;
1593
        char *unit;
1594
        char *bus_path;
1595
        char *start_job;
1596
        int pty_fd;
1597

1598
        /* Bus objects */
1599
        sd_bus *bus;
1600
        sd_bus_slot *match_properties_changed;
1601
        sd_bus_slot *match_disconnected;
1602
        sd_event_source *retry_timer;
1603

1604
        /* Current state of the unit */
1605
        char *active_state;
1606
        char *job;
1607

1608
        /* The exit data of the unit */
1609
        uint64_t inactive_exit_usec;
1610
        uint64_t inactive_enter_usec;
1611
        char *result;
1612
        uint64_t cpu_usage_nsec;
1613
        uint64_t memory_peak;
1614
        uint64_t memory_swap_peak;
1615
        uint64_t ip_ingress_bytes;
1616
        uint64_t ip_egress_bytes;
1617
        uint64_t io_read_bytes;
1618
        uint64_t io_write_bytes;
1619
        uint32_t exit_code;
1620
        uint32_t exit_status;
1621
} RunContext;
1622

1623
static int run_context_update(RunContext *c);
1624
static int run_context_attach_bus(RunContext *c, sd_bus *bus);
1625
static void run_context_detach_bus(RunContext *c);
1626
static int run_context_reconnect(RunContext *c);
1627
static int run_context_setup_ptyfwd(RunContext *c);
1628

1629
static void run_context_done(RunContext *c) {
1,114✔
1630
        assert(c);
1,114✔
1631

1632
        run_context_detach_bus(c);
1,114✔
1633

1634
        c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
1,114✔
1635
        c->forward = pty_forward_free(c->forward);
1,114✔
1636
        c->event = sd_event_unref(c->event);
1,114✔
1637

1638
        free(c->active_state);
1,114✔
1639
        free(c->job);
1,114✔
1640
        free(c->result);
1,114✔
1641
        free(c->unit);
1,114✔
1642
        free(c->bus_path);
1,114✔
1643
        free(c->start_job);
1,114✔
1644

1645
        safe_close(c->pty_fd);
1,114✔
1646
}
1,114✔
1647

1648
static int on_retry_timer(sd_event_source *s, uint64_t usec, void *userdata) {
×
1649
        RunContext *c = ASSERT_PTR(userdata);
×
1650

1651
        c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
×
1652

1653
        return run_context_reconnect(c);
×
1654
}
1655

1656
static int run_context_reconnect(RunContext *c) {
×
1657
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1658
        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
×
1659
        int r;
×
1660

1661
        assert(c);
×
1662

1663
        run_context_detach_bus(c);
×
1664

1665
        r = connect_bus(&bus);
×
1666
        if (r < 0)
×
1667
                goto retry_timer;
×
1668

1669
        r = sd_bus_call_method(bus,
×
1670
                               "org.freedesktop.systemd1",
1671
                               c->bus_path,
×
1672
                               "org.freedesktop.systemd1.Unit",
1673
                               "Ref",
1674
                               &error,
1675
                               /* reply = */ NULL, NULL);
1676
        if (r < 0) {
×
1677
                /* Hmm, the service manager probably hasn't finished reexecution just yet? Try again later. */
1678
                if (sd_bus_error_has_names(&error,
×
1679
                                           SD_BUS_ERROR_NO_REPLY,
1680
                                           SD_BUS_ERROR_DISCONNECTED,
1681
                                           SD_BUS_ERROR_TIMED_OUT,
1682
                                           SD_BUS_ERROR_SERVICE_UNKNOWN,
1683
                                           SD_BUS_ERROR_NAME_HAS_NO_OWNER))
1684
                        goto retry_timer;
×
1685

1686
                if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
×
1687
                        log_warning_errno(r, "Unit deactivated during reconnection to the bus, exiting.");
×
1688
                else
1689
                        log_error_errno(r, "Failed to re-add reference to unit: %s", bus_error_message(&error, r));
×
1690

1691
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
1692
                return r;
1693
        }
1694

1695
        r = run_context_attach_bus(c, bus);
×
1696
        if (r < 0) {
×
1697
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
1698
                return r;
1699
        }
1700

1701
        log_info("Reconnected to bus.");
×
1702

1703
        return run_context_update(c);
×
1704

1705
retry_timer:
×
1706
        log_warning_errno(r, "Failed to reconnect, retrying in 2s: %m");
×
1707

1708
        r = event_reset_time_relative(
×
1709
                        c->event,
1710
                        &c->retry_timer,
1711
                        CLOCK_MONOTONIC,
1712
                        2 * USEC_PER_SEC, /* accuracy= */ 0,
1713
                        on_retry_timer, c,
1714
                        SD_EVENT_PRIORITY_NORMAL,
1715
                        "retry-timeout",
1716
                        /* force_reset= */ false);
1717
        if (r < 0) {
×
1718
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
1719
                return log_error_errno(r, "Failed to install retry timer: %m");
×
1720
        }
1721

1722
        return 0;
1723
}
1724

1725
static int run_context_check_started(RunContext *c) {
6,069✔
1726
        int r;
6,069✔
1727

1728
        assert(c);
6,069✔
1729

1730
        if (!c->start_job)
6,069✔
1731
                return 0; /* Already started? */
1732

1733
        if (streq_ptr(c->start_job, c->job))
1,988✔
1734
                return 0; /* The start job is still active. */
1735

1736
        /* The start job is finished. */
1737
        c->start_job = mfree(c->start_job);
390✔
1738

1739
        /* Setup ptyfwd now if --pty-late is specified. */
1740
        r = run_context_setup_ptyfwd(c);
390✔
1741
        if (r < 0) {
390✔
1742
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
1743
                return r;
×
1744
        }
1745

1746
        if (STRPTR_IN_SET(c->active_state, "inactive", "failed"))
390✔
1747
                return 0; /* Already finished or failed? */
30✔
1748

1749
        /* Notify our caller that the service is now running, just in case. */
1750
        (void) sd_notifyf(/* unset_environment= */ false,
360✔
1751
                          "READY=1\n"
1752
                          "RUN_UNIT=%s",
1753
                          c->unit);
1754
        return 0;
360✔
1755
}
1756

1757
static void run_context_check_done(RunContext *c) {
6,079✔
1758
        assert(c);
6,079✔
1759

1760
        bool done = STRPTR_IN_SET(c->active_state, "inactive", "failed") &&
6,079✔
1761
                !c->start_job &&   /* our start job */
1,058✔
1762
                !c->job;           /* any other job */
974✔
1763

1764
        if (done && c->forward) /* If the service is gone, it's time to drain the output */
974✔
1765
                done = pty_forward_drain(c->forward);
20✔
1766

1767
        if (done)
974✔
1768
                (void) sd_event_exit(c->event, EXIT_SUCCESS);
974✔
1769
}
6,079✔
1770

1771
static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
6,069✔
1772
        char **p = ASSERT_PTR(userdata);
6,069✔
1773
        const char *job;
6,069✔
1774
        uint32_t id;
6,069✔
1775
        int r;
6,069✔
1776

1777
        assert(m);
6,069✔
1778

1779
        r = sd_bus_message_read(m, "(uo)", &id, &job);
6,069✔
1780
        if (r < 0)
6,069✔
1781
                return r;
6,069✔
1782

1783
        return free_and_strdup(p, id == 0 ? NULL : job);
6,069✔
1784
}
1785

1786
static int run_context_update(RunContext *c) {
6,069✔
1787

1788
        static const struct bus_properties_map map[] = {
6,069✔
1789
                { "ActiveState",                     "s",    NULL,    offsetof(RunContext, active_state)        },
1790
                { "InactiveExitTimestampMonotonic",  "t",    NULL,    offsetof(RunContext, inactive_exit_usec)  },
1791
                { "InactiveEnterTimestampMonotonic", "t",    NULL,    offsetof(RunContext, inactive_enter_usec) },
1792
                { "Result",                          "s",    NULL,    offsetof(RunContext, result)              },
1793
                { "ExecMainCode",                    "i",    NULL,    offsetof(RunContext, exit_code)           },
1794
                { "ExecMainStatus",                  "i",    NULL,    offsetof(RunContext, exit_status)         },
1795
                { "CPUUsageNSec",                    "t",    NULL,    offsetof(RunContext, cpu_usage_nsec)      },
1796
                { "MemoryPeak",                      "t",    NULL,    offsetof(RunContext, memory_peak)         },
1797
                { "MemorySwapPeak",                  "t",    NULL,    offsetof(RunContext, memory_swap_peak)    },
1798
                { "IPIngressBytes",                  "t",    NULL,    offsetof(RunContext, ip_ingress_bytes)    },
1799
                { "IPEgressBytes",                   "t",    NULL,    offsetof(RunContext, ip_egress_bytes)     },
1800
                { "IOReadBytes",                     "t",    NULL,    offsetof(RunContext, io_read_bytes)       },
1801
                { "IOWriteBytes",                    "t",    NULL,    offsetof(RunContext, io_write_bytes)      },
1802
                { "Job",                             "(uo)", map_job, offsetof(RunContext, job)                 },
1803
                {}
1804
        };
1805

1806
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
6,069✔
1807
        int r;
6,069✔
1808

1809
        assert(c);
6,069✔
1810
        assert(c->bus);
6,069✔
1811

1812
        r = bus_map_all_properties(
12,138✔
1813
                        c->bus,
1814
                        "org.freedesktop.systemd1",
1815
                        c->bus_path,
6,069✔
1816
                        map,
1817
                        BUS_MAP_STRDUP,
1818
                        &error,
1819
                        NULL,
1820
                        c);
1821
        if (r < 0) {
6,069✔
1822
                /* If this is a connection error, then try to reconnect. This might be because the service
1823
                 * manager is being restarted. Handle this gracefully. */
1824
                if (sd_bus_error_has_names(
×
1825
                                    &error,
1826
                                    SD_BUS_ERROR_NO_REPLY,
1827
                                    SD_BUS_ERROR_DISCONNECTED,
1828
                                    SD_BUS_ERROR_TIMED_OUT,
1829
                                    SD_BUS_ERROR_SERVICE_UNKNOWN,
1830
                                    SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
1831

1832
                        log_info_errno(r, "Bus call failed due to connection problems. Trying to reconnect...");
×
1833
                        /* Not propagating error, because we handled it already, by reconnecting. */
1834
                        return run_context_reconnect(c);
×
1835
                }
1836

1837
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
1838
                return log_error_errno(r, "Failed to query unit state: %s", bus_error_message(&error, r));
×
1839
        }
1840

1841
        r = run_context_check_started(c);
6,069✔
1842
        if (r < 0)
6,069✔
1843
                return r;
1844

1845
        run_context_check_done(c);
6,069✔
1846
        return 0;
1847
}
1848

1849
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
5,105✔
1850
        return run_context_update(ASSERT_PTR(userdata));
5,105✔
1851
}
1852

1853
static int on_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
×
1854
        /* If our connection gets terminated, then try to reconnect. This might be because the service
1855
         * manager is being restarted. Handle this gracefully. */
1856
        log_info("Got disconnected from bus connection. Trying to reconnect...");
×
1857
        return run_context_reconnect(ASSERT_PTR(userdata));
×
1858
}
1859

1860
static int run_context_attach_bus(RunContext *c, sd_bus *bus) {
964✔
1861
        int r;
964✔
1862

1863
        assert(c);
964✔
1864
        assert(bus);
964✔
1865

1866
        assert(!c->bus);
964✔
1867
        assert(!c->match_properties_changed);
964✔
1868
        assert(!c->match_disconnected);
964✔
1869

1870
        c->bus = sd_bus_ref(bus);
964✔
1871

1872
        r = sd_bus_match_signal_async(
1,928✔
1873
                        c->bus,
1874
                        &c->match_properties_changed,
1875
                        "org.freedesktop.systemd1",
1876
                        c->bus_path,
964✔
1877
                        "org.freedesktop.DBus.Properties",
1878
                        "PropertiesChanged",
1879
                        on_properties_changed, NULL, c);
1880
        if (r < 0)
964✔
1881
                return log_error_errno(r, "Failed to request PropertiesChanged signal match: %m");
×
1882

1883
        r = sd_bus_match_signal_async(
964✔
1884
                        bus,
1885
                        &c->match_disconnected,
1886
                        "org.freedesktop.DBus.Local",
1887
                        /* path= */ NULL,
1888
                        "org.freedesktop.DBus.Local",
1889
                        "Disconnected",
1890
                        on_disconnected, NULL, c);
1891
        if (r < 0)
964✔
1892
                return log_error_errno(r, "Failed to request Disconnected signal match: %m");
×
1893

1894
        r = sd_bus_attach_event(c->bus, c->event, SD_EVENT_PRIORITY_NORMAL);
964✔
1895
        if (r < 0)
964✔
1896
                return log_error_errno(r, "Failed to attach bus to event loop: %m");
×
1897

1898
        return 0;
1899
}
1900

1901
static void run_context_detach_bus(RunContext *c) {
1,114✔
1902
        assert(c);
1,114✔
1903

1904
        if (c->bus) {
1,114✔
1905
                (void) sd_bus_detach_event(c->bus);
964✔
1906
                c->bus = sd_bus_unref(c->bus);
964✔
1907
        }
1908

1909
        c->match_properties_changed = sd_bus_slot_unref(c->match_properties_changed);
1,114✔
1910
        c->match_disconnected = sd_bus_slot_unref(c->match_disconnected);
1,114✔
1911
}
1,114✔
1912

1913
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
10✔
1914
        RunContext *c = ASSERT_PTR(userdata);
10✔
1915

1916
        assert(f);
10✔
1917

1918
        if (rcode == -ECANCELED) {
10✔
1919
                log_debug_errno(rcode, "PTY forwarder disconnected.");
×
1920
                if (!arg_wait)
×
1921
                        return sd_event_exit(c->event, EXIT_SUCCESS);
×
1922

1923
                /* If --wait is specified, we'll only exit the pty forwarding, but will continue to wait
1924
                 * for the service to end. If the user hits ^C we'll exit too. */
1925
        } else if (rcode < 0) {
10✔
1926
                (void) sd_event_exit(c->event, EXIT_FAILURE);
×
1927
                return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
×
1928
        }
1929

1930
        run_context_check_done(c);
10✔
1931
        return 0;
10✔
1932
}
1933

1934
static int make_transient_service_unit(
1,114✔
1935
                sd_bus *bus,
1936
                sd_bus_message **message,
1937
                const char *service,
1938
                const char *pty_path,
1939
                int pty_fd) {
1940

1941
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1,114✔
1942
        int r;
1,114✔
1943

1944
        assert(bus);
1,114✔
1945
        assert(message);
1,114✔
1946
        assert(service);
1,114✔
1947

1948
        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
1,114✔
1949
        if (r < 0)
1,114✔
1950
                return bus_log_create_error(r);
×
1951

1952
        /* Name and mode */
1953
        r = sd_bus_message_append(m, "ss", service, job_mode_to_string(arg_job_mode));
1,114✔
1954
        if (r < 0)
1,114✔
1955
                return bus_log_create_error(r);
×
1956

1957
        /* Properties */
1958
        r = sd_bus_message_open_container(m, 'a', "(sv)");
1,114✔
1959
        if (r < 0)
1,114✔
1960
                return bus_log_create_error(r);
×
1961

1962
        r = transient_service_set_properties(m, pty_path, pty_fd);
1,114✔
1963
        if (r < 0)
1,114✔
1964
                return r;
1965

1966
        r = sd_bus_message_close_container(m);
1,110✔
1967
        if (r < 0)
1,110✔
1968
                return bus_log_create_error(r);
×
1969

1970
        /* Auxiliary units */
1971
        r = sd_bus_message_append(m, "a(sa(sv))", 0);
1,110✔
1972
        if (r < 0)
1,110✔
1973
                return bus_log_create_error(r);
×
1974

1975
        *message = TAKE_PTR(m);
1,110✔
1976
        return 0;
1,110✔
1977
}
1978

1979
static int bus_call_with_hint(
1,123✔
1980
                sd_bus *bus,
1981
                sd_bus_message *message,
1982
                const char *name,
1983
                sd_bus_message **reply) {
1984

1985
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1986
        int r;
1,123✔
1987

1988
        r = sd_bus_call(bus, message, 0, &error, reply);
1,123✔
1989
        if (r < 0) {
1,123✔
1990
                log_error_errno(r, "Failed to start transient %s unit: %s", name, bus_error_message(&error, r));
18✔
1991

1992
                if (!arg_expand_environment &&
18✔
1993
                    sd_bus_error_has_names(&error,
×
1994
                                           SD_BUS_ERROR_UNKNOWN_PROPERTY,
1995
                                           SD_BUS_ERROR_PROPERTY_READ_ONLY))
1996
                        log_notice_errno(r, "Hint: --expand-environment=no is not supported by old systemd");
×
1997
        }
1998

1999
        return r;
1,123✔
2000
}
2001

2002
static int acquire_invocation_id(sd_bus *bus, const char *unit, sd_id128_t *ret) {
621✔
2003
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
2004
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
621✔
2005
        _cleanup_free_ char *object = NULL;
621✔
2006
        int r;
621✔
2007

2008
        assert(bus);
621✔
2009
        assert(ret);
621✔
2010

2011
        if (unit) {
621✔
2012
                object = unit_dbus_path_from_name(unit);
604✔
2013
                if (!object)
604✔
2014
                        return log_oom();
×
2015
        }
2016

2017
        r = sd_bus_get_property(bus,
621✔
2018
                                "org.freedesktop.systemd1",
2019
                                object ?: "/org/freedesktop/systemd1/unit/self",
2020
                                "org.freedesktop.systemd1.Unit",
2021
                                "InvocationID",
2022
                                &error,
2023
                                &reply,
2024
                                "ay");
2025
        if (r < 0)
621✔
2026
                return log_error_errno(r, "Failed to request invocation ID for unit: %s", bus_error_message(&error, r));
×
2027

2028
        r = bus_message_read_id128(reply, ret);
621✔
2029
        if (r < 0)
621✔
2030
                return bus_log_parse_error(r);
×
2031

2032
        return r; /* Return true when we get a non-null invocation ID. */
2033
}
2034

2035
static void set_window_title(PTYForward *f) {
10✔
2036
        _cleanup_free_ char *hn = NULL, *cl = NULL, *dot = NULL;
10✔
2037

2038
        assert(f);
10✔
2039

2040
        if (!shall_set_terminal_title())
10✔
2041
                return;
2042

2043
        if (!arg_host)
10✔
2044
                (void) gethostname_strict(&hn);
9✔
2045

2046
        cl = strv_join(arg_cmdline, " ");
10✔
2047
        if (!cl)
10✔
2048
                return (void) log_oom();
×
2049

2050
        if (emoji_enabled())
10✔
2051
                dot = strjoin(glyph(privileged_execution() ? GLYPH_RED_CIRCLE : GLYPH_YELLOW_CIRCLE), " ");
×
2052

2053
        if (arg_host || hn)
10✔
2054
                (void) pty_forward_set_titlef(f, "%s%s on %s", strempty(dot), cl, arg_host ?: hn);
20✔
2055
        else
2056
                (void) pty_forward_set_titlef(f, "%s%s", strempty(dot), cl);
×
2057

2058
        (void) pty_forward_set_title_prefix(f, dot);
10✔
2059
}
2060

2061
static int fchown_to_capsule(int fd, const char *capsule) {
×
2062
        _cleanup_free_ char *p = NULL;
×
2063
        int r;
×
2064

2065
        assert(fd >= 0);
×
2066
        assert(capsule);
×
2067

2068
        p = path_join("/run/capsules/", capsule);
×
2069
        if (!p)
×
2070
                return -ENOMEM;
2071

2072
        struct stat st;
×
2073
        r = chase_and_stat(p, /* root= */ NULL, CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS, /* ret_path= */ NULL, &st);
×
2074
        if (r < 0)
×
2075
                return r;
2076

2077
        if (uid_is_system(st.st_uid) || gid_is_system(st.st_gid)) /* paranoid safety check */
×
2078
                return -EPERM;
×
2079

2080
        return fchmod_and_chown(fd, 0600, st.st_uid, st.st_gid);
×
2081
}
2082

2083
static int print_unit_invocation(const char *unit, sd_id128_t invocation_id) {
621✔
2084
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
621✔
2085
        int r;
621✔
2086

2087
        assert(unit);
621✔
2088

2089
        if (!sd_json_format_enabled(arg_json_format_flags)) {
621✔
2090
                if (sd_id128_is_null(invocation_id))
619✔
2091
                        log_info("Running as unit: %s", unit);
124✔
2092
                else
2093
                        log_info("Running as unit: %s; invocation ID: " SD_ID128_FORMAT_STR, unit, SD_ID128_FORMAT_VAL(invocation_id));
495✔
2094
                return 0;
619✔
2095
        }
2096

2097
        r = sd_json_variant_set_field_string(&v, "unit", unit);
2✔
2098
        if (r < 0)
2✔
2099
                return r;
2100

2101
        if (!sd_id128_is_null(invocation_id)) {
2✔
2102
                r = sd_json_variant_set_field_id128(&v, "invocation_id", invocation_id);
2✔
2103
                if (r < 0)
2✔
2104
                        return r;
2105
        }
2106

2107
        return sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
2✔
2108
}
2109

2110
static int run_context_setup_ptyfwd(RunContext *c) {
400✔
2111
        int r;
400✔
2112

2113
        assert(c);
400✔
2114

2115
        if (c->pty_fd < 0 || c->forward)
400✔
2116
                return 0;
2117

2118
        /* Stop agents now that we are online, to avoid TTY conflicts */
2119
        polkit_agent_close();
10✔
2120
        ask_password_agent_close();
10✔
2121

2122
        if (!arg_quiet)
10✔
2123
                log_info("Press ^] three times within 1s to disconnect TTY.");
7✔
2124

2125
        r = pty_forward_new(c->event, c->pty_fd, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &c->forward);
10✔
2126
        if (r < 0)
10✔
2127
                return log_error_errno(r, "Failed to create PTY forwarder: %m");
×
2128

2129
        pty_forward_set_hangup_handler(c->forward, pty_forward_handler, c);
10✔
2130

2131
        /* Make sure to process any TTY events before we process bus events */
2132
        (void) pty_forward_set_priority(c->forward, SD_EVENT_PRIORITY_IMPORTANT);
10✔
2133

2134
        if (!isempty(arg_background))
10✔
2135
                (void) pty_forward_set_background_color(c->forward, arg_background);
×
2136

2137
        set_window_title(c->forward);
10✔
2138
        return 0;
10✔
2139
}
2140

2141
static int start_transient_service(sd_bus *bus) {
1,114✔
2142
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
2,224✔
2143
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1,114✔
2144
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
2145
        _cleanup_free_ char *pty_path = NULL;
1,114✔
2146
        _cleanup_close_ int peer_fd = -EBADF;
1,114✔
2147
        int r;
1,114✔
2148

2149
        assert(bus);
1,114✔
2150

2151
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1,114✔
2152
        (void) ask_password_agent_open_if_enabled(arg_transport, arg_ask_password);
1,114✔
2153

2154
        _cleanup_(run_context_done) RunContext c = {
1,114✔
2155
                .pty_fd = -EBADF,
2156
                .cpu_usage_nsec = NSEC_INFINITY,
2157
                .memory_peak = UINT64_MAX,
2158
                .memory_swap_peak = UINT64_MAX,
2159
                .ip_ingress_bytes = UINT64_MAX,
2160
                .ip_egress_bytes = UINT64_MAX,
2161
                .io_read_bytes = UINT64_MAX,
2162
                .io_write_bytes = UINT64_MAX,
2163
                .inactive_exit_usec = USEC_INFINITY,
2164
                .inactive_enter_usec = USEC_INFINITY,
2165
        };
2166

2167
        if (arg_stdio == ARG_STDIO_PTY) {
1,114✔
2168

2169
                if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)) {
10✔
2170
                        c.pty_fd = openpt_allocate(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK, &pty_path);
9✔
2171
                        if (c.pty_fd < 0)
9✔
2172
                                return log_error_errno(c.pty_fd, "Failed to acquire pseudo tty: %m");
×
2173

2174
                        peer_fd = pty_open_peer(c.pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
9✔
2175
                        if (peer_fd < 0)
9✔
2176
                                return log_error_errno(peer_fd, "Failed to open pty peer: %m");
×
2177

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

2181
                                r = fchown_to_capsule(peer_fd, arg_host);
×
2182
                                if (r < 0)
×
2183
                                        return log_error_errno(r, "Failed to chown tty to capsule UID/GID: %m");
×
2184
                        }
2185

2186
                } else if (arg_transport == BUS_TRANSPORT_MACHINE) {
1✔
2187
                        _cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL;
1✔
2188
                        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pty_reply = NULL;
1✔
2189
                        const char *s;
1✔
2190

2191
                        r = sd_bus_default_system(&system_bus);
1✔
2192
                        if (r < 0)
1✔
2193
                                return log_error_errno(r, "Failed to connect to system bus: %m");
×
2194

2195
                        (void) sd_bus_set_allow_interactive_authorization(system_bus, arg_ask_password);
1✔
2196

2197
                        r = bus_call_method(system_bus,
1✔
2198
                                            bus_machine_mgr,
2199
                                            "OpenMachinePTY",
2200
                                            &error,
2201
                                            &pty_reply,
2202
                                            "s", arg_host);
2203
                        if (r < 0)
1✔
2204
                                return log_error_errno(r, "Failed to get machine PTY: %s", bus_error_message(&error, r));
×
2205

2206
                        r = sd_bus_message_read(pty_reply, "hs", &c.pty_fd, &s);
1✔
2207
                        if (r < 0)
1✔
2208
                                return bus_log_parse_error(r);
×
2209

2210
                        c.pty_fd = fcntl(c.pty_fd, F_DUPFD_CLOEXEC, 3);
1✔
2211
                        if (c.pty_fd < 0)
1✔
2212
                                return log_error_errno(errno, "Failed to duplicate master fd: %m");
×
2213

2214
                        pty_path = strdup(s);
1✔
2215
                        if (!pty_path)
1✔
2216
                                return log_oom();
×
2217

2218
                        peer_fd = pty_open_peer(c.pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
1✔
2219
                        if (peer_fd < 0)
1✔
2220
                                return log_error_errno(peer_fd, "Failed to open PTY peer: %m");
×
2221
                } else
2222
                        assert_not_reached();
×
2223
        }
2224

2225
        if (arg_unit) {
1,114✔
2226
                r = unit_name_mangle_with_suffix(
524✔
2227
                                arg_unit,
2228
                                "as unit",
2229
                                arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
524✔
2230
                                ".service",
2231
                                &c.unit);
2232
                if (r < 0)
524✔
2233
                        return log_error_errno(r, "Failed to mangle unit name: %m");
×
2234
        } else {
2235
                r = make_unit_name(UNIT_SERVICE, &c.unit);
590✔
2236
                if (r < 0)
590✔
2237
                        return r;
2238
        }
2239

2240
        /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin
2241
         * lets skip this however, because we should start that already when the start job is running, and
2242
         * there's little point in waiting for the start job to complete in that case anyway, as we'll wait
2243
         * for EOF anyway, which is going to be much later. */
2244
        if (!arg_no_block && arg_stdio == ARG_STDIO_NONE) {
1,114✔
2245
                r = bus_wait_for_jobs_new(bus, &w);
719✔
2246
                if (r < 0)
719✔
2247
                        return log_error_errno(r, "Could not watch jobs: %m");
×
2248
        }
2249

2250
        r = make_transient_service_unit(bus, &m, c.unit, pty_path, peer_fd);
1,114✔
2251
        if (r < 0)
1,114✔
2252
                return r;
2253
        peer_fd = safe_close(peer_fd);
1,110✔
2254

2255
        r = bus_call_with_hint(bus, m, "service", &reply);
1,110✔
2256
        if (r < 0)
1,110✔
2257
                return r;
2258

2259
        const char *object;
1,097✔
2260
        r = sd_bus_message_read(reply, "o", &object);
1,097✔
2261
        if (r < 0)
1,097✔
2262
                return bus_log_parse_error(r);
×
2263

2264
        if (w) {
1,097✔
2265
                r = bus_wait_for_jobs_one(
2,109✔
2266
                                w,
2267
                                object,
2268
                                arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
703✔
2269
                                arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
703✔
2270
                if (r < 0)
703✔
2271
                        return r;
8✔
2272
        } else if (!arg_no_block) {
394✔
2273
                c.start_job = strdup(object);
390✔
2274
                if (!c.start_job)
390✔
2275
                        return log_oom();
×
2276
        }
2277

2278
        if (!arg_quiet) {
1,089✔
2279
                sd_id128_t invocation_id;
604✔
2280

2281
                r = acquire_invocation_id(bus, c.unit, &invocation_id);
604✔
2282
                if (r < 0)
604✔
2283
                        return r;
×
2284

2285
                r = print_unit_invocation(c.unit, invocation_id);
604✔
2286
                if (r < 0)
604✔
2287
                        return r;
2288
        }
2289

2290
        if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
1,089✔
2291
                c.bus_path = unit_dbus_path_from_name(c.unit);
964✔
2292
                if (!c.bus_path)
964✔
2293
                        return log_oom();
×
2294

2295
                r = sd_event_default(&c.event);
964✔
2296
                if (r < 0)
964✔
2297
                        return log_error_errno(r, "Failed to get event loop: %m");
×
2298

2299
                _cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL;
964✔
2300
                if (c.pty_fd >= 0) {
964✔
2301
                        if (arg_exec_user && !terminal_is_dumb()) {
10✔
2302
                                r = osc_context_open_chpriv(arg_exec_user, /* ret_seq= */ NULL, &osc_context_id);
×
2303
                                if (r < 0)
×
2304
                                        return log_error_errno(r, "Failed to set OSC context: %m");
×
2305
                        }
2306

2307
                        (void) sd_event_set_signal_exit(c.event, true);
10✔
2308

2309
                        assert(arg_pty_late >= 0);
10✔
2310
                        if (!arg_pty_late) { /* If late PTY mode is off, start pty forwarder immediately */
10✔
2311
                                r = run_context_setup_ptyfwd(&c);
10✔
2312
                                if (r < 0)
10✔
2313
                                        return r;
2314
                        }
2315
                }
2316

2317
                r = run_context_attach_bus(&c, bus);
964✔
2318
                if (r < 0)
964✔
2319
                        return r;
2320

2321
                r = run_context_update(&c);
964✔
2322
                if (r < 0)
964✔
2323
                        return r;
2324

2325
                r = sd_event_loop(c.event);
964✔
2326
                if (r < 0)
964✔
2327
                        return log_error_errno(r, "Failed to run event loop: %m");
×
2328

2329
                if (arg_wait && !arg_quiet) {
964✔
2330

2331
                        if (!isempty(c.result))
417✔
2332
                                log_info("Finished with result: %s", strna(c.result));
417✔
2333

2334
                        if (c.exit_code > 0)
417✔
2335
                                log_info("Main processes terminated with: code=%s, status=%u/%s",
832✔
2336
                                         sigchld_code_to_string(c.exit_code),
2337
                                         c.exit_status,
2338
                                         strna(c.exit_code == CLD_EXITED ?
2339
                                               exit_status_to_string(c.exit_status, EXIT_STATUS_FULL) :
2340
                                               signal_to_string(c.exit_status)));
2341

2342
                        if (timestamp_is_set(c.inactive_enter_usec) &&
417✔
2343
                            timestamp_is_set(c.inactive_exit_usec) &&
417✔
2344
                            c.inactive_enter_usec > c.inactive_exit_usec)
2345
                                log_info("Service runtime: %s",
417✔
2346
                                         FORMAT_TIMESPAN(c.inactive_enter_usec - c.inactive_exit_usec, USEC_PER_MSEC));
2347

2348
                        if (c.cpu_usage_nsec != NSEC_INFINITY)
417✔
2349
                                log_info("CPU time consumed: %s",
417✔
2350
                                         FORMAT_TIMESPAN(DIV_ROUND_UP(c.cpu_usage_nsec, NSEC_PER_USEC), USEC_PER_MSEC));
2351

2352
                        if (c.memory_peak != UINT64_MAX) {
417✔
2353
                                const char *swap;
417✔
2354

2355
                                if (c.memory_swap_peak != UINT64_MAX)
417✔
2356
                                        swap = strjoina(" (swap: ", FORMAT_BYTES(c.memory_swap_peak), ")");
2,919✔
2357
                                else
2358
                                        swap = "";
2359

2360
                                log_info("Memory peak: %s%s", FORMAT_BYTES(c.memory_peak), swap);
417✔
2361
                        }
2362

2363
                        const char *ip_ingress = NULL, *ip_egress = NULL;
417✔
2364

2365
                        if (!IN_SET(c.ip_ingress_bytes, 0, UINT64_MAX))
417✔
2366
                                ip_ingress = strjoina(" received: ", FORMAT_BYTES(c.ip_ingress_bytes));
×
2367
                        if (!IN_SET(c.ip_egress_bytes, 0, UINT64_MAX))
417✔
2368
                                ip_egress = strjoina(" sent: ", FORMAT_BYTES(c.ip_egress_bytes));
×
2369

2370
                        if (ip_ingress || ip_egress)
417✔
2371
                                log_info("IP traffic%s%s", strempty(ip_ingress), strempty(ip_egress));
×
2372

2373
                        const char *io_read = NULL, *io_write = NULL;
417✔
2374

2375
                        if (!IN_SET(c.io_read_bytes, 0, UINT64_MAX))
417✔
2376
                                io_read = strjoina(" read: ", FORMAT_BYTES(c.io_read_bytes));
×
2377
                        if (!IN_SET(c.io_write_bytes, 0, UINT64_MAX))
417✔
2378
                                io_write = strjoina(" written: ", FORMAT_BYTES(c.io_write_bytes));
×
2379

2380
                        if (io_read || io_write)
417✔
2381
                                log_info("IO bytes%s%s", strempty(io_read), strempty(io_write));
×
2382
                }
2383

2384
                /* Try to propagate the service's return value. But if the service defines
2385
                 * e.g. SuccessExitStatus, honour this, and return 0 to mean "success". */
2386
                if (streq_ptr(c.result, "success"))
964✔
2387
                        return EXIT_SUCCESS;
2388
                if (streq_ptr(c.result, "exit-code") && c.exit_status > 0)
107✔
2389
                        return c.exit_status;
105✔
2390
                if (streq_ptr(c.result, "signal"))
2✔
2391
                        return EXIT_EXCEPTION;
2392
                return EXIT_FAILURE;
1✔
2393
        }
2394

2395
        return EXIT_SUCCESS;
2396
}
2397

2398
static int start_transient_scope(sd_bus *bus) {
19✔
2399
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2✔
2400
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
2✔
2401
        _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
2✔
2402
        _cleanup_free_ char *scope = NULL;
2✔
2403
        const char *object = NULL;
19✔
2404
        sd_id128_t invocation_id;
19✔
2405
        bool allow_pidfd = true;
19✔
2406
        int r;
19✔
2407

2408
        assert(bus);
19✔
2409
        assert(!strv_isempty(arg_cmdline));
19✔
2410

2411
        r = bus_wait_for_jobs_new(bus, &w);
19✔
2412
        if (r < 0)
19✔
2413
                return log_error_errno(r, "Could not watch jobs: %m");
×
2414

2415
        if (arg_unit) {
19✔
2416
                r = unit_name_mangle_with_suffix(arg_unit, "as unit",
6✔
2417
                                                 arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
6✔
2418
                                                 ".scope", &scope);
2419
                if (r < 0)
6✔
2420
                        return log_error_errno(r, "Failed to mangle scope name: %m");
×
2421
        } else {
2422
                r = make_unit_name(UNIT_SCOPE, &scope);
13✔
2423
                if (r < 0)
13✔
2424
                        return r;
2425
        }
2426

2427
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
19✔
2428
        (void) ask_password_agent_open_if_enabled(arg_transport, arg_ask_password);
19✔
2429

2430
        for (;;) {
×
2431
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
2432
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
19✔
2433

2434
                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
19✔
2435
                if (r < 0)
19✔
2436
                        return bus_log_create_error(r);
×
2437

2438
                /* Name and Mode */
2439
                r = sd_bus_message_append(m, "ss", scope, job_mode_to_string(arg_job_mode));
19✔
2440
                if (r < 0)
19✔
2441
                        return bus_log_create_error(r);
×
2442

2443
                /* Properties */
2444
                r = sd_bus_message_open_container(m, 'a', "(sv)");
19✔
2445
                if (r < 0)
19✔
2446
                        return bus_log_create_error(r);
×
2447

2448
                r = transient_scope_set_properties(m, allow_pidfd);
19✔
2449
                if (r < 0)
19✔
2450
                        return r;
2451

2452
                r = sd_bus_message_close_container(m);
19✔
2453
                if (r < 0)
19✔
2454
                        return bus_log_create_error(r);
×
2455

2456
                /* Auxiliary units */
2457
                r = sd_bus_message_append(m, "a(sa(sv))", 0);
19✔
2458
                if (r < 0)
19✔
2459
                        return bus_log_create_error(r);
×
2460

2461
                r = sd_bus_call(bus, m, 0, &error, &reply);
19✔
2462
                if (r < 0) {
19✔
2463
                        if (sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY) && allow_pidfd) {
2✔
2464
                                log_debug("Retrying with classic PIDs.");
×
2465
                                allow_pidfd = false;
×
2466
                                continue;
×
2467
                        }
2468

2469
                        return log_error_errno(r, "Failed to start transient scope unit: %s", bus_error_message(&error, r));
2✔
2470
                }
2471

2472
                break;
17✔
2473
        }
2474

2475
        r = sd_bus_message_read(reply, "o", &object);
17✔
2476
        if (r < 0)
17✔
2477
                return bus_log_parse_error(r);
×
2478

2479
        r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
17✔
2480
                                  arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
17✔
2481
        if (r < 0)
17✔
2482
                return r;
2483

2484
        r = acquire_invocation_id(bus, NULL, &invocation_id);
17✔
2485
        if (r < 0)
17✔
2486
                return r;
2487
        if (r == 0)
17✔
2488
                log_debug("No invocation ID set.");
×
2489
        else {
2490
                if (strv_extendf(&user_env, "INVOCATION_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(invocation_id)) < 0)
17✔
2491
                        return log_oom();
×
2492
        }
2493

2494
        /* Stop agents before we pass control away and before we drop privileges, to avoid TTY conflicts and
2495
         * before we become unable to stop agents. */
2496
        polkit_agent_close();
17✔
2497
        ask_password_agent_close();
17✔
2498

2499
        if (arg_nice_set) {
17✔
2500
                if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
×
2501
                        return log_error_errno(errno, "Failed to set nice level: %m");
×
2502
        }
2503

2504
        if (arg_exec_group) {
17✔
2505
                gid_t gid;
×
2506

2507
                r = get_group_creds(&arg_exec_group, &gid, 0);
×
2508
                if (r < 0)
×
2509
                        return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
×
2510

2511
                if (setresgid(gid, gid, gid) < 0)
×
2512
                        return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
×
2513
        }
2514

2515
        if (arg_exec_user) {
17✔
2516
                const char *home, *shell;
1✔
2517
                uid_t uid;
1✔
2518
                gid_t gid;
1✔
2519

2520
                r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell,
1✔
2521
                                   USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS);
2522
                if (r < 0)
1✔
2523
                        return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
×
2524

2525
                if (home) {
1✔
2526
                        r = strv_extendf(&user_env, "HOME=%s", home);
1✔
2527
                        if (r < 0)
1✔
2528
                                return log_oom();
×
2529
                }
2530

2531
                if (shell) {
1✔
2532
                        r = strv_extendf(&user_env, "SHELL=%s", shell);
1✔
2533
                        if (r < 0)
1✔
2534
                                return log_oom();
×
2535
                }
2536

2537
                r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
1✔
2538
                if (r < 0)
1✔
2539
                        return log_oom();
×
2540

2541
                r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
1✔
2542
                if (r < 0)
1✔
2543
                        return log_oom();
×
2544

2545
                if (!arg_exec_group) {
1✔
2546
                        if (setresgid(gid, gid, gid) < 0)
1✔
2547
                                return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
×
2548
                }
2549

2550
                if (setresuid(uid, uid, uid) < 0)
1✔
2551
                        return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
×
2552
        }
2553

2554
        if (arg_working_directory && chdir(arg_working_directory) < 0)
17✔
2555
                return log_error_errno(errno, "Failed to change directory to '%s': %m", arg_working_directory);
×
2556

2557
        env = strv_env_merge(environ, user_env, arg_environment);
17✔
2558
        if (!env)
17✔
2559
                return log_oom();
×
2560

2561
        if (!arg_quiet) {
17✔
2562
                r = print_unit_invocation(scope, invocation_id);
17✔
2563
                if (r < 0)
17✔
2564
                        return r;
2565
        }
2566

2567
        if (arg_expand_environment) {
17✔
2568
                _cleanup_strv_free_ char **expanded_cmdline = NULL, **unset_variables = NULL, **bad_variables = NULL;
17✔
2569

2570
                r = replace_env_argv(arg_cmdline, env, &expanded_cmdline, &unset_variables, &bad_variables);
17✔
2571
                if (r < 0)
17✔
2572
                        return log_error_errno(r, "Failed to expand environment variables: %m");
×
2573

2574
                free_and_replace(arg_cmdline, expanded_cmdline);
17✔
2575

2576
                if (!strv_isempty(unset_variables)) {
17✔
2577
                        _cleanup_free_ char *ju = strv_join(unset_variables, ", ");
×
2578
                        log_warning("Referenced but unset environment variable evaluates to an empty string: %s", strna(ju));
×
2579
                }
2580

2581
                if (!strv_isempty(bad_variables)) {
17✔
2582
                        _cleanup_free_ char *jb = strv_join(bad_variables, ", ");
×
2583
                        log_warning("Invalid environment variable name evaluates to an empty string: %s", strna(jb));
×
2584
                }
2585
        }
2586

2587
        execvpe(arg_cmdline[0], arg_cmdline, env);
17✔
2588

2589
        return log_error_errno(errno, "Failed to execute: %m");
×
2590
}
2591

2592
static int make_transient_trigger_unit(
18✔
2593
                sd_bus *bus,
2594
                sd_bus_message **message,
2595
                const char *suffix,
2596
                const char *trigger,
2597
                const char *service) {
2598

2599
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
18✔
2600
        int r;
18✔
2601

2602
        assert(bus);
18✔
2603
        assert(message);
18✔
2604
        assert(suffix);
18✔
2605
        assert(trigger);
18✔
2606
        assert(service);
18✔
2607

2608
        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
18✔
2609
        if (r < 0)
18✔
2610
                return bus_log_create_error(r);
×
2611

2612
        /* Name and Mode */
2613
        r = sd_bus_message_append(m, "ss", trigger, job_mode_to_string(arg_job_mode));
18✔
2614
        if (r < 0)
18✔
2615
                return bus_log_create_error(r);
×
2616

2617
        /* Properties */
2618
        r = sd_bus_message_open_container(m, 'a', "(sv)");
18✔
2619
        if (r < 0)
18✔
2620
                return bus_log_create_error(r);
×
2621

2622
        if (streq(suffix, ".path"))
18✔
2623
                r = transient_unit_set_properties(m, UNIT_PATH, arg_path_property);
1✔
2624
        else if (streq(suffix, ".socket"))
17✔
2625
                r = transient_unit_set_properties(m, UNIT_SOCKET, arg_socket_property);
2✔
2626
        else if (streq(suffix, ".timer"))
15✔
2627
                r = transient_timer_set_properties(m);
15✔
2628
        else
2629
                assert_not_reached();
×
2630
        if (r < 0)
18✔
2631
                return r;
2632

2633
        r = sd_bus_message_close_container(m);
13✔
2634
        if (r < 0)
13✔
2635
                return bus_log_create_error(r);
×
2636

2637
        r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
13✔
2638
        if (r < 0)
13✔
2639
                return bus_log_create_error(r);
×
2640

2641
        if (!strv_isempty(arg_cmdline)) {
13✔
2642
                r = sd_bus_message_open_container(m, 'r', "sa(sv)");
13✔
2643
                if (r < 0)
13✔
2644
                        return bus_log_create_error(r);
×
2645

2646
                r = sd_bus_message_append(m, "s", service);
13✔
2647
                if (r < 0)
13✔
2648
                        return bus_log_create_error(r);
×
2649

2650
                r = sd_bus_message_open_container(m, 'a', "(sv)");
13✔
2651
                if (r < 0)
13✔
2652
                        return bus_log_create_error(r);
×
2653

2654
                r = transient_service_set_properties(m, /* pty_path = */ NULL, /* pty_fd = */ -EBADF);
13✔
2655
                if (r < 0)
13✔
2656
                        return r;
2657

2658
                r = sd_bus_message_close_container(m);
13✔
2659
                if (r < 0)
13✔
2660
                        return bus_log_create_error(r);
×
2661

2662
                r = sd_bus_message_close_container(m);
13✔
2663
                if (r < 0)
13✔
2664
                        return bus_log_create_error(r);
×
2665
        }
2666

2667
        r = sd_bus_message_close_container(m);
13✔
2668
        if (r < 0)
13✔
2669
                return bus_log_create_error(r);
×
2670

2671
        *message = TAKE_PTR(m);
13✔
2672
        return 0;
13✔
2673
}
2674

2675
static int start_transient_trigger(sd_bus *bus, const char *suffix) {
18✔
2676
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
31✔
2677
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
2678
        _cleanup_free_ char *trigger = NULL, *service = NULL;
18✔
2679
        const char *object = NULL;
18✔
2680
        int r;
18✔
2681

2682
        assert(bus);
18✔
2683
        assert(suffix);
18✔
2684

2685
        r = bus_wait_for_jobs_new(bus, &w);
18✔
2686
        if (r < 0)
18✔
2687
                return log_error_errno(r, "Could not watch jobs: %m");
×
2688

2689
        if (arg_unit) {
18✔
2690
                switch (unit_name_to_type(arg_unit)) {
6✔
2691

2692
                case UNIT_SERVICE:
1✔
2693
                        service = strdup(arg_unit);
1✔
2694
                        if (!service)
1✔
2695
                                return log_oom();
×
2696

2697
                        r = unit_name_change_suffix(service, suffix, &trigger);
1✔
2698
                        if (r < 0)
1✔
2699
                                return log_error_errno(r, "Failed to change unit suffix: %m");
×
2700
                        break;
2701

2702
                case UNIT_TIMER:
×
2703
                        trigger = strdup(arg_unit);
×
2704
                        if (!trigger)
×
2705
                                return log_oom();
×
2706

2707
                        r = unit_name_change_suffix(trigger, ".service", &service);
×
2708
                        if (r < 0)
×
2709
                                return log_error_errno(r, "Failed to change unit suffix: %m");
×
2710
                        break;
2711

2712
                default:
5✔
2713
                        r = unit_name_mangle_with_suffix(arg_unit, "as unit",
5✔
2714
                                                         arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
5✔
2715
                                                         ".service", &service);
2716
                        if (r < 0)
5✔
2717
                                return log_error_errno(r, "Failed to mangle unit name: %m");
×
2718

2719
                        r = unit_name_mangle_with_suffix(arg_unit, "as trigger",
5✔
2720
                                                         arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
5✔
2721
                                                         suffix, &trigger);
2722
                        if (r < 0)
5✔
2723
                                return log_error_errno(r, "Failed to mangle unit name: %m");
×
2724

2725
                        break;
2726
                }
2727
        } else {
2728
                r = make_unit_name(UNIT_SERVICE, &service);
12✔
2729
                if (r < 0)
12✔
2730
                        return r;
2731

2732
                r = unit_name_change_suffix(service, suffix, &trigger);
12✔
2733
                if (r < 0)
12✔
2734
                        return log_error_errno(r, "Failed to change unit suffix: %m");
×
2735
        }
2736

2737
        r = make_transient_trigger_unit(bus, &m, suffix, trigger, service);
18✔
2738
        if (r < 0)
18✔
2739
                return r;
2740

2741
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
13✔
2742
        (void) ask_password_agent_open_if_enabled(arg_transport, arg_ask_password);
13✔
2743

2744
        r = bus_call_with_hint(bus, m, suffix + 1, &reply);
13✔
2745
        if (r < 0)
13✔
2746
                return r;
2747

2748
        r = sd_bus_message_read(reply, "o", &object);
8✔
2749
        if (r < 0)
8✔
2750
                return bus_log_parse_error(r);
×
2751

2752
        r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
8✔
2753
                                  arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
8✔
2754
        if (r < 0)
8✔
2755
                return r;
2756

2757
        if (!arg_quiet) {
8✔
2758
                log_info("Running %s as unit: %s", suffix + 1, trigger);
8✔
2759
                if (!strv_isempty(arg_cmdline))
8✔
2760
                        log_info("Will run service as unit: %s", service);
18✔
2761
        }
2762

2763
        return EXIT_SUCCESS;
2764
}
2765

2766
static bool shall_make_executable_absolute(void) {
1,156✔
2767
        if (arg_exec_path)
1,156✔
2768
                return false;
2769
        if (strv_isempty(arg_cmdline))
1,156✔
2770
                return false;
2771
        if (arg_transport != BUS_TRANSPORT_LOCAL)
1,156✔
2772
                return false;
2773

2774
        FOREACH_STRING(f, "RootDirectory=", "RootImage=", "ExecSearchPath=", "MountImages=", "ExtensionImages=")
6,399✔
2775
                if (strv_find_startswith(arg_property, f))
5,359✔
2776
                        return false;
72✔
2777

2778
        return true;
1,040✔
2779
}
2780

2781
static int run(int argc, char* argv[]) {
1,168✔
2782
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1,151✔
2783
        int r;
1,168✔
2784

2785
        log_setup();
1,168✔
2786

2787
        if (invoked_as(argv, "run0"))
1,168✔
2788
                r = parse_argv_sudo_mode(argc, argv);
146✔
2789
        else
2790
                r = parse_argv(argc, argv);
1,022✔
2791
        if (r <= 0)
1,168✔
2792
                return r;
2793

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

2799
                _cleanup_free_ char *command = NULL;
1,040✔
2800
                r = find_executable(arg_cmdline[0], &command);
1,040✔
2801
                if (ERRNO_IS_NEG_PRIVILEGE(r))
1,040✔
2802
                        log_debug_errno(r, "Failed to find executable '%s' due to permission problems, leaving path as is: %m", arg_cmdline[0]);
1,039✔
2803
                else if (r < 0)
1,040✔
2804
                        return log_error_errno(r, "Failed to find executable %s: %m", arg_cmdline[0]);
1✔
2805
                else
2806
                        free_and_replace(arg_cmdline[0], command);
1,039✔
2807
        }
2808

2809
        if (!arg_description) {
1,155✔
2810
                _cleanup_free_ char *t = NULL;
1,154✔
2811

2812
                if (strv_isempty(arg_cmdline))
1,154✔
2813
                        t = strdup(arg_unit);
×
2814
                else if (startswith(arg_cmdline[0], "-")) {
1,154✔
2815
                        /* Drop the login shell marker from the command line when generating the description,
2816
                         * in order to minimize user confusion. */
2817
                        _cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
1✔
2818
                        if (!l)
1✔
2819
                                return log_oom();
×
2820

2821
                        r = free_and_strdup_warn(l + 0, l[0] + 1);
1✔
2822
                        if (r < 0)
1✔
2823
                                return r;
2824

2825
                        t = quote_command_line(l, SHELL_ESCAPE_EMPTY);
1✔
2826
                } else
2827
                        t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY);
1,153✔
2828
                if (!t)
1,154✔
2829
                        return log_oom();
×
2830

2831
                arg_description = strjoin("[", program_invocation_short_name, "] ", t);
1,154✔
2832
                if (!arg_description)
1,154✔
2833
                        return log_oom();
×
2834
        }
2835

2836
        r = connect_bus(&bus);
1,155✔
2837
        if (r < 0)
1,155✔
2838
                return r;
2839

2840
        if (arg_scope)
1,151✔
2841
                return start_transient_scope(bus);
19✔
2842
        if (arg_path_property)
1,132✔
2843
                return start_transient_trigger(bus, ".path");
1✔
2844
        if (arg_socket_property)
1,131✔
2845
                return start_transient_trigger(bus, ".socket");
2✔
2846
        if (arg_with_timer)
1,129✔
2847
                return start_transient_trigger(bus, ".timer");
15✔
2848
        return start_transient_service(bus);
1,114✔
2849
}
2850

2851
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
2,336✔
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