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