chiark / gitweb /
567fd97e3c2536d47572acb205ad5b9ce645a74b
[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
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 }