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