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