chiark / gitweb /
3fb72e61035c79749009d832c45307bd3f44d8f7
[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                 {},
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 }