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