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