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