]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/run/run.c
run: allow non-absolute paths as command
[thirdparty/systemd.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-internal.h"
27 #include "bus-message.h"
28 #include "strv.h"
29 #include "build.h"
30 #include "unit-name.h"
31
32 static bool arg_scope = false;
33 static bool arg_user = 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
40 static int help(void) {
41
42 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
43 "Run the specified command in a transient scope or service unit.\n\n"
44 " -h --help Show this help\n"
45 " --version Show package version\n"
46 " --user Run as user unit\n"
47 " --scope Run this as scope rather than service\n"
48 " --unit=UNIT Run under the specified unit name\n"
49 " --description=TEXT Description for unit\n"
50 " --slice=SLICE Run in the specified slice\n"
51 " -r --remain-after-exit Leave service around until explicitly stopped\n"
52 " --send-sighup Send SIGHUP when terminating\n",
53 program_invocation_short_name);
54
55 return 0;
56 }
57
58 static int parse_argv(int argc, char *argv[]) {
59
60 enum {
61 ARG_VERSION = 0x100,
62 ARG_USER,
63 ARG_SCOPE,
64 ARG_UNIT,
65 ARG_DESCRIPTION,
66 ARG_SLICE,
67 ARG_SEND_SIGHUP,
68 };
69
70 static const struct option options[] = {
71 { "help", no_argument, NULL, 'h' },
72 { "version", no_argument, NULL, ARG_VERSION },
73 { "user", no_argument, NULL, ARG_USER },
74 { "scope", no_argument, NULL, ARG_SCOPE },
75 { "unit", required_argument, NULL, ARG_UNIT },
76 { "description", required_argument, NULL, ARG_DESCRIPTION },
77 { "slice", required_argument, NULL, ARG_SLICE },
78 { "remain-after-exit", no_argument, NULL, 'r' },
79 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
80 { NULL, 0, NULL, 0 },
81 };
82
83 int c;
84
85 assert(argc >= 0);
86 assert(argv);
87
88 while ((c = getopt_long(argc, argv, "+hr", options, NULL)) >= 0) {
89
90 switch (c) {
91
92 case 'h':
93 help();
94 return 0;
95
96 case ARG_VERSION:
97 puts(PACKAGE_STRING);
98 puts(SYSTEMD_FEATURES);
99 return 0;
100
101 case ARG_USER:
102 arg_user = true;
103 break;
104
105 case ARG_SCOPE:
106 arg_scope = true;
107 break;
108
109 case ARG_UNIT:
110 arg_unit = optarg;
111 break;
112
113 case ARG_DESCRIPTION:
114 arg_description = optarg;
115 break;
116
117 case ARG_SLICE:
118 arg_slice = optarg;
119 break;
120
121 case ARG_SEND_SIGHUP:
122 arg_send_sighup = true;
123 break;
124
125 case 'r':
126 arg_remain_after_exit = true;
127 break;
128
129 case '?':
130 return -EINVAL;
131
132 default:
133 log_error("Unknown option code %c", c);
134 return -EINVAL;
135 }
136 }
137
138 if (optind >= argc) {
139 log_error("Command line to execute required.");
140 return -EINVAL;
141 }
142
143 return 1;
144 }
145
146 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
147 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
148 int r;
149
150 log_info("Running as unit %s.", name);
151
152 r = sd_bus_message_new_method_call(
153 bus,
154 "org.freedesktop.systemd1",
155 "/org/freedesktop/systemd1",
156 "org.freedesktop.systemd1.Manager",
157 "StartTransientUnit", &m);
158 if (r < 0)
159 return r;
160
161 r = sd_bus_message_append(m, "ss", name, "fail");
162 if (r < 0)
163 return r;
164
165 r = sd_bus_message_open_container(m, 'a', "(sv)");
166 if (r < 0)
167 return r;
168
169 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
170 if (r < 0)
171 return r;
172
173 if (!isempty(arg_slice)) {
174 _cleanup_free_ char *slice;
175
176 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
177 if (!slice)
178 return -ENOMEM;
179
180 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
181 if (r < 0)
182 return r;
183 }
184
185 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
186 if (r < 0)
187 return r;
188
189 *ret = m;
190 m = NULL;
191
192 return 0;
193 }
194
195 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
196 int r;
197
198 r = sd_bus_message_close_container(m);
199 if (r < 0)
200 return r;
201
202 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
203 }
204
205 static int start_transient_service(
206 sd_bus *bus,
207 char **argv,
208 sd_bus_error *error) {
209
210 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
211 _cleanup_free_ char *name = NULL;
212 char **i;
213 int r;
214
215 if (arg_unit)
216 name = unit_name_mangle_with_suffix(arg_unit, ".service");
217 else
218 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
219 if (!name)
220 return -ENOMEM;
221
222 r = message_start_transient_unit_new(bus, name, &m);
223 if (r < 0)
224 return r;
225
226 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
227 if (r < 0)
228 return r;
229
230 r = sd_bus_message_open_container(m, 'r', "sv");
231 if (r < 0)
232 return r;
233
234 r = sd_bus_message_append(m, "s", "ExecStart");
235 if (r < 0)
236 return r;
237
238 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
239 if (r < 0)
240 return r;
241
242 r = sd_bus_message_open_container(m, 'a', "(sasb)");
243 if (r < 0)
244 return r;
245
246 r = sd_bus_message_open_container(m, 'r', "sasb");
247 if (r < 0)
248 return r;
249
250 r = sd_bus_message_append(m, "s", argv[0]);
251 if (r < 0)
252 return r;
253
254 r = sd_bus_message_open_container(m, 'a', "s");
255 if (r < 0)
256 return r;
257
258 STRV_FOREACH(i, argv) {
259 r = sd_bus_message_append(m, "s", *i);
260 if (r < 0)
261 return r;
262 }
263
264 r = sd_bus_message_close_container(m);
265 if (r < 0)
266 return r;
267
268 r = sd_bus_message_append(m, "b", false);
269 if (r < 0)
270 return r;
271
272 r = sd_bus_message_close_container(m);
273 if (r < 0)
274 return r;
275
276 r = sd_bus_message_close_container(m);
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 r = sd_bus_message_close_container(m);
285 if (r < 0)
286 return r;
287
288 return message_start_transient_unit_send(bus, m, error, &reply);
289 }
290
291 static int start_transient_scope(
292 sd_bus *bus,
293 char **argv,
294 sd_bus_error *error) {
295
296 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
297 _cleanup_free_ char *name = NULL;
298 int r;
299
300 if (arg_unit)
301 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
302 else
303 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
304 if (!name)
305 return -ENOMEM;
306
307 r = message_start_transient_unit_new(bus, name, &m);
308 if (r < 0)
309 return r;
310
311 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
312 if (r < 0)
313 return r;
314
315 r = message_start_transient_unit_send(bus, m, error, &reply);
316 if (r < 0)
317 return r;
318
319 execvp(argv[0], argv);
320 log_error("Failed to execute: %m");
321 return -errno;
322 }
323
324 int main(int argc, char* argv[]) {
325 sd_bus_error error = SD_BUS_ERROR_NULL;
326 _cleanup_bus_unref_ sd_bus *bus = NULL;
327 _cleanup_free_ char *description = NULL, *command = NULL;
328 int r;
329
330 log_parse_environment();
331 log_open();
332
333 r = parse_argv(argc, argv);
334 if (r <= 0)
335 goto fail;
336
337 r = find_binary(argv[optind], &command);
338 if (r < 0) {
339 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
340 goto fail;
341 }
342 argv[optind] = command;
343
344 if (!arg_description) {
345 description = strv_join(argv + optind, " ");
346 if (!description) {
347 r = log_oom();
348 goto fail;
349 }
350
351 arg_description = description;
352 }
353
354 if (arg_user)
355 r = sd_bus_open_user(&bus);
356 else
357 r = sd_bus_open_system(&bus);
358 if (r < 0) {
359 log_error("Failed to create new bus connection: %s", strerror(-r));
360 goto fail;
361 }
362
363 if (arg_scope)
364 r = start_transient_scope(bus, argv + optind, &error);
365 else
366 r = start_transient_service(bus, argv + optind, &error);
367 if (r < 0) {
368 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
369 sd_bus_error_free(&error);
370 goto fail;
371 }
372
373 fail:
374 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
375 }