chiark / gitweb /
f4c70050894c9fd4cbeff60a554b9bcca6eabfe2
[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         log_info("Running as unit %s.", name);
182
183         r = sd_bus_message_new_method_call(
184                         bus,
185                         "org.freedesktop.systemd1",
186                         "/org/freedesktop/systemd1",
187                         "org.freedesktop.systemd1.Manager",
188                         "StartTransientUnit", &m);
189         if (r < 0)
190                 return r;
191
192         r = sd_bus_message_append(m, "ss", name, "fail");
193         if (r < 0)
194                 return r;
195
196         r = sd_bus_message_open_container(m, 'a', "(sv)");
197         if (r < 0)
198                 return r;
199
200         r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
201         if (r < 0)
202                 return r;
203
204         if (!isempty(arg_slice)) {
205                 _cleanup_free_ char *slice;
206
207                 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
208                 if (!slice)
209                         return -ENOMEM;
210
211                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
212                 if (r < 0)
213                         return r;
214         }
215
216         r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
217         if (r < 0)
218                 return r;
219
220         *ret = m;
221         m = NULL;
222
223         return 0;
224 }
225
226 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
227         int r;
228
229         r = sd_bus_message_close_container(m);
230         if (r < 0)
231                 return r;
232
233         return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
234 }
235
236 static int start_transient_service(
237                 sd_bus *bus,
238                 char **argv,
239                 sd_bus_error *error) {
240
241         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
242         _cleanup_free_ char *name = NULL;
243         char **i;
244         int r;
245
246         if (arg_unit)
247                 name = unit_name_mangle_with_suffix(arg_unit, ".service");
248         else
249                 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
250         if (!name)
251                 return -ENOMEM;
252
253         r = message_start_transient_unit_new(bus, name, &m);
254         if (r < 0)
255                 return r;
256
257         r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
258         if (r < 0)
259                 return r;
260
261         r = sd_bus_message_open_container(m, 'r', "sv");
262         if (r < 0)
263                 return r;
264
265         r = sd_bus_message_append(m, "s", "ExecStart");
266         if (r < 0)
267                 return r;
268
269         r = sd_bus_message_open_container(m, 'v', "a(sasb)");
270         if (r < 0)
271                 return r;
272
273         r = sd_bus_message_open_container(m, 'a', "(sasb)");
274         if (r < 0)
275                 return r;
276
277         r = sd_bus_message_open_container(m, 'r', "sasb");
278         if (r < 0)
279                 return r;
280
281         r = sd_bus_message_append(m, "s", argv[0]);
282         if (r < 0)
283                 return r;
284
285         r = sd_bus_message_open_container(m, 'a', "s");
286         if (r < 0)
287                 return r;
288
289         STRV_FOREACH(i, argv) {
290                 r = sd_bus_message_append(m, "s", *i);
291                 if (r < 0)
292                         return r;
293         }
294
295         r = sd_bus_message_close_container(m);
296         if (r < 0)
297                 return r;
298
299         r = sd_bus_message_append(m, "b", false);
300         if (r < 0)
301                 return r;
302
303         r = sd_bus_message_close_container(m);
304         if (r < 0)
305                 return r;
306
307         r = sd_bus_message_close_container(m);
308         if (r < 0)
309                 return r;
310
311         r = sd_bus_message_close_container(m);
312         if (r < 0)
313                 return r;
314
315         r = sd_bus_message_close_container(m);
316         if (r < 0)
317                 return r;
318
319         return  message_start_transient_unit_send(bus, m, error, &reply);
320 }
321
322 static int start_transient_scope(
323                 sd_bus *bus,
324                 char **argv,
325                 sd_bus_error *error) {
326
327         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
328         _cleanup_free_ char *name = NULL;
329         int r;
330
331         if (arg_unit)
332                 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
333         else
334                 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
335         if (!name)
336                 return -ENOMEM;
337
338         r = message_start_transient_unit_new(bus, name, &m);
339         if (r < 0)
340                 return r;
341
342         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
343         if (r < 0)
344                 return r;
345
346         r = message_start_transient_unit_send(bus, m, error, &reply);
347         if (r < 0)
348                 return r;
349
350         execvp(argv[0], argv);
351         log_error("Failed to execute: %m");
352         return -errno;
353 }
354
355 int main(int argc, char* argv[]) {
356         sd_bus_error error = SD_BUS_ERROR_NULL;
357         _cleanup_bus_unref_ sd_bus *bus = NULL;
358         _cleanup_free_ char *description = NULL, *command = NULL;
359         int r;
360
361         log_parse_environment();
362         log_open();
363
364         r = parse_argv(argc, argv);
365         if (r <= 0)
366                 goto finish;
367
368         r = find_binary(argv[optind], &command);
369         if (r < 0) {
370                 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
371                 goto finish;
372         }
373         argv[optind] = command;
374
375         if (!arg_description) {
376                 description = strv_join(argv + optind, " ");
377                 if (!description) {
378                         r = log_oom();
379                         goto finish;
380                 }
381
382                 arg_description = description;
383         }
384
385         r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
386         if (r < 0) {
387                 log_error("Failed to create bus connection: %s", strerror(-r));
388                 goto finish;
389         }
390
391         if (arg_scope)
392                 r = start_transient_scope(bus, argv + optind, &error);
393         else
394                 r = start_transient_service(bus, argv + optind, &error);
395         if (r < 0) {
396                 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
397                 sd_bus_error_free(&error);
398                 goto finish;
399         }
400
401 finish:
402         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
403 }