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