]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/dbus1-generator/dbus1-generator.c
treewide: use log_*_errno whenever %m is in the format string
[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 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_errno(errno, "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_ADDRESS "\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_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT "\n",
100 getuid(), run);
101 }
102 }
103
104 r = fflush_and_check(f);
105 if (r < 0)
106 return log_error_errno(r, "Failed to write %s: %m", a);
107
108 fclose(f);
109 f = NULL;
110
111 service = s;
112 }
113
114 b = strjoin(arg_dest_late, "/", name, ".busname", NULL);
115 if (!b)
116 return log_oom();
117
118 f = fopen(b, "wxe");
119 if (!f) {
120 log_error_errno(errno, "Failed to create %s: %m", b);
121 return -errno;
122 }
123
124 fprintf(f,
125 "# Automatically generated by systemd-dbus1-generator\n\n"
126 "[Unit]\n"
127 "SourcePath=%s\n"
128 "Description=DBUS1: %s\n"
129 "Documentation=man:systemd-dbus1-generator(8)\n\n"
130 "[BusName]\n"
131 "Name=%s\n"
132 "Service=%s\n"
133 "AllowWorld=talk\n",
134 path,
135 name,
136 name,
137 service);
138
139 r = fflush_and_check(f);
140 if (r < 0)
141 return log_error_errno(r, "Failed to write %s: %m", b);
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_errno(errno, "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_errno(errno, "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_errno(errno, "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_errno(errno, "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_errno(errno, "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_errno(errno, "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_errno(errno, "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("/sys/fs/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 return log_error_errno(r, "Failed to determine whether we are running as user or system instance: %m");
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 }