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