run.c revision 6348d701bd24afcca4857417e66756f752f02136
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <getopt.h>
#include <stdio.h>
#include "sd-bus.h"
#include "sd-event.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "calendarspec.h"
#include "env-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "formats-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "ptyfwd.h"
#include "signal-util.h"
#include "spawn-polkit-agent.h"
#include "strv.h"
#include "terminal-util.h"
#include "unit-name.h"
#include "user-util.h"
static bool arg_ask_password = true;
static bool arg_scope = false;
static bool arg_remain_after_exit = false;
static bool arg_no_block = false;
static const char *arg_description = NULL;
static bool arg_send_sighup = false;
static bool arg_user = false;
static const char *arg_service_type = NULL;
static const char *arg_exec_user = NULL;
static const char *arg_exec_group = NULL;
static int arg_nice = 0;
static bool arg_nice_set = false;
static char **arg_environment = NULL;
static char **arg_property = NULL;
static bool arg_pty = false;
static usec_t arg_on_active = 0;
static usec_t arg_on_boot = 0;
static usec_t arg_on_startup = 0;
static usec_t arg_on_unit_active = 0;
static usec_t arg_on_unit_inactive = 0;
static const char *arg_on_calendar = NULL;
static char **arg_timer_property = NULL;
static bool arg_quiet = false;
static void polkit_agent_open_if_enabled(void) {
/* Open the polkit agent as a child process if necessary */
if (!arg_ask_password)
return;
if (arg_transport != BUS_TRANSPORT_LOCAL)
return;
}
static void help(void) {
printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
"Run the specified command in a transient scope or service or timer\n"
"unit. If timer option is specified and unit is exist which is\n"
"specified with --unit option then command can be omitted.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-ask-password Do not prompt for password\n"
" --user Run as user unit\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" --scope Run this as scope rather than service\n"
" --unit=UNIT Run under the specified unit name\n"
" -p --property=NAME=VALUE Set unit property\n"
" --description=TEXT Description for unit\n"
" --slice=SLICE Run in the specified slice\n"
" --no-block Do not wait until operation finished\n"
" -r --remain-after-exit Leave service around until explicitly stopped\n"
" --send-sighup Send SIGHUP when terminating\n"
" --service-type=TYPE Service type\n"
" --uid=USER Run as system user\n"
" --gid=GROUP Run as system group\n"
" --nice=NICE Nice level\n"
" --setenv=NAME=VALUE Set environment\n"
" -t --pty Run service on pseudo tty\n"
" -q --quiet Suppress information messages during runtime\n\n"
"Timer options:\n\n"
" --on-active=SECONDS Run after SECONDS delay\n"
" --on-boot=SECONDS Run SECONDS after machine was booted up\n"
" --on-startup=SECONDS Run SECONDS after systemd activation\n"
" --on-unit-active=SECONDS Run SECONDS after the last activation\n"
" --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n"
" --on-calendar=SPEC Realtime timer\n"
" --timer-property=NAME=VALUE Set timer unit property\n",
}
static bool with_timer(void) {
return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar;
}
enum {
ARG_VERSION = 0x100,
};
{},
};
int r, c;
switch (c) {
case 'h':
help();
return 0;
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
case ARG_VERSION:
return version();
case ARG_USER:
arg_user = true;
break;
case ARG_SYSTEM:
arg_user = false;
break;
case ARG_SCOPE:
arg_scope = true;
break;
case ARG_UNIT:
break;
case ARG_DESCRIPTION:
break;
case ARG_SLICE:
break;
case ARG_SEND_SIGHUP:
arg_send_sighup = true;
break;
case 'r':
arg_remain_after_exit = true;
break;
case 'H':
break;
case 'M':
break;
case ARG_SERVICE_TYPE:
break;
case ARG_EXEC_USER:
break;
case ARG_EXEC_GROUP:
break;
case ARG_NICE:
log_error("Failed to parse nice value");
return -EINVAL;
}
arg_nice_set = true;
break;
case ARG_SETENV:
return log_oom();
break;
case 'p':
return log_oom();
break;
case 't':
arg_pty = true;
break;
case 'q':
arg_quiet = true;
break;
case ARG_ON_ACTIVE:
if (r < 0) {
return r;
}
break;
case ARG_ON_BOOT:
if (r < 0) {
return r;
}
break;
case ARG_ON_STARTUP:
if (r < 0) {
return r;
}
break;
case ARG_ON_UNIT_ACTIVE:
if (r < 0) {
return r;
}
break;
case ARG_ON_UNIT_INACTIVE:
if (r < 0) {
return r;
}
break;
case ARG_ON_CALENDAR: {
if (r < 0) {
return r;
}
break;
}
case ARG_TIMER_PROPERTY:
return log_oom();
break;
case ARG_NO_BLOCK:
arg_no_block = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
log_error("Command line to execute required.");
return -EINVAL;
}
log_error("Execution in user context is not supported on non-local systems.");
return -EINVAL;
}
log_error("Scope execution is not supported on non-local systems.");
return -EINVAL;
}
log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
return -EINVAL;
}
log_error("--pty is not compatible in timer or --scope mode.");
return -EINVAL;
}
log_error("--pty is only supported when connecting to the local system or containers.");
return -EINVAL;
}
if (arg_scope && with_timer()) {
log_error("Timer options are not supported in --scope mode.");
return -EINVAL;
}
if (arg_timer_property && !with_timer()) {
log_error("--timer-property= has no effect without any other timer options.");
return -EINVAL;
}
return 1;
}
char **i;
int r;
if (r < 0)
return r;
STRV_FOREACH(i, properties) {
if (r < 0)
return r;
r = bus_append_unit_property_assignment(m, *i);
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
}
return 0;
}
static int transient_cgroup_set_properties(sd_bus_message *m) {
int r;
assert(m);
_cleanup_free_ char *slice;
if (r < 0)
return r;
if (r < 0)
return r;
}
return 0;
}
static int transient_kill_set_properties(sd_bus_message *m) {
assert(m);
if (arg_send_sighup)
else
return 0;
}
int r;
assert(m);
if (r < 0)
return r;
r = transient_kill_set_properties(m);
if (r < 0)
return r;
r = transient_cgroup_set_properties(m);
if (r < 0)
return r;
if (arg_remain_after_exit) {
if (r < 0)
return r;
}
if (arg_service_type) {
if (r < 0)
return r;
}
if (arg_exec_user) {
if (r < 0)
return r;
}
if (arg_exec_group) {
if (r < 0)
return r;
}
if (arg_nice_set) {
if (r < 0)
return r;
}
if (pty_path) {
const char *e;
r = sd_bus_message_append(m,
"(sv)(sv)(sv)(sv)",
"StandardInput", "s", "tty",
"StandardOutput", "s", "tty",
"StandardError", "s", "tty",
if (r < 0)
return r;
e = getenv("TERM");
if (e) {
char *n;
n = strjoina("TERM=", e);
r = sd_bus_message_append(m,
"(sv)",
"Environment", "as", 1, n);
if (r < 0)
return r;
}
}
if (!strv_isempty(arg_environment)) {
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
}
/* Exec container */
{
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
r = sd_bus_message_append_strv(m, argv);
if (r < 0)
return r;
r = sd_bus_message_append(m, "b", false);
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
}
return 0;
}
static int transient_scope_set_properties(sd_bus_message *m) {
int r;
assert(m);
if (r < 0)
return r;
r = transient_kill_set_properties(m);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
static int transient_timer_set_properties(sd_bus_message *m) {
int r;
assert(m);
if (r < 0)
return r;
/* Automatically clean up our transient timers */
if (r < 0)
return r;
if (arg_on_active) {
if (r < 0)
return r;
}
if (arg_on_boot) {
if (r < 0)
return r;
}
if (arg_on_startup) {
if (r < 0)
return r;
}
if (arg_on_unit_active) {
if (r < 0)
return r;
}
if (arg_on_unit_inactive) {
if (r < 0)
return r;
}
if (arg_on_calendar) {
if (r < 0)
return r;
}
return 0;
}
char *p;
int r;
assert(t >= 0);
assert(t < _UNIT_TYPE_MAX);
if (r < 0) {
/* We couldn't get the unique name, which is a pretty
* common case if we are connected to systemd
* directly. In that case, just pick a random uuid as
* name */
r = sd_id128_randomize(&rnd);
if (r < 0)
return log_error_errno(r, "Failed to generate random run unit name: %m");
if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
return log_oom();
return 0;
}
/* We managed to get the unique name, then let's use that to
* name our transient units. */
if (!id) {
return -EINVAL;
}
if (!p)
return log_oom();
*ret = p;
return 0;
}
static int start_transient_service(
char **argv) {
int r;
if (arg_pty) {
if (arg_transport == BUS_TRANSPORT_LOCAL) {
if (master < 0)
if (r < 0)
return log_error_errno(r, "Failed to determine tty name: %m");
} else if (arg_transport == BUS_TRANSPORT_MACHINE) {
const char *s;
r = sd_bus_default_system(&system_bus);
if (r < 0)
return log_error_errno(r, "Failed to connect to system bus: %m");
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"OpenMachinePTY",
&error,
&reply,
"s", arg_host);
if (r < 0) {
return r;
}
if (r < 0)
return bus_log_parse_error(r);
if (master < 0)
if (!pty_path)
return log_oom();
} else
assert_not_reached("Can't allocate tty via ssh");
}
if (!arg_no_block) {
r = bus_wait_for_jobs_new(bus, &w);
if (r < 0)
return log_error_errno(r, "Could not watch jobs: %m");
}
if (arg_unit) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
} else {
if (r < 0)
return r;
}
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
/* Name and mode */
if (r < 0)
return bus_log_create_error(r);
/* Properties */
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
/* Auxiliary units */
r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return log_error_errno(r, "Failed to start transient service unit: %s", bus_error_message(&error, r));
if (w) {
const char *object;
if (r < 0)
return bus_log_parse_error(r);
if (r < 0)
return r;
}
if (master >= 0) {
char last_char = 0;
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
if (!arg_quiet)
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
} else if (!arg_quiet)
return 0;
}
static int start_transient_scope(
char **argv) {
int r;
r = bus_wait_for_jobs_new(bus, &w);
if (r < 0)
return log_oom();
if (arg_unit) {
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
} else {
if (r < 0)
return r;
}
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
/* Name and Mode */
if (r < 0)
return bus_log_create_error(r);
/* Properties */
if (r < 0)
return bus_log_create_error(r);
r = transient_scope_set_properties(m);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
/* Auxiliary units */
r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0)
return bus_log_create_error(r);
if (r < 0) {
return r;
}
if (arg_nice_set) {
}
if (arg_exec_group) {
if (r < 0)
}
if (arg_exec_user) {
if (r < 0)
if (r < 0)
return log_oom();
if (r < 0)
return log_oom();
if (r < 0)
return log_oom();
if (r < 0)
return log_oom();
if (!arg_exec_group) {
}
}
if (!env)
return log_oom();
if (r < 0)
return bus_log_parse_error(r);
if (r < 0)
return r;
if (!arg_quiet)
}
static int start_transient_timer(
char **argv) {
int r;
r = bus_wait_for_jobs_new(bus, &w);
if (r < 0)
return log_oom();
if (arg_unit) {
switch (unit_name_to_type(arg_unit)) {
case UNIT_SERVICE:
if (!service)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to change unit suffix: %m");
break;
case UNIT_TIMER:
if (!timer)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to change unit suffix: %m");
break;
default:
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
break;
}
} else {
if (r < 0)
return r;
if (r < 0)
return log_error_errno(r, "Failed to change unit suffix: %m");
}
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
/* Name and Mode */
if (r < 0)
return bus_log_create_error(r);
/* Properties */
if (r < 0)
return bus_log_create_error(r);
r = transient_timer_set_properties(m);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
if (argv[0]) {
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
}
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
if (r < 0) {
return r;
}
if (r < 0)
return bus_log_parse_error(r);
if (r < 0)
return r;
if (argv[0])
return 0;
}
int r;
log_open();
if (r <= 0)
goto finish;
/* Patch in an absolute path */
if (r < 0) {
goto finish;
}
}
if (!arg_description) {
if (!description) {
r = log_oom();
goto finish;
}
if (r < 0)
goto finish;
}
}
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
}
if (arg_scope)
else if (with_timer())
else
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}