chiark / gitweb /
Remove unnecessary casts in printfs
[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         r = sd_bus_message_new_method_call(
253                         bus,
254                         &m,
255                         "org.freedesktop.systemd1",
256                         "/org/freedesktop/systemd1",
257                         "org.freedesktop.systemd1.Manager",
258                         "StartTransientUnit");
259         if (r < 0)
260                 return r;
261
262         r = sd_bus_message_append(m, "ss", name, "fail");
263         if (r < 0)
264                 return r;
265
266         r = sd_bus_message_open_container(m, 'a', "(sv)");
267         if (r < 0)
268                 return r;
269
270         STRV_FOREACH(i, arg_property) {
271                 r = sd_bus_message_open_container(m, 'r', "sv");
272                 if (r < 0)
273                         return r;
274
275                 r = bus_append_unit_property_assignment(m, *i);
276                 if (r < 0)
277                         return r;
278
279                 r = sd_bus_message_close_container(m);
280                 if (r < 0)
281                         return r;
282         }
283
284         r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
285         if (r < 0)
286                 return r;
287
288         if (!isempty(arg_slice)) {
289                 _cleanup_free_ char *slice;
290
291                 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
292                 if (!slice)
293                         return -ENOMEM;
294
295                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
296                 if (r < 0)
297                         return r;
298         }
299
300         if (arg_send_sighup) {
301                 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
302                 if (r < 0)
303                         return r;
304         }
305
306         *ret = m;
307         m = NULL;
308
309         return 0;
310 }
311
312 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
313         int r;
314
315         assert(bus);
316         assert(m);
317
318         r = sd_bus_message_close_container(m);
319         if (r < 0)
320                 return r;
321
322         r = sd_bus_message_append(m, "a(sa(sv))", 0);
323         if (r < 0)
324                 return r;
325
326         return sd_bus_call(bus, m, 0, error, reply);
327 }
328
329 static int start_transient_service(
330                 sd_bus *bus,
331                 char **argv,
332                 sd_bus_error *error) {
333
334         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
335         _cleanup_free_ char *name = NULL;
336         int r;
337
338         if (arg_unit)
339                 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
340         else
341                 asprintf(&name, "run-"PID_FMT".service", getpid());
342         if (!name)
343                 return log_oom();
344
345         r = message_start_transient_unit_new(bus, name, &m);
346         if (r < 0)
347                 return bus_log_create_error(r);
348
349         if (arg_remain_after_exit) {
350                 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
351                 if (r < 0)
352                         return bus_log_create_error(r);
353         }
354
355         if (arg_service_type) {
356                 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
357                 if (r < 0)
358                         return bus_log_create_error(r);
359         }
360
361         if (arg_exec_user) {
362                 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
363                 if (r < 0)
364                         return bus_log_create_error(r);
365         }
366
367         if (arg_exec_group) {
368                 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
369                 if (r < 0)
370                         return bus_log_create_error(r);
371         }
372
373         if (arg_nice_set) {
374                 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
375                 if (r < 0)
376                         return bus_log_create_error(r);
377         }
378
379         if (!strv_isempty(arg_environment)) {
380                 r = sd_bus_message_open_container(m, 'r', "sv");
381                 if (r < 0)
382                         return bus_log_create_error(r);
383
384                 r = sd_bus_message_append(m, "s", "Environment");
385                 if (r < 0)
386                         return bus_log_create_error(r);
387
388                 r = sd_bus_message_open_container(m, 'v', "as");
389                 if (r < 0)
390                         return bus_log_create_error(r);
391
392                 r = sd_bus_message_append_strv(m, arg_environment);
393                 if (r < 0)
394                         return bus_log_create_error(r);
395
396                 r = sd_bus_message_close_container(m);
397                 if (r < 0)
398                         return bus_log_create_error(r);
399
400                 r = sd_bus_message_close_container(m);
401                 if (r < 0)
402                         return bus_log_create_error(r);
403         }
404
405         r = sd_bus_message_open_container(m, 'r', "sv");
406         if (r < 0)
407                 return bus_log_create_error(r);
408
409         r = sd_bus_message_append(m, "s", "ExecStart");
410         if (r < 0)
411                 return bus_log_create_error(r);
412
413         r = sd_bus_message_open_container(m, 'v', "a(sasb)");
414         if (r < 0)
415                 return bus_log_create_error(r);
416
417         r = sd_bus_message_open_container(m, 'a', "(sasb)");
418         if (r < 0)
419                 return bus_log_create_error(r);
420
421         r = sd_bus_message_open_container(m, 'r', "sasb");
422         if (r < 0)
423                 return bus_log_create_error(r);
424
425         r = sd_bus_message_append(m, "s", argv[0]);
426         if (r < 0)
427                 return bus_log_create_error(r);
428
429         r = sd_bus_message_append_strv(m, argv);
430         if (r < 0)
431                 return bus_log_create_error(r);
432
433         r = sd_bus_message_append(m, "b", false);
434         if (r < 0)
435                 return bus_log_create_error(r);
436
437         r = sd_bus_message_close_container(m);
438         if (r < 0)
439                 return bus_log_create_error(r);
440
441         r = sd_bus_message_close_container(m);
442         if (r < 0)
443                 return bus_log_create_error(r);
444
445         r = sd_bus_message_close_container(m);
446         if (r < 0)
447                 return bus_log_create_error(r);
448
449         r = sd_bus_message_close_container(m);
450         if (r < 0)
451                 return bus_log_create_error(r);
452
453         r = message_start_transient_unit_send(bus, m, error, NULL);
454         if (r < 0)
455                 return bus_log_create_error(r);
456
457         log_info("Running as unit %s.", name);
458
459         return 0;
460 }
461
462 static int start_transient_scope(
463                 sd_bus *bus,
464                 char **argv,
465                 sd_bus_error *error) {
466
467         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
468         _cleanup_free_ char *name = NULL;
469         _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
470         int r;
471
472         assert(bus);
473
474         if (arg_unit)
475                 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
476         else
477                 asprintf(&name, "run-"PID_FMT".scope", getpid());
478         if (!name)
479                 return log_oom();
480
481         r = message_start_transient_unit_new(bus, name, &m);
482         if (r < 0)
483                 return bus_log_create_error(r);
484
485         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
486         if (r < 0)
487                 return bus_log_create_error(r);
488
489         r = message_start_transient_unit_send(bus, m, error, NULL);
490         if (r < 0)
491                 return bus_log_create_error(r);
492
493         if (arg_nice_set) {
494                 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) {
495                         log_error("Failed to set nice level: %m");
496                         return -errno;
497                 }
498         }
499
500         if (arg_exec_group) {
501                 gid_t gid;
502
503                 r = get_group_creds(&arg_exec_group, &gid);
504                 if (r < 0) {
505                         log_error("Failed to resolve group %s: %s", arg_exec_group, strerror(-r));
506                         return r;
507                 }
508
509                 if (setresgid(gid, gid, gid) < 0) {
510                         log_error("Failed to change GID to " GID_FMT ": %m", gid);
511                         return -errno;
512                 }
513         }
514
515         if (arg_exec_user) {
516                 const char *home, *shell;
517                 uid_t uid;
518                 gid_t gid;
519
520                 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
521                 if (r < 0) {
522                         log_error("Failed to resolve user %s: %s", arg_exec_user, strerror(-r));
523                         return r;
524                 }
525
526                 r = strv_extendf(&user_env, "HOME=%s", home);
527                 if (r < 0)
528                         return log_oom();
529
530                 r = strv_extendf(&user_env, "SHELL=%s", shell);
531                 if (r < 0)
532                         return log_oom();
533
534                 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
535                 if (r < 0)
536                         return log_oom();
537
538                 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
539                 if (r < 0)
540                         return log_oom();
541
542                 if (!arg_exec_group) {
543                         if (setresgid(gid, gid, gid) < 0) {
544                                 log_error("Failed to change GID to " GID_FMT ": %m", gid);
545                                 return -errno;
546                         }
547                 }
548
549                 if (setresuid(uid, uid, uid) < 0) {
550                         log_error("Failed to change UID to " UID_FMT ": %m", uid);
551                         return -errno;
552                 }
553         }
554
555         env = strv_env_merge(3, environ, user_env, arg_environment);
556         if (!env)
557                 return log_oom();
558
559         log_info("Running as unit %s.", name);
560
561         execvpe(argv[0], argv, env);
562         log_error("Failed to execute: %m");
563         return -errno;
564 }
565
566 int main(int argc, char* argv[]) {
567         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
568         _cleanup_bus_unref_ sd_bus *bus = NULL;
569         _cleanup_free_ char *description = NULL, *command = NULL;
570         int r;
571
572         log_parse_environment();
573         log_open();
574
575         r = parse_argv(argc, argv);
576         if (r <= 0)
577                 goto finish;
578
579         r = find_binary(argv[optind], &command);
580         if (r < 0) {
581                 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
582                 goto finish;
583         }
584         argv[optind] = command;
585
586         if (!arg_description) {
587                 description = strv_join(argv + optind, " ");
588                 if (!description) {
589                         r = log_oom();
590                         goto finish;
591                 }
592
593                 arg_description = description;
594         }
595
596         r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
597         if (r < 0) {
598                 log_error("Failed to create bus connection: %s", strerror(-r));
599                 goto finish;
600         }
601
602         if (arg_scope)
603                 r = start_transient_scope(bus, argv + optind, &error);
604         else
605                 r = start_transient_service(bus, argv + optind, &error);
606
607 finish:
608         strv_free(arg_environment);
609         strv_free(arg_property);
610
611         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
612 }