chiark / gitweb /
build-sys: add a makefile target to run all tests through valgrind
[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-internal.h"
27 #include "bus-message.h"
28 #include "strv.h"
29 #include "build.h"
30 #include "unit-name.h"
31 #include "path-util.h"
32
33 static bool arg_scope = false;
34 static bool arg_user = false;
35 static bool arg_remain_after_exit = false;
36 static const char *arg_unit = NULL;
37 static const char *arg_description = NULL;
38 static const char *arg_slice = NULL;
39 static bool arg_send_sighup = false;
40
41 static int help(void) {
42
43         printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
44                "Run the specified command in a transient scope or service unit.\n\n"
45                "  -h --help               Show this help\n"
46                "     --version            Show package version\n"
47                "     --user               Run as user unit\n"
48                "     --scope              Run this as scope rather than service\n"
49                "     --unit=UNIT          Run under the specified unit name\n"
50                "     --description=TEXT   Description for unit\n"
51                "     --slice=SLICE        Run in the specified slice\n"
52                "  -r --remain-after-exit  Leave service around until explicitly stopped\n"
53                "     --send-sighup        Send SIGHUP when terminating\n",
54                program_invocation_short_name);
55
56         return 0;
57 }
58
59 static int parse_argv(int argc, char *argv[]) {
60
61         enum {
62                 ARG_VERSION = 0x100,
63                 ARG_USER,
64                 ARG_SCOPE,
65                 ARG_UNIT,
66                 ARG_DESCRIPTION,
67                 ARG_SLICE,
68                 ARG_SEND_SIGHUP,
69         };
70
71         static const struct option options[] = {
72                 { "help",              no_argument,       NULL, 'h'             },
73                 { "version",           no_argument,       NULL, ARG_VERSION     },
74                 { "user",              no_argument,       NULL, ARG_USER        },
75                 { "scope",             no_argument,       NULL, ARG_SCOPE       },
76                 { "unit",              required_argument, NULL, ARG_UNIT        },
77                 { "description",       required_argument, NULL, ARG_DESCRIPTION },
78                 { "slice",             required_argument, NULL, ARG_SLICE       },
79                 { "remain-after-exit", no_argument,       NULL, 'r'             },
80                 { "send-sighup",       no_argument,       NULL, ARG_SEND_SIGHUP },
81                 { NULL,                0,                 NULL, 0               },
82         };
83
84         int c;
85
86         assert(argc >= 0);
87         assert(argv);
88
89         while ((c = getopt_long(argc, argv, "+hr", options, NULL)) >= 0) {
90
91                 switch (c) {
92
93                 case 'h':
94                         help();
95                         return 0;
96
97                 case ARG_VERSION:
98                         puts(PACKAGE_STRING);
99                         puts(SYSTEMD_FEATURES);
100                         return 0;
101
102                 case ARG_USER:
103                         arg_user = true;
104                         break;
105
106                 case ARG_SCOPE:
107                         arg_scope = true;
108                         break;
109
110                 case ARG_UNIT:
111                         arg_unit = optarg;
112                         break;
113
114                 case ARG_DESCRIPTION:
115                         arg_description = optarg;
116                         break;
117
118                 case ARG_SLICE:
119                         arg_slice = optarg;
120                         break;
121
122                 case ARG_SEND_SIGHUP:
123                         arg_send_sighup = true;
124                         break;
125
126                 case 'r':
127                         arg_remain_after_exit = true;
128                         break;
129
130                 case '?':
131                         return -EINVAL;
132
133                 default:
134                         log_error("Unknown option code %c", c);
135                         return -EINVAL;
136                 }
137         }
138
139         if (optind >= argc) {
140                 log_error("Command line to execute required.");
141                 return -EINVAL;
142         }
143
144         return 1;
145 }
146
147 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
148         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
149         int r;
150
151         log_info("Running as unit %s.", name);
152
153         r = sd_bus_message_new_method_call(
154                         bus,
155                         "org.freedesktop.systemd1",
156                         "/org/freedesktop/systemd1",
157                         "org.freedesktop.systemd1.Manager",
158                         "StartTransientUnit", &m);
159         if (r < 0)
160                 return r;
161
162         r = sd_bus_message_append(m, "ss", name, "fail");
163         if (r < 0)
164                 return r;
165
166         r = sd_bus_message_open_container(m, 'a', "(sv)");
167         if (r < 0)
168                 return r;
169
170         r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
171         if (r < 0)
172                 return r;
173
174         if (!isempty(arg_slice)) {
175                 _cleanup_free_ char *slice;
176
177                 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
178                 if (!slice)
179                         return -ENOMEM;
180
181                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
182                 if (r < 0)
183                         return r;
184         }
185
186         r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
187         if (r < 0)
188                 return r;
189
190         *ret = m;
191         m = NULL;
192
193         return 0;
194 }
195
196 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
197         int r;
198
199         r = sd_bus_message_close_container(m);
200         if (r < 0)
201                 return r;
202
203         return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
204 }
205
206 static int start_transient_service(
207                 sd_bus *bus,
208                 char **argv,
209                 sd_bus_error *error) {
210
211         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
212         _cleanup_free_ char *name = NULL;
213         char **i;
214         int r;
215
216         if (arg_unit)
217                 name = unit_name_mangle_with_suffix(arg_unit, ".service");
218         else
219                 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
220         if (!name)
221                 return -ENOMEM;
222
223         r = message_start_transient_unit_new(bus, name, &m);
224         if (r < 0)
225                 return r;
226
227         r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
228         if (r < 0)
229                 return r;
230
231         r = sd_bus_message_open_container(m, 'r', "sv");
232         if (r < 0)
233                 return r;
234
235         r = sd_bus_message_append(m, "s", "ExecStart");
236         if (r < 0)
237                 return r;
238
239         r = sd_bus_message_open_container(m, 'v', "a(sasb)");
240         if (r < 0)
241                 return r;
242
243         r = sd_bus_message_open_container(m, 'a', "(sasb)");
244         if (r < 0)
245                 return r;
246
247         r = sd_bus_message_open_container(m, 'r', "sasb");
248         if (r < 0)
249                 return r;
250
251         r = sd_bus_message_append(m, "s", argv[0]);
252         if (r < 0)
253                 return r;
254
255         r = sd_bus_message_open_container(m, 'a', "s");
256         if (r < 0)
257                 return r;
258
259         STRV_FOREACH(i, argv) {
260                 r = sd_bus_message_append(m, "s", *i);
261                 if (r < 0)
262                         return r;
263         }
264
265         r = sd_bus_message_close_container(m);
266         if (r < 0)
267                 return r;
268
269         r = sd_bus_message_append(m, "b", false);
270         if (r < 0)
271                 return r;
272
273         r = sd_bus_message_close_container(m);
274         if (r < 0)
275                 return r;
276
277         r = sd_bus_message_close_container(m);
278         if (r < 0)
279                 return r;
280
281         r = sd_bus_message_close_container(m);
282         if (r < 0)
283                 return r;
284
285         r = sd_bus_message_close_container(m);
286         if (r < 0)
287                 return r;
288
289         return  message_start_transient_unit_send(bus, m, error, &reply);
290 }
291
292 static int start_transient_scope(
293                 sd_bus *bus,
294                 char **argv,
295                 sd_bus_error *error) {
296
297         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
298         _cleanup_free_ char *name = NULL;
299         int r;
300
301         if (arg_unit)
302                 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
303         else
304                 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
305         if (!name)
306                 return -ENOMEM;
307
308         r = message_start_transient_unit_new(bus, name, &m);
309         if (r < 0)
310                 return r;
311
312         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
313         if (r < 0)
314                 return r;
315
316         r = message_start_transient_unit_send(bus, m, error, &reply);
317         if (r < 0)
318                 return r;
319
320         execvp(argv[0], argv);
321         log_error("Failed to execute: %m");
322         return -errno;
323 }
324
325 int main(int argc, char* argv[]) {
326         sd_bus_error error = SD_BUS_ERROR_NULL;
327         _cleanup_bus_unref_ sd_bus *bus = NULL;
328         _cleanup_free_ char *description = NULL, *command = NULL;
329         int r;
330
331         log_parse_environment();
332         log_open();
333
334         r = parse_argv(argc, argv);
335         if (r <= 0)
336                 goto fail;
337
338         r = find_binary(argv[optind], &command);
339         if (r < 0) {
340                 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
341                 goto fail;
342         }
343         argv[optind] = command;
344
345         if (!arg_description) {
346                 description = strv_join(argv + optind, " ");
347                 if (!description) {
348                         r = log_oom();
349                         goto fail;
350                 }
351
352                 arg_description = description;
353         }
354
355         if (arg_user)
356                 r = sd_bus_open_user(&bus);
357         else
358                 r = sd_bus_open_system(&bus);
359         if (r < 0) {
360                 log_error("Failed to create new bus connection: %s", strerror(-r));
361                 goto fail;
362         }
363
364         if (arg_scope)
365                 r = start_transient_scope(bus, argv + optind, &error);
366         else
367                 r = start_transient_service(bus, argv + optind, &error);
368         if (r < 0) {
369                 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
370                 sd_bus_error_free(&error);
371                 goto fail;
372         }
373
374 fail:
375         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
376 }