]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/run/run.c
treewide: use log_*_errno whenever %m is in the format string
[thirdparty/systemd.git] / src / run / run.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <getopt.h>
24
25 #include "sd-bus.h"
26 #include "bus-util.h"
27 #include "strv.h"
28 #include "build.h"
29 #include "unit-name.h"
30 #include "env-util.h"
31 #include "path-util.h"
32 #include "bus-error.h"
33
34 static bool arg_scope = false;
35 static bool arg_remain_after_exit = false;
36 static const char *arg_unit = NULL;
37 static const char *arg_description = NULL;
38 static const char *arg_slice = NULL;
39 static bool arg_send_sighup = false;
40 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
41 static const char *arg_host = NULL;
42 static bool arg_user = false;
43 static const char *arg_service_type = NULL;
44 static const char *arg_exec_user = NULL;
45 static const char *arg_exec_group = NULL;
46 static int arg_nice = 0;
47 static bool arg_nice_set = false;
48 static char **arg_environment = NULL;
49 static char **arg_property = NULL;
50
51 static void help(void) {
52 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
53 "Run the specified command in a transient scope or service unit.\n\n"
54 " -h --help Show this help\n"
55 " --version Show package version\n"
56 " --user Run as user unit\n"
57 " -H --host=[USER@]HOST Operate on remote host\n"
58 " -M --machine=CONTAINER Operate on local container\n"
59 " --scope Run this as scope rather than service\n"
60 " --unit=UNIT Run under the specified unit name\n"
61 " -p --property=NAME=VALUE Set unit property\n"
62 " --description=TEXT Description for unit\n"
63 " --slice=SLICE Run in the specified slice\n"
64 " -r --remain-after-exit Leave service around until explicitly stopped\n"
65 " --send-sighup Send SIGHUP when terminating\n"
66 " --service-type=TYPE Service type\n"
67 " --uid=USER Run as system user\n"
68 " --gid=GROUP Run as system group\n"
69 " --nice=NICE Nice level\n"
70 " --setenv=NAME=VALUE Set environment\n",
71 program_invocation_short_name);
72 }
73
74 static int parse_argv(int argc, char *argv[]) {
75
76 enum {
77 ARG_VERSION = 0x100,
78 ARG_USER,
79 ARG_SYSTEM,
80 ARG_SCOPE,
81 ARG_UNIT,
82 ARG_DESCRIPTION,
83 ARG_SLICE,
84 ARG_SEND_SIGHUP,
85 ARG_EXEC_USER,
86 ARG_EXEC_GROUP,
87 ARG_SERVICE_TYPE,
88 ARG_NICE,
89 ARG_SETENV
90 };
91
92 static const struct option options[] = {
93 { "help", no_argument, NULL, 'h' },
94 { "version", no_argument, NULL, ARG_VERSION },
95 { "user", no_argument, NULL, ARG_USER },
96 { "system", no_argument, NULL, ARG_SYSTEM },
97 { "scope", no_argument, NULL, ARG_SCOPE },
98 { "unit", required_argument, NULL, ARG_UNIT },
99 { "description", required_argument, NULL, ARG_DESCRIPTION },
100 { "slice", required_argument, NULL, ARG_SLICE },
101 { "remain-after-exit", no_argument, NULL, 'r' },
102 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
103 { "host", required_argument, NULL, 'H' },
104 { "machine", required_argument, NULL, 'M' },
105 { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
106 { "uid", required_argument, NULL, ARG_EXEC_USER },
107 { "gid", required_argument, NULL, ARG_EXEC_GROUP },
108 { "nice", required_argument, NULL, ARG_NICE },
109 { "setenv", required_argument, NULL, ARG_SETENV },
110 { "property", required_argument, NULL, 'p' },
111 {},
112 };
113
114 int r, c;
115
116 assert(argc >= 0);
117 assert(argv);
118
119 while ((c = getopt_long(argc, argv, "+hrH:M:p:", options, NULL)) >= 0)
120
121 switch (c) {
122
123 case 'h':
124 help();
125 return 0;
126
127 case ARG_VERSION:
128 puts(PACKAGE_STRING);
129 puts(SYSTEMD_FEATURES);
130 return 0;
131
132 case ARG_USER:
133 arg_user = true;
134 break;
135
136 case ARG_SYSTEM:
137 arg_user = false;
138 break;
139
140 case ARG_SCOPE:
141 arg_scope = true;
142 break;
143
144 case ARG_UNIT:
145 arg_unit = optarg;
146 break;
147
148 case ARG_DESCRIPTION:
149 arg_description = optarg;
150 break;
151
152 case ARG_SLICE:
153 arg_slice = optarg;
154 break;
155
156 case ARG_SEND_SIGHUP:
157 arg_send_sighup = true;
158 break;
159
160 case 'r':
161 arg_remain_after_exit = true;
162 break;
163
164 case 'H':
165 arg_transport = BUS_TRANSPORT_REMOTE;
166 arg_host = optarg;
167 break;
168
169 case 'M':
170 arg_transport = BUS_TRANSPORT_CONTAINER;
171 arg_host = optarg;
172 break;
173
174 case ARG_SERVICE_TYPE:
175 arg_service_type = optarg;
176 break;
177
178 case ARG_EXEC_USER:
179 arg_exec_user = optarg;
180 break;
181
182 case ARG_EXEC_GROUP:
183 arg_exec_group = optarg;
184 break;
185
186 case ARG_NICE:
187 r = safe_atoi(optarg, &arg_nice);
188 if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) {
189 log_error("Failed to parse nice value");
190 return -EINVAL;
191 }
192
193 arg_nice_set = true;
194 break;
195
196 case ARG_SETENV:
197
198 if (strv_extend(&arg_environment, optarg) < 0)
199 return log_oom();
200
201 break;
202
203 case 'p':
204
205 if (strv_extend(&arg_property, optarg) < 0)
206 return log_oom();
207
208 break;
209
210 case '?':
211 return -EINVAL;
212
213 default:
214 assert_not_reached("Unhandled option");
215 }
216
217 if (optind >= argc) {
218 log_error("Command line to execute required.");
219 return -EINVAL;
220 }
221
222 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
223 log_error("Execution in user context is not supported on non-local systems.");
224 return -EINVAL;
225 }
226
227 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
228 log_error("Scope execution is not supported on non-local systems.");
229 return -EINVAL;
230 }
231
232 if (arg_scope && (arg_remain_after_exit || arg_service_type)) {
233 log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
234 return -EINVAL;
235 }
236
237 return 1;
238 }
239
240 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
241 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
242 char **i;
243 int r;
244
245 assert(bus);
246 assert(name);
247 assert(ret);
248
249 r = sd_bus_message_new_method_call(
250 bus,
251 &m,
252 "org.freedesktop.systemd1",
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
255 "StartTransientUnit");
256 if (r < 0)
257 return r;
258
259 r = sd_bus_message_append(m, "ss", name, "fail");
260 if (r < 0)
261 return r;
262
263 r = sd_bus_message_open_container(m, 'a', "(sv)");
264 if (r < 0)
265 return r;
266
267 STRV_FOREACH(i, arg_property) {
268 r = sd_bus_message_open_container(m, 'r', "sv");
269 if (r < 0)
270 return r;
271
272 r = bus_append_unit_property_assignment(m, *i);
273 if (r < 0)
274 return r;
275
276 r = sd_bus_message_close_container(m);
277 if (r < 0)
278 return r;
279 }
280
281 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
282 if (r < 0)
283 return r;
284
285 if (!isempty(arg_slice)) {
286 _cleanup_free_ char *slice;
287
288 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
289 if (!slice)
290 return -ENOMEM;
291
292 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
293 if (r < 0)
294 return r;
295 }
296
297 if (arg_send_sighup) {
298 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
299 if (r < 0)
300 return r;
301 }
302
303 *ret = m;
304 m = NULL;
305
306 return 0;
307 }
308
309 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
310 int r;
311
312 assert(bus);
313 assert(m);
314
315 r = sd_bus_message_close_container(m);
316 if (r < 0)
317 return r;
318
319 r = sd_bus_message_append(m, "a(sa(sv))", 0);
320 if (r < 0)
321 return r;
322
323 return sd_bus_call(bus, m, 0, error, reply);
324 }
325
326 static int start_transient_service(
327 sd_bus *bus,
328 char **argv,
329 sd_bus_error *error) {
330
331 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
332 _cleanup_free_ char *name = NULL;
333 int r;
334
335 if (arg_unit) {
336 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
337 if (!name)
338 return log_oom();
339 } else if (asprintf(&name, "run-"PID_FMT".service", getpid()) < 0)
340 return log_oom();
341
342 r = message_start_transient_unit_new(bus, name, &m);
343 if (r < 0)
344 return bus_log_create_error(r);
345
346 if (arg_remain_after_exit) {
347 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
348 if (r < 0)
349 return bus_log_create_error(r);
350 }
351
352 if (arg_service_type) {
353 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
354 if (r < 0)
355 return bus_log_create_error(r);
356 }
357
358 if (arg_exec_user) {
359 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
360 if (r < 0)
361 return bus_log_create_error(r);
362 }
363
364 if (arg_exec_group) {
365 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
366 if (r < 0)
367 return bus_log_create_error(r);
368 }
369
370 if (arg_nice_set) {
371 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
372 if (r < 0)
373 return bus_log_create_error(r);
374 }
375
376 if (!strv_isempty(arg_environment)) {
377 r = sd_bus_message_open_container(m, 'r', "sv");
378 if (r < 0)
379 return bus_log_create_error(r);
380
381 r = sd_bus_message_append(m, "s", "Environment");
382 if (r < 0)
383 return bus_log_create_error(r);
384
385 r = sd_bus_message_open_container(m, 'v', "as");
386 if (r < 0)
387 return bus_log_create_error(r);
388
389 r = sd_bus_message_append_strv(m, arg_environment);
390 if (r < 0)
391 return bus_log_create_error(r);
392
393 r = sd_bus_message_close_container(m);
394 if (r < 0)
395 return bus_log_create_error(r);
396
397 r = sd_bus_message_close_container(m);
398 if (r < 0)
399 return bus_log_create_error(r);
400 }
401
402 r = sd_bus_message_open_container(m, 'r', "sv");
403 if (r < 0)
404 return bus_log_create_error(r);
405
406 r = sd_bus_message_append(m, "s", "ExecStart");
407 if (r < 0)
408 return bus_log_create_error(r);
409
410 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
411 if (r < 0)
412 return bus_log_create_error(r);
413
414 r = sd_bus_message_open_container(m, 'a', "(sasb)");
415 if (r < 0)
416 return bus_log_create_error(r);
417
418 r = sd_bus_message_open_container(m, 'r', "sasb");
419 if (r < 0)
420 return bus_log_create_error(r);
421
422 r = sd_bus_message_append(m, "s", argv[0]);
423 if (r < 0)
424 return bus_log_create_error(r);
425
426 r = sd_bus_message_append_strv(m, argv);
427 if (r < 0)
428 return bus_log_create_error(r);
429
430 r = sd_bus_message_append(m, "b", false);
431 if (r < 0)
432 return bus_log_create_error(r);
433
434 r = sd_bus_message_close_container(m);
435 if (r < 0)
436 return bus_log_create_error(r);
437
438 r = sd_bus_message_close_container(m);
439 if (r < 0)
440 return bus_log_create_error(r);
441
442 r = sd_bus_message_close_container(m);
443 if (r < 0)
444 return bus_log_create_error(r);
445
446 r = sd_bus_message_close_container(m);
447 if (r < 0)
448 return bus_log_create_error(r);
449
450 r = message_start_transient_unit_send(bus, m, error, NULL);
451 if (r < 0)
452 return bus_log_create_error(r);
453
454 log_info("Running as unit %s.", name);
455
456 return 0;
457 }
458
459 static int start_transient_scope(
460 sd_bus *bus,
461 char **argv,
462 sd_bus_error *error) {
463
464 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
465 _cleanup_free_ char *name = NULL;
466 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
467 int r;
468
469 assert(bus);
470
471 if (arg_unit) {
472 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
473 if (!name)
474 return log_oom();
475 } else if (asprintf(&name, "run-"PID_FMT".scope", getpid()) < 0)
476 return log_oom();
477
478 r = message_start_transient_unit_new(bus, name, &m);
479 if (r < 0)
480 return bus_log_create_error(r);
481
482 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
483 if (r < 0)
484 return bus_log_create_error(r);
485
486 r = message_start_transient_unit_send(bus, m, error, NULL);
487 if (r < 0)
488 return bus_log_create_error(r);
489
490 if (arg_nice_set) {
491 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) {
492 log_error_errno(errno, "Failed to set nice level: %m");
493 return -errno;
494 }
495 }
496
497 if (arg_exec_group) {
498 gid_t gid;
499
500 r = get_group_creds(&arg_exec_group, &gid);
501 if (r < 0)
502 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
503
504 if (setresgid(gid, gid, gid) < 0) {
505 log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
506 return -errno;
507 }
508 }
509
510 if (arg_exec_user) {
511 const char *home, *shell;
512 uid_t uid;
513 gid_t gid;
514
515 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
516 if (r < 0)
517 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
518
519 r = strv_extendf(&user_env, "HOME=%s", home);
520 if (r < 0)
521 return log_oom();
522
523 r = strv_extendf(&user_env, "SHELL=%s", shell);
524 if (r < 0)
525 return log_oom();
526
527 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
528 if (r < 0)
529 return log_oom();
530
531 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
532 if (r < 0)
533 return log_oom();
534
535 if (!arg_exec_group) {
536 if (setresgid(gid, gid, gid) < 0) {
537 log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
538 return -errno;
539 }
540 }
541
542 if (setresuid(uid, uid, uid) < 0) {
543 log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
544 return -errno;
545 }
546 }
547
548 env = strv_env_merge(3, environ, user_env, arg_environment);
549 if (!env)
550 return log_oom();
551
552 log_info("Running as unit %s.", name);
553
554 execvpe(argv[0], argv, env);
555 log_error_errno(errno, "Failed to execute: %m");
556 return -errno;
557 }
558
559 int main(int argc, char* argv[]) {
560 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
561 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
562 _cleanup_free_ char *description = NULL, *command = NULL;
563 int r;
564
565 log_parse_environment();
566 log_open();
567
568 r = parse_argv(argc, argv);
569 if (r <= 0)
570 goto finish;
571
572 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
573 if (r < 0) {
574 log_error_errno(r, "Failed to find executable %s%s: %m",
575 argv[optind],
576 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
577 goto finish;
578 }
579 argv[optind] = command;
580
581 if (!arg_description) {
582 description = strv_join(argv + optind, " ");
583 if (!description) {
584 r = log_oom();
585 goto finish;
586 }
587
588 arg_description = description;
589 }
590
591 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
592 if (r < 0) {
593 log_error_errno(r, "Failed to create bus connection: %m");
594 goto finish;
595 }
596
597 if (arg_scope)
598 r = start_transient_scope(bus, argv + optind, &error);
599 else
600 r = start_transient_service(bus, argv + optind, &error);
601
602 finish:
603 strv_free(arg_environment);
604 strv_free(arg_property);
605
606 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
607 }