chiark / gitweb /
main: make gcc shut up
[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 #include "bus-error.h"
32
33 static bool arg_scope = 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 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
40 static char *arg_host = NULL;
41 static bool arg_user = false;
42 static const char *arg_service_type = NULL;
43 static const char *arg_exec_user = NULL;
44 static const char *arg_exec_group = NULL;
45 static int arg_nice = 0;
46 static bool arg_nice_set = false;
47 static char **arg_environment = NULL;
48
49 static int help(void) {
50
51         printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
52                "Run the specified command in a transient scope or service unit.\n\n"
53                "  -h --help               Show this help\n"
54                "     --version            Show package version\n"
55                "     --user               Run as user unit\n"
56                "  -H --host=[USER@]HOST   Operate on remote host\n"
57                "  -M --machine=CONTAINER  Operate on local container\n"
58                "     --scope              Run this as scope rather than service\n"
59                "     --unit=UNIT          Run under the specified unit name\n"
60                "     --description=TEXT   Description for unit\n"
61                "     --slice=SLICE        Run in the specified slice\n"
62                "  -r --remain-after-exit  Leave service around until explicitly stopped\n"
63                "     --send-sighup        Send SIGHUP when terminating\n"
64                "     --service-type=TYPE  Service type\n"
65                "     --uid=USER           Run as system user\n"
66                "     --gid=GROUP          Run as system group\n"
67                "     --nice=NICE          Nice level\n"
68                "     --setenv=NAME=VALUE  Set environment\n",
69                program_invocation_short_name);
70
71         return 0;
72 }
73
74 static int parse_argv(int argc, char *argv[]) {
75
76         enum {
77                 ARG_VERSION = 0x100,
78                 ARG_USER,
79                 ARG_SYSTEM,
80                 ARG_SCOPE,
81                 ARG_UNIT,
82                 ARG_DESCRIPTION,
83                 ARG_SLICE,
84                 ARG_SEND_SIGHUP,
85                 ARG_EXEC_USER,
86                 ARG_EXEC_GROUP,
87                 ARG_SERVICE_TYPE,
88                 ARG_NICE,
89                 ARG_SETENV
90         };
91
92         static const struct option options[] = {
93                 { "help",              no_argument,       NULL, 'h'              },
94                 { "version",           no_argument,       NULL, ARG_VERSION      },
95                 { "user",              no_argument,       NULL, ARG_USER         },
96                 { "system",            no_argument,       NULL, ARG_SYSTEM       },
97                 { "scope",             no_argument,       NULL, ARG_SCOPE        },
98                 { "unit",              required_argument, NULL, ARG_UNIT         },
99                 { "description",       required_argument, NULL, ARG_DESCRIPTION  },
100                 { "slice",             required_argument, NULL, ARG_SLICE        },
101                 { "remain-after-exit", no_argument,       NULL, 'r'              },
102                 { "send-sighup",       no_argument,       NULL, ARG_SEND_SIGHUP  },
103                 { "host",              required_argument, NULL, 'H'              },
104                 { "machine",           required_argument, NULL, 'M'              },
105                 { "service-type",      required_argument, NULL, ARG_SERVICE_TYPE },
106                 { "uid",               required_argument, NULL, ARG_EXEC_USER    },
107                 { "gid",               required_argument, NULL, ARG_EXEC_GROUP   },
108                 { "nice",              required_argument, NULL, ARG_NICE         },
109                 { "setenv",            required_argument, NULL, ARG_SETENV       },
110                 {},
111         };
112
113         int r, c;
114
115         assert(argc >= 0);
116         assert(argv);
117
118         while ((c = getopt_long(argc, argv, "+hrH:M:", options, NULL)) >= 0) {
119
120                 switch (c) {
121
122                 case 'h':
123                         return help();
124
125                 case ARG_VERSION:
126                         puts(PACKAGE_STRING);
127                         puts(SYSTEMD_FEATURES);
128                         return 0;
129
130                 case ARG_USER:
131                         arg_user = true;
132                         break;
133
134                 case ARG_SYSTEM:
135                         arg_user = false;
136                         break;
137
138                 case ARG_SCOPE:
139                         arg_scope = true;
140                         break;
141
142                 case ARG_UNIT:
143                         arg_unit = optarg;
144                         break;
145
146                 case ARG_DESCRIPTION:
147                         arg_description = optarg;
148                         break;
149
150                 case ARG_SLICE:
151                         arg_slice = optarg;
152                         break;
153
154                 case ARG_SEND_SIGHUP:
155                         arg_send_sighup = true;
156                         break;
157
158                 case 'r':
159                         arg_remain_after_exit = true;
160                         break;
161
162                 case 'H':
163                         arg_transport = BUS_TRANSPORT_REMOTE;
164                         arg_host = optarg;
165                         break;
166
167                 case 'M':
168                         arg_transport = BUS_TRANSPORT_CONTAINER;
169                         arg_host = optarg;
170                         break;
171
172                 case ARG_SERVICE_TYPE:
173                         arg_service_type = optarg;
174                         break;
175
176                 case ARG_EXEC_USER:
177                         arg_exec_user = optarg;
178                         break;
179
180                 case ARG_EXEC_GROUP:
181                         arg_exec_group = optarg;
182                         break;
183
184                 case ARG_NICE:
185                         r = safe_atoi(optarg, &arg_nice);
186                         if (r < 0) {
187                                 log_error("Failed to parse nice value");
188                                 return -EINVAL;
189                         }
190
191                         arg_nice_set = true;
192                         break;
193
194                 case ARG_SETENV:
195
196                         if (strv_extend(&arg_environment, optarg) < 0)
197                                 return log_oom();
198
199                         break;
200
201                 case '?':
202                         return -EINVAL;
203
204                 default:
205                         assert_not_reached("Unhandled option");
206                 }
207         }
208
209         if (optind >= argc) {
210                 log_error("Command line to execute required.");
211                 return -EINVAL;
212         }
213
214         if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
215                 log_error("Execution in user context is not supported on non-local systems.");
216                 return -EINVAL;
217         }
218
219         if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
220                 log_error("Scope execution is not supported on non-local systems.");
221                 return -EINVAL;
222         }
223
224         if (arg_scope && (arg_remain_after_exit || arg_service_type || arg_exec_user || arg_exec_group || arg_nice_set || arg_environment)) {
225                 log_error("--remain-after-exit, --service-type=, --user=, --group=, --nice= and --setenv= are not supported in --scope mode.");
226                 return -EINVAL;
227         }
228
229         return 1;
230 }
231
232 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
233         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
234         int r;
235
236         assert(bus);
237         assert(name);
238         assert(ret);
239
240         log_info("Running as unit %s.", name);
241
242         r = sd_bus_message_new_method_call(
243                         bus,
244                         "org.freedesktop.systemd1",
245                         "/org/freedesktop/systemd1",
246                         "org.freedesktop.systemd1.Manager",
247                         "StartTransientUnit", &m);
248         if (r < 0)
249                 return r;
250
251         r = sd_bus_message_append(m, "ss", name, "fail");
252         if (r < 0)
253                 return r;
254
255         r = sd_bus_message_open_container(m, 'a', "(sv)");
256         if (r < 0)
257                 return r;
258
259         r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
260         if (r < 0)
261                 return r;
262
263         if (!isempty(arg_slice)) {
264                 _cleanup_free_ char *slice;
265
266                 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
267                 if (!slice)
268                         return -ENOMEM;
269
270                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
271                 if (r < 0)
272                         return r;
273         }
274
275         r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
276         if (r < 0)
277                 return r;
278
279         *ret = m;
280         m = NULL;
281
282         return 0;
283 }
284
285 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
286         int r;
287
288         assert(bus);
289         assert(m);
290
291         r = sd_bus_message_close_container(m);
292         if (r < 0)
293                 return r;
294
295         r = sd_bus_message_append(m, "a(sa(sv))", 0);
296         if (r < 0)
297                 return r;
298
299         return sd_bus_call(bus, m, 0, error, reply);
300 }
301
302 static int start_transient_service(
303                 sd_bus *bus,
304                 char **argv,
305                 sd_bus_error *error) {
306
307         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
308         _cleanup_free_ char *name = NULL;
309         int r;
310
311         if (arg_unit)
312                 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
313         else
314                 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
315         if (!name)
316                 return -ENOMEM;
317
318         r = message_start_transient_unit_new(bus, name, &m);
319         if (r < 0)
320                 return r;
321
322         r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
323         if (r < 0)
324                 return r;
325
326         if (arg_service_type) {
327                 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
328                 if (r < 0)
329                         return r;
330         }
331
332         if (arg_exec_user) {
333                 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
334                 if (r < 0)
335                         return r;
336         }
337
338         if (arg_exec_group) {
339                 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
340                 if (r < 0)
341                         return r;
342         }
343
344         if (arg_nice_set) {
345                 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
346                 if (r < 0)
347                         return r;
348         }
349
350         if (!strv_isempty(arg_environment)) {
351                 r = sd_bus_message_open_container(m, 'r', "sv");
352                 if (r < 0)
353                         return r;
354
355                 r = sd_bus_message_append(m, "s", "Environment");
356                 if (r < 0)
357                         return r;
358
359                 r = sd_bus_message_open_container(m, 'v', "as");
360                 if (r < 0)
361                         return r;
362
363                 r = sd_bus_message_append_strv(m, arg_environment);
364                 if (r < 0)
365                         return r;
366
367                 r = sd_bus_message_close_container(m);
368                 if (r < 0)
369                         return r;
370
371                 r = sd_bus_message_close_container(m);
372                 if (r < 0)
373                         return r;
374         }
375
376         r = sd_bus_message_open_container(m, 'r', "sv");
377         if (r < 0)
378                 return r;
379
380         r = sd_bus_message_append(m, "s", "ExecStart");
381         if (r < 0)
382                 return r;
383
384         r = sd_bus_message_open_container(m, 'v', "a(sasb)");
385         if (r < 0)
386                 return r;
387
388         r = sd_bus_message_open_container(m, 'a', "(sasb)");
389         if (r < 0)
390                 return r;
391
392         r = sd_bus_message_open_container(m, 'r', "sasb");
393         if (r < 0)
394                 return r;
395
396         r = sd_bus_message_append(m, "s", argv[0]);
397         if (r < 0)
398                 return r;
399
400         r = sd_bus_message_append_strv(m, argv);
401         if (r < 0)
402                 return r;
403
404         r = sd_bus_message_append(m, "b", false);
405         if (r < 0)
406                 return r;
407
408         r = sd_bus_message_close_container(m);
409         if (r < 0)
410                 return r;
411
412         r = sd_bus_message_close_container(m);
413         if (r < 0)
414                 return r;
415
416         r = sd_bus_message_close_container(m);
417         if (r < 0)
418                 return r;
419
420         r = sd_bus_message_close_container(m);
421         if (r < 0)
422                 return r;
423
424         return message_start_transient_unit_send(bus, m, error, NULL);
425 }
426
427 static int start_transient_scope(
428                 sd_bus *bus,
429                 char **argv,
430                 sd_bus_error *error) {
431
432         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
433         _cleanup_free_ char *name = NULL;
434         int r;
435
436         assert(bus);
437
438         if (arg_unit)
439                 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
440         else
441                 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
442         if (!name)
443                 return -ENOMEM;
444
445         r = message_start_transient_unit_new(bus, name, &m);
446         if (r < 0)
447                 return r;
448
449         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
450         if (r < 0)
451                 return r;
452
453         r = message_start_transient_unit_send(bus, m, error, NULL);
454         if (r < 0)
455                 return r;
456
457         execvp(argv[0], argv);
458         log_error("Failed to execute: %m");
459         return -errno;
460 }
461
462 int main(int argc, char* argv[]) {
463         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
464         _cleanup_bus_unref_ sd_bus *bus = NULL;
465         _cleanup_free_ char *description = NULL, *command = NULL;
466         int r;
467
468         log_parse_environment();
469         log_open();
470
471         r = parse_argv(argc, argv);
472         if (r <= 0)
473                 goto finish;
474
475         r = find_binary(argv[optind], &command);
476         if (r < 0) {
477                 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
478                 goto finish;
479         }
480         argv[optind] = command;
481
482         if (!arg_description) {
483                 description = strv_join(argv + optind, " ");
484                 if (!description) {
485                         r = log_oom();
486                         goto finish;
487                 }
488
489                 arg_description = description;
490         }
491
492         r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
493         if (r < 0) {
494                 log_error("Failed to create bus connection: %s", strerror(-r));
495                 goto finish;
496         }
497
498         if (arg_scope)
499                 r = start_transient_scope(bus, argv + optind, &error);
500         else
501                 r = start_transient_service(bus, argv + optind, &error);
502         if (r < 0)
503                 log_error("Failed start transient unit: %s", bus_error_message(&error, r));
504
505 finish:
506         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
507 }