run: introduce timer support option

Support timer options --on-active=, --on-boot=, --on-startup=,
--on-unit-active=, --on-unit-inactive=, --on-calendar=. Each options
corresponding with OnActiveSec=, OnBootSec=, OnStartupSec=,
OnUnitActiveSec=, OnUnitInactiveSec=, OnCalendar= of timer
respectively. And OnCalendar= and WakeSystem= supported by
--timer-property= option like --property= of systemd-run.

And if --unit= option and timer options are specified the command can
be omitted. In this case, systemd-run assumes the target service is
already loaded. And just try to generate transient timer unit only.
This commit is contained in:
WaLyong Cho 2014-12-09 16:07:16 +09:00 committed by Lennart Poettering
parent e82959c0e3
commit 4c213d6cf4
4 changed files with 631 additions and 183 deletions

View file

@ -45,7 +45,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
<refnamediv> <refnamediv>
<refname>systemd-run</refname> <refname>systemd-run</refname>
<refpurpose>Run programs in transient scope or service units</refpurpose> <refpurpose>Run programs in transient scope or service or timer units</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
@ -56,15 +56,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
<arg choice="opt" rep="repeat">ARGS</arg> <arg choice="opt" rep="repeat">ARGS</arg>
</arg> </arg>
</cmdsynopsis> </cmdsynopsis>
<cmdsynopsis>
<command>systemd-run</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="opt" rep="repeat">TIMER OPTIONS</arg>
<arg choice="req"><replaceable>COMMAND</replaceable></arg>
<arg choice="opt" rep="repeat">ARGS</arg>
</cmdsynopsis>
</refsynopsisdiv> </refsynopsisdiv>
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para><command>systemd-run</command> may be used to create and start <para><command>systemd-run</command> may be used to create and
a transient <filename>.service</filename> or a start a transient <filename>.service</filename> or a transient
<filename>.scope</filename> unit and run the specified <filename>.timer</filename> or a <filename>.scope</filename> unit
<replaceable>COMMAND</replaceable> in it.</para> and run the specified <replaceable>COMMAND</replaceable> in
it.</para>
<para>If a command is run as transient service unit, it will be <para>If a command is run as transient service unit, it will be
started and managed by the service manager like any other service, started and managed by the service manager like any other service,
@ -74,6 +82,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
will start the service asynchronously in the background and will start the service asynchronously in the background and
immediately return.</para> immediately return.</para>
<para>If a command is run with timer options, transient timer unit
also be created with transient service unit. But the transient
timer unit is only started immediately. The transient service unit
will be started when the transient timer is elapsed. If
<option>--unit=</option> is specified with timer options, the
<replaceable>COMMAND</replaceable> can be omitted. In this case,
<command>systemd-run</command> assumes service unit is already
loaded and creates transient timer unit only. To successfully
create timer unit, already loaded service unit should be specified
with <option>--unit=</option>. This transient timer unit can
activate the existing service unit like any other timer.</para>
<para>If a command is run as transient scope unit, it will be <para>If a command is run as transient scope unit, it will be
started directly by <command>systemd-run</command> and thus started directly by <command>systemd-run</command> and thus
inherit the execution environment of the caller. It is however inherit the execution environment of the caller. It is however
@ -210,6 +230,54 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
<xi:include href="user-system-options.xml" xpointer="host" /> <xi:include href="user-system-options.xml" xpointer="host" />
<xi:include href="user-system-options.xml" xpointer="machine" /> <xi:include href="user-system-options.xml" xpointer="machine" />
<varlistentry>
<term><option>--on-active=</option></term>
<term><option>--on-boot=</option></term>
<term><option>--on-startup=</option></term>
<term><option>--on-unit-active=</option></term>
<term><option>--on-unit-inactive=</option></term>
<listitem><para>Defines monotonic timers relative to different
starting points. Also see <varname>OnActiveSec=</varname>,
<varname>OnBootSec=</varname>,
<varname>OnStartupSec=</varname>,
<varname>OnUnitActiveSec=</varname> and
<varname>OnUnitInactiveSec=</varname> in
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
options have no effect in conjunction with
<option>--scope</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--on-calendar=</option></term>
<listitem><para>Defines realtime (i.e. wallclock) timers with
calendar event expressions. Also see
<varname>OnCalendar=</varname> in
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
option has no effect in conjunction with
<option>--scope</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--timer-property=</option></term>
<listitem><para>Sets a timer unit property for the timer unit
that is created. It is similar with
<option>--property</option> but only for created timer
unit. This option only has effect in conjunction with
<option>--on-active=</option>, <option>--on-boot=</option>,
<option>--on-startup=</option>,
<option>--on-unit-active=</option>,
<option>--on-unit-inactive=</option>,
<option>--on-calendar=</option>. This takes an assignment in
the same format as
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>set-property</command> command.</para> </listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" /> <xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" /> <xi:include href="standard-options.xml" xpointer="version" />
</variablelist> </variablelist>
@ -250,6 +318,21 @@ Sep 08 07:37:21 bupkis env[19948]: BOOT_IMAGE=/vmlinuz-3.11.0-0.rc5.git6.2.fc20.
property.</para> property.</para>
<programlisting># systemd-run -p BlockIOWeight=10 updatedb</programlisting> <programlisting># systemd-run -p BlockIOWeight=10 updatedb</programlisting>
<para>The following command will touch a file after 30 seconds.</para>
<programlisting># date; systemd-run --on-active=30 --timer-property=AccuracySec=100ms /bin/touch /tmp/foo
Mon Dec 8 20:44:24 KST 2014
Running as unit run-71.timer.
Will run as unit run-71.service.
# journalctl -b -u run-73.timer
-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. --
Dec 08 20:44:38 container systemd[1]: Starting /bin/touch /tmp/foo.
Dec 08 20:44:38 container systemd[1]: Started /bin/touch /tmp/foo.
# journalctl -b -u run-73.service
-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. --
Dec 08 20:44:48 container systemd[1]: Starting /bin/touch /tmp/foo...
Dec 08 20:44:48 container systemd[1]: Started /bin/touch /tmp/foo.</programlisting>
</refsect1> </refsect1>
<refsect1> <refsect1>
@ -263,6 +346,7 @@ Sep 08 07:37:21 bupkis env[19948]: BOOT_IMAGE=/vmlinuz-3.11.0-0.rc5.git6.2.fc20.
<citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para> </para>
</refsect1> </refsect1>

View file

@ -681,9 +681,11 @@ static int transient_aux_units_from_message(
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
return r; return r;
r = unit_load(u); if (r != -EEXIST) {
if (r < 0) r = unit_load(u);
return r; if (r < 0)
return r;
}
r = sd_bus_message_exit_container(message); r = sd_bus_message_exit_container(message);
if (r < 0) if (r < 0)

View file

@ -1372,7 +1372,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (STR_IN_SET(field, if (STR_IN_SET(field,
"CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
"SendSIGHUP", "SendSIGKILL")) { "SendSIGHUP", "SendSIGKILL",
"WakeSystem")) {
r = parse_boolean(eq); r = parse_boolean(eq);
if (r < 0) { if (r < 0) {
@ -1533,6 +1534,17 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "i", sig); r = sd_bus_message_append(m, "v", "i", sig);
} else if (streq(field, "AccuracySec")) {
usec_t u;
r = parse_sec(eq, &u);
if (r < 0) {
log_error("Failed to parse %s value %s", field, eq);
return -EINVAL;
}
r = sd_bus_message_append(m, "v", "t", u);
} else { } else {
log_error("Unknown assignment %s.", assignment); log_error("Unknown assignment %s.", assignment);
return -EINVAL; return -EINVAL;

View file

@ -30,6 +30,7 @@
#include "env-util.h" #include "env-util.h"
#include "path-util.h" #include "path-util.h"
#include "bus-error.h" #include "bus-error.h"
#include "calendarspec.h"
static bool arg_scope = false; static bool arg_scope = false;
static bool arg_remain_after_exit = false; static bool arg_remain_after_exit = false;
@ -47,30 +48,51 @@ static int arg_nice = 0;
static bool arg_nice_set = false; static bool arg_nice_set = false;
static char **arg_environment = NULL; static char **arg_environment = NULL;
static char **arg_property = NULL; static char **arg_property = NULL;
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 char *arg_on_calendar = NULL;
static char **arg_timer_property = NULL;
static void help(void) { static void help(void) {
printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n" printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
"Run the specified command in a transient scope or service unit.\n\n" "Run the specified command in a transient scope or service or timer\n"
" -h --help Show this help\n" "unit. If timer option is specified and unit is exist which is\n"
" --version Show package version\n" "specified with --unit option then command can be ommited.\n\n"
" --user Run as user unit\n" " -h --help Show this help\n"
" -H --host=[USER@]HOST Operate on remote host\n" " --version Show package version\n"
" -M --machine=CONTAINER Operate on local container\n" " --user Run as user unit\n"
" --scope Run this as scope rather than service\n" " -H --host=[USER@]HOST Operate on remote host\n"
" --unit=UNIT Run under the specified unit name\n" " -M --machine=CONTAINER Operate on local container\n"
" -p --property=NAME=VALUE Set unit property\n" " --scope Run this as scope rather than service\n"
" --description=TEXT Description for unit\n" " --unit=UNIT Run under the specified unit name\n"
" --slice=SLICE Run in the specified slice\n" " -p --property=NAME=VALUE Set unit property\n"
" -r --remain-after-exit Leave service around until explicitly stopped\n" " --description=TEXT Description for unit\n"
" --send-sighup Send SIGHUP when terminating\n" " --slice=SLICE Run in the specified slice\n"
" --service-type=TYPE Service type\n" " -r --remain-after-exit Leave service around until explicitly stopped\n"
" --uid=USER Run as system user\n" " --send-sighup Send SIGHUP when terminating\n"
" --gid=GROUP Run as system group\n" " --service-type=TYPE Service type\n"
" --nice=NICE Nice level\n" " --uid=USER Run as system user\n"
" --setenv=NAME=VALUE Set environment\n", " --gid=GROUP Run as system group\n"
" --nice=NICE Nice level\n"
" --setenv=NAME=VALUE Set environment\n\n"
"Timer options:\n\n"
" --on-active=SEC Run after seconds\n"
" --on-boot=SEC Run after seconds from machine was booted up\n"
" --on-startup=SEC Run after seconds from systemd was first started\n"
" --on-unit-active=SEC Run after seconds from the last activation\n"
" --on-unit-inactive=SEC Run after seconds from the last deactivation\n"
" --on-calendar=SPEC Realtime timer\n"
" --timer-property=NAME=VALUE Set timer unit property\n",
program_invocation_short_name); program_invocation_short_name);
} }
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;
}
static int parse_argv(int argc, char *argv[]) { static int parse_argv(int argc, char *argv[]) {
enum { enum {
@ -86,32 +108,47 @@ static int parse_argv(int argc, char *argv[]) {
ARG_EXEC_GROUP, ARG_EXEC_GROUP,
ARG_SERVICE_TYPE, ARG_SERVICE_TYPE,
ARG_NICE, ARG_NICE,
ARG_SETENV ARG_SETENV,
ARG_ON_ACTIVE,
ARG_ON_BOOT,
ARG_ON_STARTUP,
ARG_ON_UNIT_ACTIVE,
ARG_ON_UNIT_INACTIVE,
ARG_ON_CALENDAR,
ARG_TIMER_PROPERTY
}; };
static const struct option options[] = { static const struct option options[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION }, { "version", no_argument, NULL, ARG_VERSION },
{ "user", no_argument, NULL, ARG_USER }, { "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM }, { "system", no_argument, NULL, ARG_SYSTEM },
{ "scope", no_argument, NULL, ARG_SCOPE }, { "scope", no_argument, NULL, ARG_SCOPE },
{ "unit", required_argument, NULL, ARG_UNIT }, { "unit", required_argument, NULL, ARG_UNIT },
{ "description", required_argument, NULL, ARG_DESCRIPTION }, { "description", required_argument, NULL, ARG_DESCRIPTION },
{ "slice", required_argument, NULL, ARG_SLICE }, { "slice", required_argument, NULL, ARG_SLICE },
{ "remain-after-exit", no_argument, NULL, 'r' }, { "remain-after-exit", no_argument, NULL, 'r' },
{ "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
{ "host", required_argument, NULL, 'H' }, { "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' }, { "machine", required_argument, NULL, 'M' },
{ "service-type", required_argument, NULL, ARG_SERVICE_TYPE }, { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
{ "uid", required_argument, NULL, ARG_EXEC_USER }, { "uid", required_argument, NULL, ARG_EXEC_USER },
{ "gid", required_argument, NULL, ARG_EXEC_GROUP }, { "gid", required_argument, NULL, ARG_EXEC_GROUP },
{ "nice", required_argument, NULL, ARG_NICE }, { "nice", required_argument, NULL, ARG_NICE },
{ "setenv", required_argument, NULL, ARG_SETENV }, { "setenv", required_argument, NULL, ARG_SETENV },
{ "property", required_argument, NULL, 'p' }, { "property", required_argument, NULL, 'p' },
{ "on-active", required_argument, NULL, ARG_ON_ACTIVE },
{ "on-boot", required_argument, NULL, ARG_ON_BOOT },
{ "on-startup", required_argument, NULL, ARG_ON_STARTUP },
{ "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
{ "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
{ "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
{ "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
{}, {},
}; };
int r, c; int r, c;
CalendarSpec *spec = NULL;
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
@ -207,6 +244,74 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_ON_ACTIVE:
r = parse_sec(optarg, &arg_on_active);
if (r < 0) {
log_error("Failed to parse timer value: %s", optarg);
return r;
}
break;
case ARG_ON_BOOT:
r = parse_sec(optarg, &arg_on_boot);
if (r < 0) {
log_error("Failed to parse timer value: %s", optarg);
return r;
}
break;
case ARG_ON_STARTUP:
r = parse_sec(optarg, &arg_on_startup);
if (r < 0) {
log_error("Failed to parse timer value: %s", optarg);
return r;
}
break;
case ARG_ON_UNIT_ACTIVE:
r = parse_sec(optarg, &arg_on_unit_active);
if (r < 0) {
log_error("Failed to parse timer value: %s", optarg);
return r;
}
break;
case ARG_ON_UNIT_INACTIVE:
r = parse_sec(optarg, &arg_on_unit_inactive);
if (r < 0) {
log_error("Failed to parse timer value: %s", optarg);
return r;
}
break;
case ARG_ON_CALENDAR:
r = calendar_spec_from_string(optarg, &spec);
if (r < 0) {
log_error("Invalid calendar spec: %s", optarg);
return r;
}
free(spec);
arg_on_calendar = optarg;
break;
case ARG_TIMER_PROPERTY:
if (strv_extend(&arg_timer_property, optarg) < 0)
return log_oom();
break;
case '?': case '?':
return -EINVAL; return -EINVAL;
@ -214,7 +319,7 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option"); assert_not_reached("Unhandled option");
} }
if (optind >= argc) { if ((optind >= argc) && (!arg_unit || !with_timer())) {
log_error("Command line to execute required."); log_error("Command line to execute required.");
return -EINVAL; return -EINVAL;
} }
@ -234,44 +339,34 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL; 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; return 1;
} }
static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) { static int transient_unit_set_properties(sd_bus_message *m, UnitType t) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
char **i; char **i;
int r; int r;
assert(bus); STRV_FOREACH(i, t == UNIT_TIMER ? arg_timer_property : arg_property) {
assert(name);
assert(ret);
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0)
return r;
r = sd_bus_message_append(m, "ss", name, "fail");
if (r < 0)
return r;
r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0)
return r;
STRV_FOREACH(i, arg_property) {
r = sd_bus_message_open_container(m, 'r', "sv"); r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0) if (r < 0)
return r; return r;
r = bus_append_unit_property_assignment(m, *i); r = bus_append_unit_property_assignment(m, *i);
if (r < 0) if (r < 0) {
return r; r = sd_bus_message_append(m, "sv", 0);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(m); r = sd_bus_message_close_container(m);
if (r < 0) if (r < 0)
@ -294,33 +389,196 @@ static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bu
return r; return r;
} }
if (arg_send_sighup) { if (arg_send_sighup && t != UNIT_TIMER) {
r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup); r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
if (r < 0) if (r < 0)
return r; return r;
} }
*ret = m; return 0;
m = NULL; }
static int transient_service_set_properties(sd_bus_message *m, char **argv) {
int r;
assert(m);
r = transient_unit_set_properties(m, UNIT_SERVICE);
if (r < 0)
return r;
if (arg_remain_after_exit) {
r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
if (r < 0)
return r;
}
if (arg_service_type) {
r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
if (r < 0)
return r;
}
if (arg_exec_user) {
r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
if (r < 0)
return r;
}
if (arg_exec_group) {
r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
if (r < 0)
return r;
}
if (arg_nice_set) {
r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
if (r < 0)
return r;
}
if (!strv_isempty(arg_environment)) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return r;
r = sd_bus_message_append(m, "s", "Environment");
if (r < 0)
return r;
r = sd_bus_message_open_container(m, 'v', "as");
if (r < 0)
return r;
r = sd_bus_message_append_strv(m, arg_environment);
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 */
{
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return r;
r = sd_bus_message_append(m, "s", "ExecStart");
if (r < 0)
return r;
r = sd_bus_message_open_container(m, 'v', "a(sasb)");
if (r < 0)
return r;
r = sd_bus_message_open_container(m, 'a', "(sasb)");
if (r < 0)
return r;
r = sd_bus_message_open_container(m, 'r', "sasb");
if (r < 0)
return r;
r = sd_bus_message_append(m, "s", argv[0]);
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; return 0;
} }
static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) { static int transient_timer_set_properties(sd_bus_message *m) {
int r; int r;
assert(bus);
assert(m); assert(m);
r = sd_bus_message_close_container(m); r = transient_unit_set_properties(m, UNIT_TIMER);
if (r < 0) if (r < 0)
return r; return r;
r = sd_bus_message_append(m, "a(sa(sv))", 0); if (arg_on_active) {
r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
if (r < 0)
return r;
}
if (arg_on_boot) {
r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
if (r < 0)
return r;
}
if (arg_on_startup) {
r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
if (r < 0)
return r;
}
if (arg_on_unit_active) {
r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
if (r < 0)
return r;
}
if (arg_on_unit_inactive) {
r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
if (r < 0)
return r;
}
if (arg_on_calendar) {
r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
if (r < 0)
return r;
}
return 0;
}
static int transient_scope_set_properties(sd_bus_message *m) {
int r;
assert(m);
r = transient_unit_set_properties(m, UNIT_SCOPE);
if (r < 0) if (r < 0)
return r; return r;
return sd_bus_call(bus, m, 0, error, reply); r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
if (r < 0)
return r;
return 0;
} }
static int start_transient_service( static int start_transient_service(
@ -329,64 +587,151 @@ static int start_transient_service(
sd_bus_error *error) { sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_free_ char *name = NULL; _cleanup_free_ char *service = NULL;
int r; int r;
assert(bus);
assert(argv);
if (arg_unit) { if (arg_unit) {
name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
if (!name) if (!service)
return log_oom(); return log_oom();
} else if (asprintf(&name, "run-"PID_FMT".service", getpid()) < 0) } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
return log_oom(); return log_oom();
r = message_start_transient_unit_new(bus, name, &m); r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
if (arg_remain_after_exit) { /* name and mode */
r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit); r = sd_bus_message_append(m, "ss", service, "fail");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
}
if (arg_service_type) { /* properties */
r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type); r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
}
if (arg_exec_user) { r = transient_service_set_properties(m, argv);
r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user); if (r < 0)
if (r < 0) return bus_log_create_error(r);
return bus_log_create_error(r);
}
if (arg_exec_group) { r = sd_bus_message_close_container(m);
r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group); if (r < 0)
if (r < 0) return bus_log_create_error(r);
return bus_log_create_error(r);
}
if (arg_nice_set) { /* aux */
r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice); r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
}
if (!strv_isempty(arg_environment)) { /* send dbus */
r = sd_bus_message_open_container(m, 'r', "sv"); r = sd_bus_call(bus, m, 0, error, NULL);
if (r < 0)
return bus_log_create_error(r);
log_info("Running as unit %s.", service);
return 0;
}
static int start_transient_timer(
sd_bus *bus,
char **argv,
sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_free_ char *timer = NULL, *service = NULL;
int r;
assert(bus);
assert(argv);
if (arg_unit) {
switch(unit_name_to_type(arg_unit)) {
case UNIT_SERVICE:
service = strdup(arg_unit);
timer = unit_name_change_suffix(service, ".timer");
if (!timer)
return log_oom();
break;
case UNIT_TIMER:
timer = strdup(arg_unit);
service = unit_name_change_suffix(timer, ".service");
if (!service)
return log_oom();
break;
default:
service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
if (!service)
return log_oom();
timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
if (!timer)
return log_oom();
break;
}
} else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
(asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
return log_oom();
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0)
return bus_log_create_error(r);
/* name and mode */
r = sd_bus_message_append(m, "ss", timer, "fail");
if (r < 0)
return bus_log_create_error(r);
/* properties */
r = sd_bus_message_open_container(m, 'a', "(sv)");
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 (argv[0]) {
r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", "Environment"); r = sd_bus_message_open_container(m, 'r', "sa(sv)");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "as"); r = sd_bus_message_append(m, "s", service);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, arg_environment); r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0)
return bus_log_create_error(r);
r = transient_service_set_properties(m, argv);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
@ -397,61 +742,24 @@ static int start_transient_service(
r = sd_bus_message_close_container(m); r = sd_bus_message_close_container(m);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
} else {
r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0)
return bus_log_create_error(r);
} }
r = sd_bus_message_open_container(m, 'r', "sv"); /* send dbus */
r = sd_bus_call(bus, m, 0, error, NULL);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", "ExecStart"); log_info("Running as unit %s.", timer);
if (r < 0) if (argv[0])
return bus_log_create_error(r); log_info("Will run as unit %s.", service);
r = sd_bus_message_open_container(m, 'v', "a(sasb)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(sasb)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'r', "sasb");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", argv[0]);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, argv);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "b", false);
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);
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
r = message_start_transient_unit_send(bus, m, error, NULL);
if (r < 0)
return bus_log_create_error(r);
log_info("Running as unit %s.", name);
return 0; return 0;
} }
@ -462,28 +770,55 @@ static int start_transient_scope(
sd_bus_error *error) { sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_free_ char *name = NULL; _cleanup_free_ char *scope = NULL;
_cleanup_strv_free_ char **env = NULL, **user_env = NULL; _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
int r; int r;
assert(bus); assert(bus);
assert(argv);
if (arg_unit) { if (arg_unit) {
name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope"); scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
if (!name) if (!scope)
return log_oom(); return log_oom();
} else if (asprintf(&name, "run-"PID_FMT".scope", getpid()) < 0) } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
return log_oom(); return log_oom();
r = message_start_transient_unit_new(bus, name, &m); r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); /* name and mode */
r = sd_bus_message_append(m, "ss", scope, "fail");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = message_start_transient_unit_send(bus, m, error, NULL); /* properties */
r = sd_bus_message_open_container(m, 'a', "(sv)");
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);
/* aux */
r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0)
return bus_log_create_error(r);
/* send dbus */
r = sd_bus_call(bus, m, 0, error, NULL);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
@ -541,7 +876,7 @@ static int start_transient_scope(
if (!env) if (!env)
return log_oom(); return log_oom();
log_info("Running as unit %s.", name); log_info("Running as unit %s.", scope);
execvpe(argv[0], argv, env); execvpe(argv[0], argv, env);
log_error_errno(errno, "Failed to execute: %m"); log_error_errno(errno, "Failed to execute: %m");
@ -561,14 +896,16 @@ int main(int argc, char* argv[]) {
if (r <= 0) if (r <= 0)
goto finish; goto finish;
r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command); if (argc > optind) {
if (r < 0) { r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
log_error_errno(r, "Failed to find executable %s%s: %m", if (r < 0) {
argv[optind], log_error_errno(r, "Failed to find executable %s%s: %m",
arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system"); argv[optind],
goto finish; arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
goto finish;
}
argv[optind] = command;
} }
argv[optind] = command;
if (!arg_description) { if (!arg_description) {
description = strv_join(argv + optind, " "); description = strv_join(argv + optind, " ");
@ -577,6 +914,16 @@ int main(int argc, char* argv[]) {
goto finish; goto finish;
} }
if (arg_unit && isempty(description)) {
free(description);
description = strdup(arg_unit);
if (!description) {
r = log_oom();
goto finish;
}
}
arg_description = description; arg_description = description;
} }
@ -588,12 +935,15 @@ int main(int argc, char* argv[]) {
if (arg_scope) if (arg_scope)
r = start_transient_scope(bus, argv + optind, &error); r = start_transient_scope(bus, argv + optind, &error);
else if (with_timer())
r = start_transient_timer(bus, argv + optind, &error);
else else
r = start_transient_service(bus, argv + optind, &error); r = start_transient_service(bus, argv + optind, &error);
finish: finish:
strv_free(arg_environment); strv_free(arg_environment);
strv_free(arg_property); strv_free(arg_property);
strv_free(arg_timer_property);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
} }