chiark / gitweb /
bus-policy: append items rather than prepending them
[elogind.git] / src / dbus1-generator / dbus1-generator.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 "util.h"
23 #include "conf-parser.h"
24 #include "special.h"
25 #include "mkdir.h"
26 #include "bus-util.h"
27 #include "bus-internal.h"
28 #include "unit-name.h"
29 #include "cgroup-util.h"
30
31 static const char *arg_dest_late = "/tmp", *arg_dest = "/tmp";
32
33 static int create_dbus_files(
34                 const char *path,
35                 const char *name,
36                 const char *service,
37                 const char *exec,
38                 const char *user,
39                 const char *type) {
40
41         _cleanup_free_ char *b = NULL, *s = NULL, *lnk = NULL;
42         _cleanup_fclose_ FILE *f = NULL;
43         int r;
44
45         assert(path);
46         assert(name);
47         assert(service || exec);
48
49         if (!service) {
50                 _cleanup_free_ char *a = NULL;
51
52                 s = strjoin("dbus-", name, ".service", NULL);
53                 if (!s)
54                         return log_oom();
55
56                 a = strjoin(arg_dest_late, "/", s, NULL);
57                 if (!a)
58                         return log_oom();
59
60                 f = fopen(a, "wxe");
61                 if (!f) {
62                         log_error("Failed to create %s: %m", a);
63                         return -errno;
64                 }
65
66                 fprintf(f,
67                         "# Automatically generated by systemd-dbus1-generator\n\n"
68                         "[Unit]\n"
69                         "SourcePath=%s\n"
70                         "Description=DBUS1: %s\n"
71                         "Documentation=man:systemd-dbus1-generator(8)\n\n"
72                         "[Service]\n"
73                         "ExecStart=%s\n"
74                         "Type=dbus\n"
75                         "BusName=%s\n",
76                         path,
77                         name,
78                         exec,
79                         name);
80
81                 if (user)
82                         fprintf(f, "User=%s\n", user);
83
84
85                 if (type) {
86                         fprintf(f, "Environment=DBUS_STARTER_BUS_TYPE=%s\n", type);
87
88                         if (streq(type, "system"))
89                                 fprintf(f, "Environment=DBUS_STARTER_ADDRESS=" DEFAULT_SYSTEM_BUS_PATH "\n");
90                         else if (streq(type, "session")) {
91                                 char *run;
92
93                                 run = getenv("XDG_RUNTIME_DIR");
94                                 if (!run) {
95                                         log_error("XDG_RUNTIME_DIR not set.");
96                                         return -EINVAL;
97                                 }
98
99                                 fprintf(f, "Environment=DBUS_STARTER_ADDRESS="KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT "\n",
100                                         getuid(), run);
101                         }
102                 }
103
104                 r = fflush_and_check(f);
105                 if (r < 0) {
106                         log_error("Failed to write %s: %s", a, strerror(-r));
107                         return r;
108                 }
109
110                 fclose(f);
111                 f = NULL;
112
113                 service = s;
114         }
115
116         b = strjoin(arg_dest_late, "/", name, ".busname", NULL);
117         if (!b)
118                 return log_oom();
119
120         f = fopen(b, "wxe");
121         if (!f) {
122                 log_error("Failed to create %s: %m", b);
123                 return -errno;
124         }
125
126         fprintf(f,
127                 "# Automatically generated by systemd-dbus1-generator\n\n"
128                 "[Unit]\n"
129                 "SourcePath=%s\n"
130                 "Description=DBUS1: %s\n"
131                 "Documentation=man:systemd-dbus1-generator(8)\n\n"
132                 "[BusName]\n"
133                 "Name=%s\n"
134                 "Service=%s\n"
135                 "AllowWorld=talk\n",
136                 path,
137                 name,
138                 name,
139                 service);
140
141         r = fflush_and_check(f);
142         if (r < 0) {
143                 log_error("Failed to write %s: %s", b, strerror(-r));
144                 return r;
145         }
146
147         lnk = strjoin(arg_dest_late, "/" SPECIAL_BUSNAMES_TARGET ".wants/", name, ".busname", NULL);
148         if (!lnk)
149                 return log_oom();
150
151         mkdir_parents_label(lnk, 0755);
152         if (symlink(b, lnk)) {
153                 log_error("Failed to create symlink %s: %m", lnk);
154                 return -errno;
155         }
156
157         return 0;
158 }
159
160 static int add_dbus(const char *path, const char *fname, const char *type) {
161         _cleanup_free_ char *name = NULL, *exec = NULL, *user = NULL, *service = NULL;
162
163         const ConfigTableItem table[] = {
164                 { "D-BUS Service", "Name", config_parse_string, 0, &name },
165                 { "D-BUS Service", "Exec", config_parse_string, 0, &exec },
166                 { "D-BUS Service", "User", config_parse_string, 0, &user },
167                 { "D-BUS Service", "SystemdService", config_parse_string, 0, &service },
168         };
169
170         char *p;
171         int r;
172
173         assert(path);
174         assert(fname);
175
176         p = strappenda(path, "/", fname);
177         r = config_parse(NULL, p, NULL,
178                          "D-BUS Service\0",
179                          config_item_table_lookup, table,
180                          true, false, true, NULL);
181         if (r < 0)
182                 return r;
183
184         if (!name) {
185                 log_warning("Activation file %s lacks name setting, ignoring.", p);
186                 return 0;
187         }
188
189         if (!service_name_is_valid(name)) {
190                 log_warning("Bus service name %s is not valid, ignoring.", name);
191                 return 0;
192         }
193
194         if (streq(name, "org.freedesktop.systemd1")) {
195                 log_debug("Skipping %s, identified as systemd.", p);
196                 return 0;
197         }
198
199         if (service) {
200                 if (!unit_name_is_valid(service, TEMPLATE_INVALID)) {
201                         log_warning("Unit name %s is not valid, ignoring.", service);
202                         return 0;
203                 }
204                 if (!endswith(service, ".service")) {
205                         log_warning("Bus names can only activate services, ignoring %s.", p);
206                         return 0;
207                 }
208         } else {
209                 if (streq(exec, "/bin/false") || !exec) {
210                         log_warning("Neither service name nor binary path specified, ignoring %s.", p);
211                         return 0;
212                 }
213
214                 if (exec[0] != '/') {
215                         log_warning("Exec= in %s does not start with an absolute path, ignoring.", p);
216                         return 0;
217                 }
218         }
219
220         return create_dbus_files(p, name, service, exec, user, type);
221 }
222
223 static int parse_dbus_fragments(const char *path, const char *type) {
224         _cleanup_closedir_ DIR *d = NULL;
225         struct dirent *de;
226         int r;
227
228         assert(path);
229         assert(type);
230
231         d = opendir(path);
232         if (!d) {
233                 if (errno == -ENOENT)
234                         return 0;
235
236                 log_error("Failed to enumerate D-Bus activated services: %m");
237                 return -errno;
238         }
239
240         r = 0;
241         FOREACH_DIRENT(de, d, goto fail) {
242                 int q;
243
244                 if (!endswith(de->d_name, ".service"))
245                         continue;
246
247                 q = add_dbus(path, de->d_name, type);
248                 if (q < 0)
249                         r = q;
250         }
251
252         return r;
253
254 fail:
255         log_error("Failed to read D-Bus services directory: %m");
256         return -errno;
257 }
258
259 static int link_busnames_target(const char *units) {
260         const char *f, *t;
261
262         f = strappenda(units, "/" SPECIAL_BUSNAMES_TARGET);
263         t = strappenda(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET);
264
265         mkdir_parents_label(t, 0755);
266         if (symlink(f, t) < 0) {
267                 log_error("Failed to create symlink %s: %m", t);
268                 return -errno;
269         }
270
271         return 0;
272 }
273
274 static int link_compatibility(const char *units) {
275         const char *f, *t;
276
277         f = strappenda(units, "/systemd-bus-proxyd.socket");
278         t = strappenda(arg_dest, "/" SPECIAL_DBUS_SOCKET);
279         mkdir_parents_label(t, 0755);
280         if (symlink(f, t) < 0) {
281                 log_error("Failed to create symlink %s: %m", t);
282                 return -errno;
283         }
284
285         f = strappenda(units, "/systemd-bus-proxyd.socket");
286         t = strappenda(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket");
287         mkdir_parents_label(t, 0755);
288         if (symlink(f, t) < 0) {
289                 log_error("Failed to create symlink %s: %m", t);
290                 return -errno;
291         }
292
293         t = strappenda(arg_dest, "/" SPECIAL_DBUS_SERVICE);
294         if (symlink("/dev/null", t) < 0) {
295                 log_error("Failed to mask %s: %m", t);
296                 return -errno;
297         }
298
299         return 0;
300 }
301
302 int main(int argc, char *argv[]) {
303         const char *path, *type, *units;
304         int r, q;
305
306         if (argc > 1 && argc != 4) {
307                 log_error("This program takes three or no arguments.");
308                 return EXIT_FAILURE;
309         }
310
311         if (argc > 1) {
312                 arg_dest = argv[1];
313                 arg_dest_late = argv[3];
314         }
315
316         log_set_target(LOG_TARGET_SAFE);
317         log_parse_environment();
318         log_open();
319
320         umask(0022);
321
322         if (access("/dev/kdbus/control", F_OK) < 0)
323                 return 0;
324
325         r = cg_pid_get_owner_uid(0, NULL);
326         if (r >= 0) {
327                 path = "/usr/share/dbus-1/services";
328                 type = "session";
329                 units = USER_DATA_UNIT_PATH;
330         } else if (r == -ENOENT) {
331                 path = "/usr/share/dbus-1/system-services";
332                 type = "system";
333                 units = SYSTEM_DATA_UNIT_PATH;
334         } else {
335                 log_error("Failed to determine whether we are running as user or system instance: %s", strerror(-r));
336                 return r;
337         }
338
339         r = parse_dbus_fragments(path, type);
340
341         /* FIXME: One day this should just be pulled in statically from basic.target */
342         q = link_busnames_target(units);
343         if (q < 0)
344                 r = q;
345
346         q = link_compatibility(units);
347         if (q < 0)
348                 r = q;
349
350         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
351 }