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