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