]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/run/run.c
path-util.c: small modernization
[thirdparty/systemd.git] / src / run / run.c
CommitLineData
c2756a68
LP
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>
6c12b52e 23#include <getopt.h>
c2756a68
LP
24
25#include "sd-bus.h"
26#include "bus-internal.h"
27#include "bus-message.h"
28#include "strv.h"
6c12b52e
LP
29#include "build.h"
30#include "unit-name.h"
c2756a68 31
6c12b52e
LP
32static bool arg_scope = false;
33static bool arg_user = false;
6577c7ce 34static bool arg_remain_after_exit = false;
6c12b52e 35static const char *arg_unit = NULL;
9f2e86af 36static const char *arg_description = NULL;
c221420b 37static const char *arg_slice = NULL;
a6c0353b 38static bool arg_send_sighup = false;
c2756a68 39
6c12b52e
LP
40static int help(void) {
41
42 printf("%s [OPTIONS...] [COMMAND LINE...]\n\n"
3803cde4 43 "Run the specified command in a transient scope or service unit.\n\n"
6577c7ce
LP
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"
a6c0353b
LP
51 " -r --remain-after-exit Leave service around until explicitly stopped\n"
52 " --send-sighup Send SIGHUP when terminating\n",
6c12b52e
LP
53 program_invocation_short_name);
54
55 return 0;
56}
57
58static int parse_argv(int argc, char *argv[]) {
59
60 enum {
61 ARG_VERSION = 0x100,
62 ARG_USER,
63 ARG_SCOPE,
9f2e86af 64 ARG_UNIT,
c221420b 65 ARG_DESCRIPTION,
a6c0353b
LP
66 ARG_SLICE,
67 ARG_SEND_SIGHUP,
6c12b52e
LP
68 };
69
70 static const struct option options[] = {
6577c7ce
LP
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 },
a6c0353b
LP
78 { "remain-after-exit", no_argument, NULL, 'r' },
79 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
6577c7ce 80 { NULL, 0, NULL, 0 },
6c12b52e
LP
81 };
82
83 int c;
84
85 assert(argc >= 0);
86 assert(argv);
87
6577c7ce 88 while ((c = getopt_long(argc, argv, "+hr", options, NULL)) >= 0) {
6c12b52e
LP
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
9f2e86af
LP
113 case ARG_DESCRIPTION:
114 arg_description = optarg;
115 break;
116
c221420b
LP
117 case ARG_SLICE:
118 arg_slice = optarg;
119 break;
120
a6c0353b
LP
121 case ARG_SEND_SIGHUP:
122 arg_send_sighup = true;
123 break;
124
6577c7ce
LP
125 case 'r':
126 arg_remain_after_exit = true;
127 break;
128
6c12b52e
LP
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
146static 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;
c2756a68
LP
148 int r;
149
6c12b52e
LP
150 log_info("Running as unit %s.", name);
151
c2756a68
LP
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
9f2e86af 169 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
c2756a68
LP
170 if (r < 0)
171 return r;
172
c221420b
LP
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
a6c0353b
LP
185 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
186 if (r < 0)
187 return r;
188
6c12b52e
LP
189 *ret = m;
190 m = NULL;
191
192 return 0;
193}
194
195static 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
6c12b52e
LP
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
205static 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
6577c7ce
LP
226 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
227 if (r < 0)
228 return r;
229
9f2e86af
LP
230 r = sd_bus_message_open_container(m, 'r', "sv");
231 if (r < 0)
232 return r;
233
c2756a68
LP
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
9f2e86af
LP
284 r = sd_bus_message_close_container(m);
285 if (r < 0)
286 return r;
287
6c12b52e
LP
288 return message_start_transient_unit_send(bus, m, error, &reply);
289}
290
291static 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);
c2756a68
LP
308 if (r < 0)
309 return r;
310
9f2e86af 311 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
6c12b52e
LP
312 if (r < 0)
313 return r;
314
315 r = message_start_transient_unit_send(bus, m, error, &reply);
c2756a68
LP
316 if (r < 0)
317 return r;
318
6c12b52e
LP
319 execvp(argv[0], argv);
320 log_error("Failed to execute: %m");
321 return -errno;
c2756a68
LP
322}
323
324int main(int argc, char* argv[]) {
325 sd_bus_error error = SD_BUS_ERROR_NULL;
326 _cleanup_bus_unref_ sd_bus *bus = NULL;
9f2e86af 327 _cleanup_free_ char *description = NULL;
c2756a68
LP
328 int r;
329
330 log_parse_environment();
331 log_open();
332
6c12b52e
LP
333 r = parse_argv(argc, argv);
334 if (r <= 0)
c2756a68 335 goto fail;
c2756a68 336
9f2e86af
LP
337 if (!arg_description) {
338 description = strv_join(argv + optind, " ");
339 if (!description) {
340 r = log_oom();
341 goto fail;
342 }
343
344 arg_description = description;
345 }
346
6c12b52e
LP
347 if (arg_user)
348 r = sd_bus_open_user(&bus);
349 else
350 r = sd_bus_open_system(&bus);
c2756a68 351 if (r < 0) {
6c12b52e 352 log_error("Failed to create new bus connection: %s", strerror(-r));
c2756a68
LP
353 goto fail;
354 }
355
6c12b52e
LP
356 if (arg_scope)
357 r = start_transient_scope(bus, argv + optind, &error);
358 else
359 r = start_transient_service(bus, argv + optind, &error);
c2756a68 360 if (r < 0) {
9f2e86af 361 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
c2756a68
LP
362 sd_bus_error_free(&error);
363 goto fail;
364 }
365
366fail:
367 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
368}