chiark / gitweb /
Use enums to make it obvious what boolean params mean
[elogind.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 }