]>
Commit | Line | Data |
---|---|---|
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" | |
40ca29a1 | 26 | #include "bus-util.h" |
c2756a68 | 27 | #include "strv.h" |
6c12b52e LP |
28 | #include "build.h" |
29 | #include "unit-name.h" | |
4bcc8c3c | 30 | #include "path-util.h" |
c2756a68 | 31 | |
6c12b52e LP |
32 | static bool arg_scope = false; |
33 | static bool arg_user = false; | |
6577c7ce | 34 | static bool arg_remain_after_exit = false; |
6c12b52e | 35 | static const char *arg_unit = NULL; |
9f2e86af | 36 | static const char *arg_description = NULL; |
c221420b | 37 | static const char *arg_slice = NULL; |
a6c0353b | 38 | static bool arg_send_sighup = false; |
c2756a68 | 39 | |
6c12b52e LP |
40 | static int help(void) { |
41 | ||
c9d954b2 | 42 | printf("%s [OPTIONS...] COMMAND [ARGS...]\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 | ||
58 | static 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 | ||
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; | |
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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); | |
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 | ||
324 | int main(int argc, char* argv[]) { | |
325 | sd_bus_error error = SD_BUS_ERROR_NULL; | |
326 | _cleanup_bus_unref_ sd_bus *bus = NULL; | |
c9d954b2 | 327 | _cleanup_free_ char *description = NULL, *command = 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 | |
c9d954b2 ZJS |
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 | ||
9f2e86af LP |
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 | ||
6c12b52e LP |
354 | if (arg_user) |
355 | r = sd_bus_open_user(&bus); | |
356 | else | |
357 | r = sd_bus_open_system(&bus); | |
c2756a68 | 358 | if (r < 0) { |
6c12b52e | 359 | log_error("Failed to create new bus connection: %s", strerror(-r)); |
c2756a68 LP |
360 | goto fail; |
361 | } | |
362 | ||
6c12b52e LP |
363 | if (arg_scope) |
364 | r = start_transient_scope(bus, argv + optind, &error); | |
365 | else | |
366 | r = start_transient_service(bus, argv + optind, &error); | |
c2756a68 | 367 | if (r < 0) { |
9f2e86af | 368 | log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r)); |
c2756a68 LP |
369 | sd_bus_error_free(&error); |
370 | goto fail; | |
371 | } | |
372 | ||
373 | fail: | |
374 | return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; | |
375 | } |