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 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);
74 static int parse_argv(int argc, char *argv[]) {
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' },
119 while ((c = getopt_long(argc, argv, "+hrH:M:p:", options, NULL)) >= 0)
128 puts(PACKAGE_STRING);
129 puts(SYSTEMD_FEATURES);
148 case ARG_DESCRIPTION:
149 arg_description = optarg;
156 case ARG_SEND_SIGHUP:
157 arg_send_sighup = true;
161 arg_remain_after_exit = true;
165 arg_transport = BUS_TRANSPORT_REMOTE;
170 arg_transport = BUS_TRANSPORT_CONTAINER;
174 case ARG_SERVICE_TYPE:
175 arg_service_type = optarg;
179 arg_exec_user = optarg;
183 arg_exec_group = optarg;
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");
198 if (strv_extend(&arg_environment, optarg) < 0)
205 if (strv_extend(&arg_property, optarg) < 0)
214 assert_not_reached("Unhandled option");
217 if (optind >= argc) {
218 log_error("Command line to execute required.");
222 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
223 log_error("Execution in user context is not supported on non-local systems.");
227 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
228 log_error("Scope execution is not supported on non-local systems.");
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.");
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;
249 r = sd_bus_message_new_method_call(
252 "org.freedesktop.systemd1",
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
255 "StartTransientUnit");
259 r = sd_bus_message_append(m, "ss", name, "fail");
263 r = sd_bus_message_open_container(m, 'a', "(sv)");
267 STRV_FOREACH(i, arg_property) {
268 r = sd_bus_message_open_container(m, 'r', "sv");
272 r = bus_append_unit_property_assignment(m, *i);
276 r = sd_bus_message_close_container(m);
281 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
285 if (!isempty(arg_slice)) {
286 _cleanup_free_ char *slice;
288 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
292 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
297 if (arg_send_sighup) {
298 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
309 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
315 r = sd_bus_message_close_container(m);
319 r = sd_bus_message_append(m, "a(sa(sv))", 0);
323 return sd_bus_call(bus, m, 0, error, reply);
326 static int start_transient_service(
329 sd_bus_error *error) {
331 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
332 _cleanup_free_ char *name = NULL;
336 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
339 } else if (asprintf(&name, "run-"PID_FMT".service", getpid()) < 0)
342 r = message_start_transient_unit_new(bus, name, &m);
344 return bus_log_create_error(r);
346 if (arg_remain_after_exit) {
347 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
349 return bus_log_create_error(r);
352 if (arg_service_type) {
353 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
355 return bus_log_create_error(r);
359 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
361 return bus_log_create_error(r);
364 if (arg_exec_group) {
365 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
367 return bus_log_create_error(r);
371 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
373 return bus_log_create_error(r);
376 if (!strv_isempty(arg_environment)) {
377 r = sd_bus_message_open_container(m, 'r', "sv");
379 return bus_log_create_error(r);
381 r = sd_bus_message_append(m, "s", "Environment");
383 return bus_log_create_error(r);
385 r = sd_bus_message_open_container(m, 'v', "as");
387 return bus_log_create_error(r);
389 r = sd_bus_message_append_strv(m, arg_environment);
391 return bus_log_create_error(r);
393 r = sd_bus_message_close_container(m);
395 return bus_log_create_error(r);
397 r = sd_bus_message_close_container(m);
399 return bus_log_create_error(r);
402 r = sd_bus_message_open_container(m, 'r', "sv");
404 return bus_log_create_error(r);
406 r = sd_bus_message_append(m, "s", "ExecStart");
408 return bus_log_create_error(r);
410 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
412 return bus_log_create_error(r);
414 r = sd_bus_message_open_container(m, 'a', "(sasb)");
416 return bus_log_create_error(r);
418 r = sd_bus_message_open_container(m, 'r', "sasb");
420 return bus_log_create_error(r);
422 r = sd_bus_message_append(m, "s", argv[0]);
424 return bus_log_create_error(r);
426 r = sd_bus_message_append_strv(m, argv);
428 return bus_log_create_error(r);
430 r = sd_bus_message_append(m, "b", false);
432 return bus_log_create_error(r);
434 r = sd_bus_message_close_container(m);
436 return bus_log_create_error(r);
438 r = sd_bus_message_close_container(m);
440 return bus_log_create_error(r);
442 r = sd_bus_message_close_container(m);
444 return bus_log_create_error(r);
446 r = sd_bus_message_close_container(m);
448 return bus_log_create_error(r);
450 r = message_start_transient_unit_send(bus, m, error, NULL);
452 return bus_log_create_error(r);
454 log_info("Running as unit %s.", name);
459 static int start_transient_scope(
462 sd_bus_error *error) {
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;
472 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
475 } else if (asprintf(&name, "run-"PID_FMT".scope", getpid()) < 0)
478 r = message_start_transient_unit_new(bus, name, &m);
480 return bus_log_create_error(r);
482 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
484 return bus_log_create_error(r);
486 r = message_start_transient_unit_send(bus, m, error, NULL);
488 return bus_log_create_error(r);
491 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
492 return log_error_errno(errno, "Failed to set nice level: %m");
495 if (arg_exec_group) {
498 r = get_group_creds(&arg_exec_group, &gid);
500 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
502 if (setresgid(gid, gid, gid) < 0)
503 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
507 const char *home, *shell;
511 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
513 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
515 r = strv_extendf(&user_env, "HOME=%s", home);
519 r = strv_extendf(&user_env, "SHELL=%s", shell);
523 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
527 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
531 if (!arg_exec_group) {
532 if (setresgid(gid, gid, gid) < 0)
533 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
536 if (setresuid(uid, uid, uid) < 0)
537 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
540 env = strv_env_merge(3, environ, user_env, arg_environment);
544 log_info("Running as unit %s.", name);
546 execvpe(argv[0], argv, env);
547 log_error_errno(errno, "Failed to execute: %m");
551 int main(int argc, char* argv[]) {
552 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
553 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
554 _cleanup_free_ char *description = NULL, *command = NULL;
557 log_parse_environment();
560 r = parse_argv(argc, argv);
564 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
566 log_error_errno(r, "Failed to find executable %s%s: %m",
568 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
571 argv[optind] = command;
573 if (!arg_description) {
574 description = strv_join(argv + optind, " ");
580 arg_description = description;
583 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
585 log_error_errno(r, "Failed to create bus connection: %m");
590 r = start_transient_scope(bus, argv + optind, &error);
592 r = start_transient_service(bus, argv + optind, &error);
595 strv_free(arg_environment);
596 strv_free(arg_property);
598 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;