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