1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
29 #include "unit-name.h"
31 #include "path-util.h"
32 #include "bus-error.h"
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;
51 static int help(void) {
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);
77 static int parse_argv(int argc, char *argv[]) {
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' },
122 while ((c = getopt_long(argc, argv, "+hrH:M:p:", options, NULL)) >= 0) {
130 puts(PACKAGE_STRING);
131 puts(SYSTEMD_FEATURES);
150 case ARG_DESCRIPTION:
151 arg_description = optarg;
158 case ARG_SEND_SIGHUP:
159 arg_send_sighup = true;
163 arg_remain_after_exit = true;
167 arg_transport = BUS_TRANSPORT_REMOTE;
172 arg_transport = BUS_TRANSPORT_CONTAINER;
176 case ARG_SERVICE_TYPE:
177 arg_service_type = optarg;
181 arg_exec_user = optarg;
185 arg_exec_group = optarg;
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");
200 if (strv_extend(&arg_environment, optarg) < 0)
207 if (strv_extend(&arg_property, optarg) < 0)
216 assert_not_reached("Unhandled option");
220 if (optind >= argc) {
221 log_error("Command line to execute required.");
225 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
226 log_error("Execution in user context is not supported on non-local systems.");
230 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
231 log_error("Scope execution is not supported on non-local systems.");
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.");
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;
252 r = sd_bus_message_new_method_call(
255 "org.freedesktop.systemd1",
256 "/org/freedesktop/systemd1",
257 "org.freedesktop.systemd1.Manager",
258 "StartTransientUnit");
262 r = sd_bus_message_append(m, "ss", name, "fail");
266 r = sd_bus_message_open_container(m, 'a', "(sv)");
270 STRV_FOREACH(i, arg_property) {
271 r = sd_bus_message_open_container(m, 'r', "sv");
275 r = bus_append_unit_property_assignment(m, *i);
279 r = sd_bus_message_close_container(m);
284 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
288 if (!isempty(arg_slice)) {
289 _cleanup_free_ char *slice;
291 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
295 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
300 if (arg_send_sighup) {
301 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
312 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
318 r = sd_bus_message_close_container(m);
322 r = sd_bus_message_append(m, "a(sa(sv))", 0);
326 return sd_bus_call(bus, m, 0, error, reply);
329 static int start_transient_service(
332 sd_bus_error *error) {
334 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
335 _cleanup_free_ char *name = NULL;
339 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
341 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
345 r = message_start_transient_unit_new(bus, name, &m);
347 return bus_log_create_error(r);
349 if (arg_remain_after_exit) {
350 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
352 return bus_log_create_error(r);
355 if (arg_service_type) {
356 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
358 return bus_log_create_error(r);
362 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
364 return bus_log_create_error(r);
367 if (arg_exec_group) {
368 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
370 return bus_log_create_error(r);
374 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
376 return bus_log_create_error(r);
379 if (!strv_isempty(arg_environment)) {
380 r = sd_bus_message_open_container(m, 'r', "sv");
382 return bus_log_create_error(r);
384 r = sd_bus_message_append(m, "s", "Environment");
386 return bus_log_create_error(r);
388 r = sd_bus_message_open_container(m, 'v', "as");
390 return bus_log_create_error(r);
392 r = sd_bus_message_append_strv(m, arg_environment);
394 return bus_log_create_error(r);
396 r = sd_bus_message_close_container(m);
398 return bus_log_create_error(r);
400 r = sd_bus_message_close_container(m);
402 return bus_log_create_error(r);
405 r = sd_bus_message_open_container(m, 'r', "sv");
407 return bus_log_create_error(r);
409 r = sd_bus_message_append(m, "s", "ExecStart");
411 return bus_log_create_error(r);
413 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
415 return bus_log_create_error(r);
417 r = sd_bus_message_open_container(m, 'a', "(sasb)");
419 return bus_log_create_error(r);
421 r = sd_bus_message_open_container(m, 'r', "sasb");
423 return bus_log_create_error(r);
425 r = sd_bus_message_append(m, "s", argv[0]);
427 return bus_log_create_error(r);
429 r = sd_bus_message_append_strv(m, argv);
431 return bus_log_create_error(r);
433 r = sd_bus_message_append(m, "b", false);
435 return bus_log_create_error(r);
437 r = sd_bus_message_close_container(m);
439 return bus_log_create_error(r);
441 r = sd_bus_message_close_container(m);
443 return bus_log_create_error(r);
445 r = sd_bus_message_close_container(m);
447 return bus_log_create_error(r);
449 r = sd_bus_message_close_container(m);
451 return bus_log_create_error(r);
453 r = message_start_transient_unit_send(bus, m, error, NULL);
455 return bus_log_create_error(r);
457 log_info("Running as unit %s.", name);
462 static int start_transient_scope(
465 sd_bus_error *error) {
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;
475 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
477 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
481 r = message_start_transient_unit_new(bus, name, &m);
483 return bus_log_create_error(r);
485 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
487 return bus_log_create_error(r);
489 r = message_start_transient_unit_send(bus, m, error, NULL);
491 return bus_log_create_error(r);
494 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) {
495 log_error("Failed to set nice level: %m");
500 if (arg_exec_group) {
503 r = get_group_creds(&arg_exec_group, &gid);
505 log_error("Failed to resolve group %s: %s", arg_exec_group, strerror(-r));
509 if (setresgid(gid, gid, gid) < 0) {
510 log_error("Failed to change GID to " GID_FMT ": %m", gid);
516 const char *home, *shell;
520 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
522 log_error("Failed to resolve user %s: %s", arg_exec_user, strerror(-r));
526 r = strv_extendf(&user_env, "HOME=%s", home);
530 r = strv_extendf(&user_env, "SHELL=%s", shell);
534 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
538 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
542 if (!arg_exec_group) {
543 if (setresgid(gid, gid, gid) < 0) {
544 log_error("Failed to change GID to " GID_FMT ": %m", gid);
549 if (setresuid(uid, uid, uid) < 0) {
550 log_error("Failed to change UID to " UID_FMT ": %m", uid);
555 env = strv_env_merge(3, environ, user_env, arg_environment);
559 log_info("Running as unit %s.", name);
561 execvpe(argv[0], argv, env);
562 log_error("Failed to execute: %m");
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;
572 log_parse_environment();
575 r = parse_argv(argc, argv);
579 r = find_binary(argv[optind], &command);
581 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
584 argv[optind] = command;
586 if (!arg_description) {
587 description = strv_join(argv + optind, " ");
593 arg_description = description;
596 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
598 log_error("Failed to create bus connection: %s", strerror(-r));
603 r = start_transient_scope(bus, argv + optind, &error);
605 r = start_transient_service(bus, argv + optind, &error);
608 strv_free(arg_environment);
609 strv_free(arg_property);
611 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;