]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/run/run.c
run: support --system to match other commands, even if redundant
[thirdparty/systemd.git] / src / run / run.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <stdio.h>
23 #include <getopt.h>
24
25 #include "sd-bus.h"
26 #include "bus-util.h"
27 #include "strv.h"
28 #include "build.h"
29 #include "unit-name.h"
30 #include "path-util.h"
31
32 static bool arg_scope = false;
33 static bool arg_remain_after_exit = false;
34 static const char *arg_unit = NULL;
35 static const char *arg_description = NULL;
36 static const char *arg_slice = NULL;
37 static bool arg_send_sighup = false;
38 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
39 static char *arg_host = NULL;
40 static bool arg_user = false;
41
42 static int help(void) {
43
44 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
45 "Run the specified command in a transient scope or service unit.\n\n"
46 " -h --help Show this help\n"
47 " --version Show package version\n"
48 " --user Run as user unit\n"
49 " -H --host=[USER@]HOST Operate on remote host\n"
50 " -M --machine=CONTAINER Operate on local container\n"
51 " --scope Run this as scope rather than service\n"
52 " --unit=UNIT Run under the specified unit name\n"
53 " --description=TEXT Description for unit\n"
54 " --slice=SLICE Run in the specified slice\n"
55 " -r --remain-after-exit Leave service around until explicitly stopped\n"
56 " --send-sighup Send SIGHUP when terminating\n",
57 program_invocation_short_name);
58
59 return 0;
60 }
61
62 static int parse_argv(int argc, char *argv[]) {
63
64 enum {
65 ARG_VERSION = 0x100,
66 ARG_USER,
67 ARG_SYSTEM,
68 ARG_SCOPE,
69 ARG_UNIT,
70 ARG_DESCRIPTION,
71 ARG_SLICE,
72 ARG_SEND_SIGHUP,
73 };
74
75 static const struct option options[] = {
76 { "help", no_argument, NULL, 'h' },
77 { "version", no_argument, NULL, ARG_VERSION },
78 { "user", no_argument, NULL, ARG_USER },
79 { "system", no_argument, NULL, ARG_SYSTEM },
80 { "scope", no_argument, NULL, ARG_SCOPE },
81 { "unit", required_argument, NULL, ARG_UNIT },
82 { "description", required_argument, NULL, ARG_DESCRIPTION },
83 { "slice", required_argument, NULL, ARG_SLICE },
84 { "remain-after-exit", no_argument, NULL, 'r' },
85 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
86 { "host", required_argument, NULL, 'H' },
87 { "machine", required_argument, NULL, 'M' },
88 { NULL, 0, NULL, 0 },
89 };
90
91 int c;
92
93 assert(argc >= 0);
94 assert(argv);
95
96 while ((c = getopt_long(argc, argv, "+hrH:M:", options, NULL)) >= 0) {
97
98 switch (c) {
99
100 case 'h':
101 help();
102 return 0;
103
104 case ARG_VERSION:
105 puts(PACKAGE_STRING);
106 puts(SYSTEMD_FEATURES);
107 return 0;
108
109 case ARG_USER:
110 arg_user = true;
111 break;
112
113 case ARG_SYSTEM:
114 arg_user = false;
115 break;
116
117 case ARG_SCOPE:
118 arg_scope = true;
119 break;
120
121 case ARG_UNIT:
122 arg_unit = optarg;
123 break;
124
125 case ARG_DESCRIPTION:
126 arg_description = optarg;
127 break;
128
129 case ARG_SLICE:
130 arg_slice = optarg;
131 break;
132
133 case ARG_SEND_SIGHUP:
134 arg_send_sighup = true;
135 break;
136
137 case 'r':
138 arg_remain_after_exit = true;
139 break;
140
141 case 'H':
142 arg_transport = BUS_TRANSPORT_REMOTE;
143 arg_host = optarg;
144 break;
145
146 case 'M':
147 arg_transport = BUS_TRANSPORT_CONTAINER;
148 arg_host = optarg;
149 break;
150
151 case '?':
152 return -EINVAL;
153
154 default:
155 log_error("Unknown option code %c", c);
156 return -EINVAL;
157 }
158 }
159
160 if (optind >= argc) {
161 log_error("Command line to execute required.");
162 return -EINVAL;
163 }
164
165 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
166 log_error("Execution in user context is not supported on non-local systems.");
167 return -EINVAL;
168 }
169
170 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
171 log_error("Scope execution is not supported on non-local systems.");
172 return -EINVAL;
173 }
174
175 return 1;
176 }
177
178 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
179 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
180 int r;
181
182 log_info("Running as unit %s.", name);
183
184 r = sd_bus_message_new_method_call(
185 bus,
186 "org.freedesktop.systemd1",
187 "/org/freedesktop/systemd1",
188 "org.freedesktop.systemd1.Manager",
189 "StartTransientUnit", &m);
190 if (r < 0)
191 return r;
192
193 r = sd_bus_message_append(m, "ss", name, "fail");
194 if (r < 0)
195 return r;
196
197 r = sd_bus_message_open_container(m, 'a', "(sv)");
198 if (r < 0)
199 return r;
200
201 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
202 if (r < 0)
203 return r;
204
205 if (!isempty(arg_slice)) {
206 _cleanup_free_ char *slice;
207
208 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
209 if (!slice)
210 return -ENOMEM;
211
212 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
213 if (r < 0)
214 return r;
215 }
216
217 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
218 if (r < 0)
219 return r;
220
221 *ret = m;
222 m = NULL;
223
224 return 0;
225 }
226
227 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
228 int r;
229
230 r = sd_bus_message_close_container(m);
231 if (r < 0)
232 return r;
233
234 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
235 }
236
237 static int start_transient_service(
238 sd_bus *bus,
239 char **argv,
240 sd_bus_error *error) {
241
242 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
243 _cleanup_free_ char *name = NULL;
244 char **i;
245 int r;
246
247 if (arg_unit)
248 name = unit_name_mangle_with_suffix(arg_unit, ".service");
249 else
250 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
251 if (!name)
252 return -ENOMEM;
253
254 r = message_start_transient_unit_new(bus, name, &m);
255 if (r < 0)
256 return r;
257
258 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
259 if (r < 0)
260 return r;
261
262 r = sd_bus_message_open_container(m, 'r', "sv");
263 if (r < 0)
264 return r;
265
266 r = sd_bus_message_append(m, "s", "ExecStart");
267 if (r < 0)
268 return r;
269
270 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
271 if (r < 0)
272 return r;
273
274 r = sd_bus_message_open_container(m, 'a', "(sasb)");
275 if (r < 0)
276 return r;
277
278 r = sd_bus_message_open_container(m, 'r', "sasb");
279 if (r < 0)
280 return r;
281
282 r = sd_bus_message_append(m, "s", argv[0]);
283 if (r < 0)
284 return r;
285
286 r = sd_bus_message_open_container(m, 'a', "s");
287 if (r < 0)
288 return r;
289
290 STRV_FOREACH(i, argv) {
291 r = sd_bus_message_append(m, "s", *i);
292 if (r < 0)
293 return r;
294 }
295
296 r = sd_bus_message_close_container(m);
297 if (r < 0)
298 return r;
299
300 r = sd_bus_message_append(m, "b", false);
301 if (r < 0)
302 return r;
303
304 r = sd_bus_message_close_container(m);
305 if (r < 0)
306 return r;
307
308 r = sd_bus_message_close_container(m);
309 if (r < 0)
310 return r;
311
312 r = sd_bus_message_close_container(m);
313 if (r < 0)
314 return r;
315
316 r = sd_bus_message_close_container(m);
317 if (r < 0)
318 return r;
319
320 return message_start_transient_unit_send(bus, m, error, &reply);
321 }
322
323 static int start_transient_scope(
324 sd_bus *bus,
325 char **argv,
326 sd_bus_error *error) {
327
328 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
329 _cleanup_free_ char *name = NULL;
330 int r;
331
332 if (arg_unit)
333 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
334 else
335 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
336 if (!name)
337 return -ENOMEM;
338
339 r = message_start_transient_unit_new(bus, name, &m);
340 if (r < 0)
341 return r;
342
343 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
344 if (r < 0)
345 return r;
346
347 r = message_start_transient_unit_send(bus, m, error, &reply);
348 if (r < 0)
349 return r;
350
351 execvp(argv[0], argv);
352 log_error("Failed to execute: %m");
353 return -errno;
354 }
355
356 int main(int argc, char* argv[]) {
357 sd_bus_error error = SD_BUS_ERROR_NULL;
358 _cleanup_bus_unref_ sd_bus *bus = NULL;
359 _cleanup_free_ char *description = NULL, *command = NULL;
360 int r;
361
362 log_parse_environment();
363 log_open();
364
365 r = parse_argv(argc, argv);
366 if (r <= 0)
367 goto finish;
368
369 r = find_binary(argv[optind], &command);
370 if (r < 0) {
371 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
372 goto finish;
373 }
374 argv[optind] = command;
375
376 if (!arg_description) {
377 description = strv_join(argv + optind, " ");
378 if (!description) {
379 r = log_oom();
380 goto finish;
381 }
382
383 arg_description = description;
384 }
385
386 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
387 if (r < 0) {
388 log_error("Failed to create bus connection: %s", strerror(-r));
389 goto finish;
390 }
391
392 if (arg_scope)
393 r = start_transient_scope(bus, argv + optind, &error);
394 else
395 r = start_transient_service(bus, argv + optind, &error);
396 if (r < 0) {
397 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
398 sd_bus_error_free(&error);
399 goto finish;
400 }
401
402 finish:
403 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
404 }