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