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