]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/run/run.c
clients: unify how we invoke getopt_long()
[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 {},
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 return help();
102
103 case ARG_VERSION:
104 puts(PACKAGE_STRING);
105 puts(SYSTEMD_FEATURES);
106 return 0;
107
108 case ARG_USER:
109 arg_user = true;
110 break;
111
112 case ARG_SYSTEM:
113 arg_user = false;
114 break;
115
116 case ARG_SCOPE:
117 arg_scope = true;
118 break;
119
120 case ARG_UNIT:
121 arg_unit = optarg;
122 break;
123
124 case ARG_DESCRIPTION:
125 arg_description = optarg;
126 break;
127
128 case ARG_SLICE:
129 arg_slice = optarg;
130 break;
131
132 case ARG_SEND_SIGHUP:
133 arg_send_sighup = true;
134 break;
135
136 case 'r':
137 arg_remain_after_exit = true;
138 break;
139
140 case 'H':
141 arg_transport = BUS_TRANSPORT_REMOTE;
142 arg_host = optarg;
143 break;
144
145 case 'M':
146 arg_transport = BUS_TRANSPORT_CONTAINER;
147 arg_host = optarg;
148 break;
149
150 case '?':
151 return -EINVAL;
152
153 default:
154 assert_not_reached("Unhandled option");
155 }
156 }
157
158 if (optind >= argc) {
159 log_error("Command line to execute required.");
160 return -EINVAL;
161 }
162
163 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
164 log_error("Execution in user context is not supported on non-local systems.");
165 return -EINVAL;
166 }
167
168 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
169 log_error("Scope execution is not supported on non-local systems.");
170 return -EINVAL;
171 }
172
173 return 1;
174 }
175
176 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
177 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
178 int r;
179
180 log_info("Running as unit %s.", name);
181
182 r = sd_bus_message_new_method_call(
183 bus,
184 "org.freedesktop.systemd1",
185 "/org/freedesktop/systemd1",
186 "org.freedesktop.systemd1.Manager",
187 "StartTransientUnit", &m);
188 if (r < 0)
189 return r;
190
191 r = sd_bus_message_append(m, "ss", name, "fail");
192 if (r < 0)
193 return r;
194
195 r = sd_bus_message_open_container(m, 'a', "(sv)");
196 if (r < 0)
197 return r;
198
199 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
200 if (r < 0)
201 return r;
202
203 if (!isempty(arg_slice)) {
204 _cleanup_free_ char *slice;
205
206 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
207 if (!slice)
208 return -ENOMEM;
209
210 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
211 if (r < 0)
212 return r;
213 }
214
215 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
216 if (r < 0)
217 return r;
218
219 *ret = m;
220 m = NULL;
221
222 return 0;
223 }
224
225 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
226 int r;
227
228 r = sd_bus_message_close_container(m);
229 if (r < 0)
230 return r;
231
232 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
233 }
234
235 static int start_transient_service(
236 sd_bus *bus,
237 char **argv,
238 sd_bus_error *error) {
239
240 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
241 _cleanup_free_ char *name = NULL;
242 char **i;
243 int r;
244
245 if (arg_unit)
246 name = unit_name_mangle_with_suffix(arg_unit, ".service");
247 else
248 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
249 if (!name)
250 return -ENOMEM;
251
252 r = message_start_transient_unit_new(bus, name, &m);
253 if (r < 0)
254 return r;
255
256 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
257 if (r < 0)
258 return r;
259
260 r = sd_bus_message_open_container(m, 'r', "sv");
261 if (r < 0)
262 return r;
263
264 r = sd_bus_message_append(m, "s", "ExecStart");
265 if (r < 0)
266 return r;
267
268 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
269 if (r < 0)
270 return r;
271
272 r = sd_bus_message_open_container(m, 'a', "(sasb)");
273 if (r < 0)
274 return r;
275
276 r = sd_bus_message_open_container(m, 'r', "sasb");
277 if (r < 0)
278 return r;
279
280 r = sd_bus_message_append(m, "s", argv[0]);
281 if (r < 0)
282 return r;
283
284 r = sd_bus_message_open_container(m, 'a', "s");
285 if (r < 0)
286 return r;
287
288 STRV_FOREACH(i, argv) {
289 r = sd_bus_message_append(m, "s", *i);
290 if (r < 0)
291 return r;
292 }
293
294 r = sd_bus_message_close_container(m);
295 if (r < 0)
296 return r;
297
298 r = sd_bus_message_append(m, "b", false);
299 if (r < 0)
300 return r;
301
302 r = sd_bus_message_close_container(m);
303 if (r < 0)
304 return r;
305
306 r = sd_bus_message_close_container(m);
307 if (r < 0)
308 return r;
309
310 r = sd_bus_message_close_container(m);
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 return message_start_transient_unit_send(bus, m, error, &reply);
319 }
320
321 static int start_transient_scope(
322 sd_bus *bus,
323 char **argv,
324 sd_bus_error *error) {
325
326 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
327 _cleanup_free_ char *name = NULL;
328 int r;
329
330 if (arg_unit)
331 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
332 else
333 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
334 if (!name)
335 return -ENOMEM;
336
337 r = message_start_transient_unit_new(bus, name, &m);
338 if (r < 0)
339 return r;
340
341 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
342 if (r < 0)
343 return r;
344
345 r = message_start_transient_unit_send(bus, m, error, &reply);
346 if (r < 0)
347 return r;
348
349 execvp(argv[0], argv);
350 log_error("Failed to execute: %m");
351 return -errno;
352 }
353
354 int main(int argc, char* argv[]) {
355 sd_bus_error error = SD_BUS_ERROR_NULL;
356 _cleanup_bus_unref_ sd_bus *bus = NULL;
357 _cleanup_free_ char *description = NULL, *command = NULL;
358 int r;
359
360 log_parse_environment();
361 log_open();
362
363 r = parse_argv(argc, argv);
364 if (r <= 0)
365 goto finish;
366
367 r = find_binary(argv[optind], &command);
368 if (r < 0) {
369 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
370 goto finish;
371 }
372 argv[optind] = command;
373
374 if (!arg_description) {
375 description = strv_join(argv + optind, " ");
376 if (!description) {
377 r = log_oom();
378 goto finish;
379 }
380
381 arg_description = description;
382 }
383
384 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
385 if (r < 0) {
386 log_error("Failed to create bus connection: %s", strerror(-r));
387 goto finish;
388 }
389
390 if (arg_scope)
391 r = start_transient_scope(bus, argv + optind, &error);
392 else
393 r = start_transient_service(bus, argv + optind, &error);
394 if (r < 0) {
395 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
396 sd_bus_error_free(&error);
397 goto finish;
398 }
399
400 finish:
401 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
402 }