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