]> git.proxmox.com Git - systemd.git/blame - src/run/run.c
Imported Upstream version 217
[systemd.git] / src / run / run.c
CommitLineData
14228c0d
MB
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"
60f067b4 26#include "bus-util.h"
14228c0d
MB
27#include "strv.h"
28#include "build.h"
29#include "unit-name.h"
60f067b4 30#include "env-util.h"
14228c0d 31#include "path-util.h"
60f067b4 32#include "bus-error.h"
14228c0d
MB
33
34static bool arg_scope = false;
14228c0d
MB
35static bool arg_remain_after_exit = false;
36static const char *arg_unit = NULL;
37static const char *arg_description = NULL;
38static const char *arg_slice = NULL;
39static bool arg_send_sighup = false;
60f067b4
JS
40static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
41static const char *arg_host = NULL;
42static bool arg_user = false;
43static const char *arg_service_type = NULL;
44static const char *arg_exec_user = NULL;
45static const char *arg_exec_group = NULL;
46static int arg_nice = 0;
47static bool arg_nice_set = false;
48static char **arg_environment = NULL;
49static char **arg_property = NULL;
14228c0d 50
5eef597e 51static void help(void) {
14228c0d
MB
52 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
53 "Run the specified command in a transient scope or service unit.\n\n"
60f067b4
JS
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",
14228c0d 71 program_invocation_short_name);
14228c0d
MB
72}
73
74static int parse_argv(int argc, char *argv[]) {
75
76 enum {
77 ARG_VERSION = 0x100,
78 ARG_USER,
60f067b4 79 ARG_SYSTEM,
14228c0d
MB
80 ARG_SCOPE,
81 ARG_UNIT,
82 ARG_DESCRIPTION,
83 ARG_SLICE,
84 ARG_SEND_SIGHUP,
60f067b4
JS
85 ARG_EXEC_USER,
86 ARG_EXEC_GROUP,
87 ARG_SERVICE_TYPE,
88 ARG_NICE,
89 ARG_SETENV
14228c0d
MB
90 };
91
92 static const struct option options[] = {
60f067b4
JS
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 {},
14228c0d
MB
112 };
113
60f067b4 114 int r, c;
14228c0d
MB
115
116 assert(argc >= 0);
117 assert(argv);
118
5eef597e 119 while ((c = getopt_long(argc, argv, "+hrH:M:p:", options, NULL)) >= 0)
14228c0d
MB
120
121 switch (c) {
122
123 case 'h':
5eef597e
MP
124 help();
125 return 0;
14228c0d
MB
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
60f067b4
JS
136 case ARG_SYSTEM:
137 arg_user = false;
138 break;
139
14228c0d
MB
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
60f067b4
JS
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
14228c0d
MB
210 case '?':
211 return -EINVAL;
212
213 default:
60f067b4 214 assert_not_reached("Unhandled option");
14228c0d 215 }
14228c0d
MB
216
217 if (optind >= argc) {
218 log_error("Command line to execute required.");
219 return -EINVAL;
220 }
221
60f067b4
JS
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
14228c0d
MB
237 return 1;
238}
239
240static 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;
60f067b4 242 char **i;
14228c0d
MB
243 int r;
244
60f067b4
JS
245 assert(bus);
246 assert(name);
247 assert(ret);
14228c0d
MB
248
249 r = sd_bus_message_new_method_call(
250 bus,
60f067b4 251 &m,
14228c0d
MB
252 "org.freedesktop.systemd1",
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
60f067b4 255 "StartTransientUnit");
14228c0d
MB
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
60f067b4
JS
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
14228c0d
MB
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
60f067b4 288 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
14228c0d
MB
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
60f067b4
JS
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 }
14228c0d
MB
302
303 *ret = m;
304 m = NULL;
305
306 return 0;
307}
308
309static 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
60f067b4
JS
312 assert(bus);
313 assert(m);
314
14228c0d
MB
315 r = sd_bus_message_close_container(m);
316 if (r < 0)
317 return r;
318
60f067b4
JS
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);
14228c0d
MB
324}
325
326static int start_transient_service(
327 sd_bus *bus,
328 char **argv,
329 sd_bus_error *error) {
330
60f067b4 331 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
14228c0d 332 _cleanup_free_ char *name = NULL;
14228c0d
MB
333 int r;
334
5eef597e 335 if (arg_unit) {
60f067b4 336 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
5eef597e
MP
337 if (!name)
338 return log_oom();
339 } else if (asprintf(&name, "run-"PID_FMT".service", getpid()) < 0)
60f067b4 340 return log_oom();
14228c0d
MB
341
342 r = message_start_transient_unit_new(bus, name, &m);
343 if (r < 0)
60f067b4 344 return bus_log_create_error(r);
14228c0d 345
60f067b4
JS
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 }
14228c0d
MB
401
402 r = sd_bus_message_open_container(m, 'r', "sv");
403 if (r < 0)
60f067b4 404 return bus_log_create_error(r);
14228c0d
MB
405
406 r = sd_bus_message_append(m, "s", "ExecStart");
407 if (r < 0)
60f067b4 408 return bus_log_create_error(r);
14228c0d
MB
409
410 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
411 if (r < 0)
60f067b4 412 return bus_log_create_error(r);
14228c0d
MB
413
414 r = sd_bus_message_open_container(m, 'a', "(sasb)");
415 if (r < 0)
60f067b4 416 return bus_log_create_error(r);
14228c0d
MB
417
418 r = sd_bus_message_open_container(m, 'r', "sasb");
419 if (r < 0)
60f067b4 420 return bus_log_create_error(r);
14228c0d
MB
421
422 r = sd_bus_message_append(m, "s", argv[0]);
423 if (r < 0)
60f067b4 424 return bus_log_create_error(r);
14228c0d 425
60f067b4 426 r = sd_bus_message_append_strv(m, argv);
14228c0d 427 if (r < 0)
60f067b4 428 return bus_log_create_error(r);
14228c0d
MB
429
430 r = sd_bus_message_append(m, "b", false);
431 if (r < 0)
60f067b4 432 return bus_log_create_error(r);
14228c0d
MB
433
434 r = sd_bus_message_close_container(m);
435 if (r < 0)
60f067b4 436 return bus_log_create_error(r);
14228c0d
MB
437
438 r = sd_bus_message_close_container(m);
439 if (r < 0)
60f067b4 440 return bus_log_create_error(r);
14228c0d
MB
441
442 r = sd_bus_message_close_container(m);
443 if (r < 0)
60f067b4 444 return bus_log_create_error(r);
14228c0d
MB
445
446 r = sd_bus_message_close_container(m);
447 if (r < 0)
60f067b4
JS
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);
14228c0d 453
60f067b4
JS
454 log_info("Running as unit %s.", name);
455
456 return 0;
14228c0d
MB
457}
458
459static int start_transient_scope(
460 sd_bus *bus,
461 char **argv,
462 sd_bus_error *error) {
463
60f067b4 464 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
14228c0d 465 _cleanup_free_ char *name = NULL;
60f067b4 466 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
14228c0d
MB
467 int r;
468
60f067b4
JS
469 assert(bus);
470
5eef597e 471 if (arg_unit) {
60f067b4 472 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
5eef597e
MP
473 if (!name)
474 return log_oom();
475 } else if (asprintf(&name, "run-"PID_FMT".scope", getpid()) < 0)
60f067b4 476 return log_oom();
14228c0d
MB
477
478 r = message_start_transient_unit_new(bus, name, &m);
479 if (r < 0)
60f067b4 480 return bus_log_create_error(r);
14228c0d
MB
481
482 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
483 if (r < 0)
60f067b4 484 return bus_log_create_error(r);
14228c0d 485
60f067b4 486 r = message_start_transient_unit_send(bus, m, error, NULL);
14228c0d 487 if (r < 0)
60f067b4
JS
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("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 log_error("Failed to resolve group %s: %s", arg_exec_group, strerror(-r));
503 return r;
504 }
505
506 if (setresgid(gid, gid, gid) < 0) {
507 log_error("Failed to change GID to " GID_FMT ": %m", gid);
508 return -errno;
509 }
510 }
511
512 if (arg_exec_user) {
513 const char *home, *shell;
514 uid_t uid;
515 gid_t gid;
516
517 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
518 if (r < 0) {
519 log_error("Failed to resolve user %s: %s", arg_exec_user, strerror(-r));
520 return r;
521 }
522
523 r = strv_extendf(&user_env, "HOME=%s", home);
524 if (r < 0)
525 return log_oom();
526
527 r = strv_extendf(&user_env, "SHELL=%s", shell);
528 if (r < 0)
529 return log_oom();
530
531 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
532 if (r < 0)
533 return log_oom();
14228c0d 534
60f067b4
JS
535 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
536 if (r < 0)
537 return log_oom();
538
539 if (!arg_exec_group) {
540 if (setresgid(gid, gid, gid) < 0) {
541 log_error("Failed to change GID to " GID_FMT ": %m", gid);
542 return -errno;
543 }
544 }
545
546 if (setresuid(uid, uid, uid) < 0) {
547 log_error("Failed to change UID to " UID_FMT ": %m", uid);
548 return -errno;
549 }
550 }
551
552 env = strv_env_merge(3, environ, user_env, arg_environment);
553 if (!env)
554 return log_oom();
555
556 log_info("Running as unit %s.", name);
557
558 execvpe(argv[0], argv, env);
14228c0d
MB
559 log_error("Failed to execute: %m");
560 return -errno;
561}
562
563int main(int argc, char* argv[]) {
60f067b4 564 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
5eef597e 565 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
14228c0d
MB
566 _cleanup_free_ char *description = NULL, *command = NULL;
567 int r;
568
569 log_parse_environment();
570 log_open();
571
572 r = parse_argv(argc, argv);
573 if (r <= 0)
60f067b4 574 goto finish;
14228c0d
MB
575
576 r = find_binary(argv[optind], &command);
577 if (r < 0) {
578 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
60f067b4 579 goto finish;
14228c0d
MB
580 }
581 argv[optind] = command;
582
583 if (!arg_description) {
584 description = strv_join(argv + optind, " ");
585 if (!description) {
586 r = log_oom();
60f067b4 587 goto finish;
14228c0d
MB
588 }
589
590 arg_description = description;
591 }
592
60f067b4 593 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
14228c0d 594 if (r < 0) {
60f067b4
JS
595 log_error("Failed to create bus connection: %s", strerror(-r));
596 goto finish;
14228c0d
MB
597 }
598
599 if (arg_scope)
600 r = start_transient_scope(bus, argv + optind, &error);
601 else
602 r = start_transient_service(bus, argv + optind, &error);
14228c0d 603
60f067b4
JS
604finish:
605 strv_free(arg_environment);
606 strv_free(arg_property);
607
14228c0d
MB
608 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
609}