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 log_error("Failed to set nice level: %m");
497 if (arg_exec_group) {
500 r = get_group_creds(&arg_exec_group, &gid);
502 log_error("Failed to resolve group %s: %s", arg_exec_group, strerror(-r));
506 if (setresgid(gid, gid, gid) < 0) {
507 log_error("Failed to change GID to " GID_FMT ": %m", gid);
513 const char *home, *shell;
517 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
519 log_error("Failed to resolve user %s: %s", arg_exec_user, strerror(-r));
523 r = strv_extendf(&user_env, "HOME=%s", home);
527 r = strv_extendf(&user_env, "SHELL=%s", shell);
531 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
535 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
539 if (!arg_exec_group) {
540 if (setresgid(gid, gid, gid) < 0) {
541 log_error("Failed to change GID to " GID_FMT ": %m", gid);
546 if (setresuid(uid, uid, uid) < 0) {
547 log_error("Failed to change UID to " UID_FMT ": %m", uid);
552 env = strv_env_merge(3, environ, user_env, arg_environment);
556 log_info("Running as unit %s.", name);
558 execvpe(argv[0], argv, env);
559 log_error("Failed to execute: %m");
563 int main(int argc, char* argv[]) {
564 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
565 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
566 _cleanup_free_ char *description = NULL, *command = NULL;
569 log_parse_environment();
572 r = parse_argv(argc, argv);
576 r = find_binary(argv[optind], &command);
578 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
581 argv[optind] = command;
583 if (!arg_description) {
584 description = strv_join(argv + optind, " ");
590 arg_description = description;
593 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
595 log_error("Failed to create bus connection: %s", strerror(-r));
600 r = start_transient_scope(bus, argv + optind, &error);
602 r = start_transient_service(bus, argv + optind, &error);
605 strv_free(arg_environment);
606 strv_free(arg_property);
608 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;