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