]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/dbus1-generator/dbus1-generator.c
e1ffc5515f0deecb286b425b1751f4f7600f303d
[thirdparty/systemd.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 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 "AllowWorld=talk\n",
132 path,
133 name,
134 name,
135 service);
136
137 fflush(f);
138 if (ferror(f)) {
139 log_error("Failed to write %s: %m", b);
140 return -errno;
141 }
142
143 lnk = strjoin(arg_dest_late, "/" SPECIAL_BUSNAMES_TARGET ".wants/", name, ".busname", NULL);
144 if (!lnk)
145 return log_oom();
146
147 mkdir_parents_label(lnk, 0755);
148 if (symlink(b, lnk)) {
149 log_error("Failed to create symlink %s: %m", lnk);
150 return -errno;
151 }
152
153 return 0;
154 }
155
156 static int add_dbus(const char *path, const char *fname, const char *type) {
157 _cleanup_free_ char *name = NULL, *exec = NULL, *user = NULL, *service = NULL;
158
159 const ConfigTableItem table[] = {
160 { "D-BUS Service", "Name", config_parse_string, 0, &name },
161 { "D-BUS Service", "Exec", config_parse_string, 0, &exec },
162 { "D-BUS Service", "User", config_parse_string, 0, &user },
163 { "D-BUS Service", "SystemdService", config_parse_string, 0, &service },
164 };
165
166 char *p;
167 int r;
168
169 assert(path);
170 assert(fname);
171
172 p = strappenda(path, "/", fname);
173 r = config_parse(NULL, p, NULL,
174 "D-BUS Service\0",
175 config_item_table_lookup, table,
176 true, false, true, NULL);
177 if (r < 0)
178 return r;
179
180 if (!name) {
181 log_warning("Activation file %s lacks name setting, ignoring.", p);
182 return 0;
183 }
184
185 if (!service_name_is_valid(name)) {
186 log_warning("Bus service name %s is not valid, ignoring.", name);
187 return 0;
188 }
189
190 if (streq(name, "org.freedesktop.systemd1")) {
191 log_debug("Skipping %s, identified as systemd.", p);
192 return 0;
193 }
194
195 if (service) {
196 if (!unit_name_is_valid(service, TEMPLATE_INVALID)) {
197 log_warning("Unit name %s is not valid, ignoring.", service);
198 return 0;
199 }
200 if (!endswith(service, ".service")) {
201 log_warning("Bus names can only activate services, ignoring %s.", p);
202 return 0;
203 }
204 } else {
205 if (streq(exec, "/bin/false") || !exec) {
206 log_warning("Neither service name nor binary path specified, ignoring %s.", p);
207 return 0;
208 }
209
210 if (exec[0] != '/') {
211 log_warning("Exec= in %s does not start with an absolute path, ignoring.", p);
212 return 0;
213 }
214 }
215
216 return create_dbus_files(p, name, service, exec, user, type);
217 }
218
219 static int parse_dbus_fragments(const char *path, const char *type) {
220 _cleanup_closedir_ DIR *d = NULL;
221 struct dirent *de;
222 int r;
223
224 assert(path);
225 assert(type);
226
227 d = opendir(path);
228 if (!d) {
229 if (errno == -ENOENT)
230 return 0;
231
232 log_error("Failed to enumerate D-Bus activated services: %m");
233 return -errno;
234 }
235
236 r = 0;
237 FOREACH_DIRENT(de, d, goto fail) {
238 int q;
239
240 if (!endswith(de->d_name, ".service"))
241 continue;
242
243 q = add_dbus(path, de->d_name, type);
244 if (q < 0)
245 r = q;
246 }
247
248 return r;
249
250 fail:
251 log_error("Failed to read D-Bus services directory: %m");
252 return -errno;
253 }
254
255 static int link_busnames_target(const char *units) {
256 const char *f, *t;
257
258 f = strappenda(units, "/" SPECIAL_BUSNAMES_TARGET);
259 t = strappenda(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET);
260
261 mkdir_parents_label(t, 0755);
262 if (symlink(f, t) < 0) {
263 log_error("Failed to create symlink %s: %m", t);
264 return -errno;
265 }
266
267 return 0;
268 }
269
270 static int link_compatibility(const char *units) {
271 const char *f, *t;
272
273 f = strappenda(units, "/systemd-bus-proxyd.socket");
274 t = strappenda(arg_dest, "/" SPECIAL_DBUS_SOCKET);
275 mkdir_parents_label(t, 0755);
276 if (symlink(f, t) < 0) {
277 log_error("Failed to create symlink %s: %m", t);
278 return -errno;
279 }
280
281 f = strappenda(units, "/systemd-bus-proxyd.socket");
282 t = strappenda(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket");
283 mkdir_parents_label(t, 0755);
284 if (symlink(f, t) < 0) {
285 log_error("Failed to create symlink %s: %m", t);
286 return -errno;
287 }
288
289 t = strappenda(arg_dest, "/" SPECIAL_DBUS_SERVICE);
290 if (symlink("/dev/null", t) < 0) {
291 log_error("Failed to mask %s: %m", t);
292 return -errno;
293 }
294
295 return 0;
296 }
297
298 int main(int argc, char *argv[]) {
299 const char *path, *type, *units;
300 int r, q;
301
302 if (argc > 1 && argc != 4) {
303 log_error("This program takes three or no arguments.");
304 return EXIT_FAILURE;
305 }
306
307 if (argc > 1) {
308 arg_dest = argv[1];
309 arg_dest_late = argv[3];
310 }
311
312 log_set_target(LOG_TARGET_SAFE);
313 log_parse_environment();
314 log_open();
315
316 umask(0022);
317
318 if (access("/dev/kdbus/control", F_OK) < 0)
319 return 0;
320
321 r = cg_pid_get_owner_uid(0, NULL);
322 if (r >= 0) {
323 path = "/usr/share/dbus-1/services";
324 type = "session";
325 units = USER_DATA_UNIT_PATH;
326 } else if (r == -ENOENT) {
327 path = "/usr/share/dbus-1/system-services";
328 type = "system";
329 units = SYSTEM_DATA_UNIT_PATH;
330 } else {
331 log_error("Failed to determine whether we are running as user or system instance: %s", strerror(-r));
332 return r;
333 }
334
335 r = parse_dbus_fragments(path, type);
336
337 /* FIXME: One day this should just be pulled in statically from basic.target */
338 q = link_busnames_target(units);
339 if (q < 0)
340 r = q;
341
342 q = link_compatibility(units);
343 if (q < 0)
344 r = q;
345
346 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
347 }