chiark / gitweb /
update TODO
[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                         &m,
245                         "org.freedesktop.systemd1",
246                         "/org/freedesktop/systemd1",
247                         "org.freedesktop.systemd1.Manager",
248                         "StartTransientUnit");
249         if (r < 0)
250                 return r;
251
252         r = sd_bus_message_append(m, "ss", name, "fail");
253         if (r < 0)
254                 return r;
255
256         r = sd_bus_message_open_container(m, 'a', "(sv)");
257         if (r < 0)
258                 return r;
259
260         r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
261         if (r < 0)
262                 return r;
263
264         if (!isempty(arg_slice)) {
265                 _cleanup_free_ char *slice;
266
267                 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
268                 if (!slice)
269                         return -ENOMEM;
270
271                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
272                 if (r < 0)
273                         return r;
274         }
275
276         r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
277         if (r < 0)
278                 return r;
279
280         *ret = m;
281         m = NULL;
282
283         return 0;
284 }
285
286 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
287         int r;
288
289         assert(bus);
290         assert(m);
291
292         r = sd_bus_message_close_container(m);
293         if (r < 0)
294                 return r;
295
296         r = sd_bus_message_append(m, "a(sa(sv))", 0);
297         if (r < 0)
298                 return r;
299
300         return sd_bus_call(bus, m, 0, error, reply);
301 }
302
303 static int start_transient_service(
304                 sd_bus *bus,
305                 char **argv,
306                 sd_bus_error *error) {
307
308         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
309         _cleanup_free_ char *name = NULL;
310         int r;
311
312         if (arg_unit)
313                 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
314         else
315                 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
316         if (!name)
317                 return -ENOMEM;
318
319         r = message_start_transient_unit_new(bus, name, &m);
320         if (r < 0)
321                 return r;
322
323         r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
324         if (r < 0)
325                 return r;
326
327         if (arg_service_type) {
328                 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
329                 if (r < 0)
330                         return r;
331         }
332
333         if (arg_exec_user) {
334                 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
335                 if (r < 0)
336                         return r;
337         }
338
339         if (arg_exec_group) {
340                 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
341                 if (r < 0)
342                         return r;
343         }
344
345         if (arg_nice_set) {
346                 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
347                 if (r < 0)
348                         return r;
349         }
350
351         if (!strv_isempty(arg_environment)) {
352                 r = sd_bus_message_open_container(m, 'r', "sv");
353                 if (r < 0)
354                         return r;
355
356                 r = sd_bus_message_append(m, "s", "Environment");
357                 if (r < 0)
358                         return r;
359
360                 r = sd_bus_message_open_container(m, 'v', "as");
361                 if (r < 0)
362                         return r;
363
364                 r = sd_bus_message_append_strv(m, arg_environment);
365                 if (r < 0)
366                         return r;
367
368                 r = sd_bus_message_close_container(m);
369                 if (r < 0)
370                         return r;
371
372                 r = sd_bus_message_close_container(m);
373                 if (r < 0)
374                         return r;
375         }
376
377         r = sd_bus_message_open_container(m, 'r', "sv");
378         if (r < 0)
379                 return r;
380
381         r = sd_bus_message_append(m, "s", "ExecStart");
382         if (r < 0)
383                 return r;
384
385         r = sd_bus_message_open_container(m, 'v', "a(sasb)");
386         if (r < 0)
387                 return r;
388
389         r = sd_bus_message_open_container(m, 'a', "(sasb)");
390         if (r < 0)
391                 return r;
392
393         r = sd_bus_message_open_container(m, 'r', "sasb");
394         if (r < 0)
395                 return r;
396
397         r = sd_bus_message_append(m, "s", argv[0]);
398         if (r < 0)
399                 return r;
400
401         r = sd_bus_message_append_strv(m, argv);
402         if (r < 0)
403                 return r;
404
405         r = sd_bus_message_append(m, "b", false);
406         if (r < 0)
407                 return r;
408
409         r = sd_bus_message_close_container(m);
410         if (r < 0)
411                 return r;
412
413         r = sd_bus_message_close_container(m);
414         if (r < 0)
415                 return r;
416
417         r = sd_bus_message_close_container(m);
418         if (r < 0)
419                 return r;
420
421         r = sd_bus_message_close_container(m);
422         if (r < 0)
423                 return r;
424
425         return message_start_transient_unit_send(bus, m, error, NULL);
426 }
427
428 static int start_transient_scope(
429                 sd_bus *bus,
430                 char **argv,
431                 sd_bus_error *error) {
432
433         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
434         _cleanup_free_ char *name = NULL;
435         int r;
436
437         assert(bus);
438
439         if (arg_unit)
440                 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
441         else
442                 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
443         if (!name)
444                 return -ENOMEM;
445
446         r = message_start_transient_unit_new(bus, name, &m);
447         if (r < 0)
448                 return r;
449
450         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
451         if (r < 0)
452                 return r;
453
454         r = message_start_transient_unit_send(bus, m, error, NULL);
455         if (r < 0)
456                 return r;
457
458         execvp(argv[0], argv);
459         log_error("Failed to execute: %m");
460         return -errno;
461 }
462
463 int main(int argc, char* argv[]) {
464         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
465         _cleanup_bus_unref_ sd_bus *bus = NULL;
466         _cleanup_free_ char *description = NULL, *command = NULL;
467         int r;
468
469         log_parse_environment();
470         log_open();
471
472         r = parse_argv(argc, argv);
473         if (r <= 0)
474                 goto finish;
475
476         r = find_binary(argv[optind], &command);
477         if (r < 0) {
478                 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
479                 goto finish;
480         }
481         argv[optind] = command;
482
483         if (!arg_description) {
484                 description = strv_join(argv + optind, " ");
485                 if (!description) {
486                         r = log_oom();
487                         goto finish;
488                 }
489
490                 arg_description = description;
491         }
492
493         r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
494         if (r < 0) {
495                 log_error("Failed to create bus connection: %s", strerror(-r));
496                 goto finish;
497         }
498
499         if (arg_scope)
500                 r = start_transient_scope(bus, argv + optind, &error);
501         else
502                 r = start_transient_service(bus, argv + optind, &error);
503         if (r < 0)
504                 log_error("Failed start transient unit: %s", bus_error_message(&error, r));
505
506 finish:
507         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
508 }