]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/dbus1-generator/dbus1-generator.c
util-lib: introduce dirent-util.[ch] for directory entry calls
[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 "bus-internal.h"
23 #include "bus-util.h"
24 #include "cgroup-util.h"
25 #include "conf-parser.h"
26 #include "dirent-util.h"
27 #include "fd-util.h"
28 #include "fileio.h"
29 #include "mkdir.h"
30 #include "special.h"
31 #include "unit-name.h"
32 #include "util.h"
33
34 static const char *arg_dest_late = "/tmp", *arg_dest = "/tmp";
35
36 static int create_dbus_files(
37 const char *path,
38 const char *name,
39 const char *service,
40 const char *exec,
41 const char *user,
42 const char *type) {
43
44 _cleanup_free_ char *b = NULL, *s = NULL, *lnk = NULL;
45 _cleanup_fclose_ FILE *f = NULL;
46 int r;
47
48 assert(path);
49 assert(name);
50 assert(service || exec);
51
52 if (!service) {
53 _cleanup_free_ char *a = NULL;
54
55 s = strjoin("dbus-", name, ".service", NULL);
56 if (!s)
57 return log_oom();
58
59 a = strjoin(arg_dest_late, "/", s, NULL);
60 if (!a)
61 return log_oom();
62
63 f = fopen(a, "wxe");
64 if (!f)
65 return log_error_errno(errno, "Failed to create %s: %m", a);
66
67 fprintf(f,
68 "# Automatically generated by systemd-dbus1-generator\n\n"
69 "[Unit]\n"
70 "SourcePath=%s\n"
71 "Description=DBUS1: %s\n"
72 "Documentation=man:systemd-dbus1-generator(8)\n\n"
73 "[Service]\n"
74 "ExecStart=%s\n"
75 "Type=dbus\n"
76 "BusName=%s\n",
77 path,
78 name,
79 exec,
80 name);
81
82 if (user)
83 fprintf(f, "User=%s\n", user);
84
85
86 if (type) {
87 fprintf(f, "Environment=DBUS_STARTER_BUS_TYPE=%s\n", type);
88
89 if (streq(type, "system"))
90 fprintf(f, "Environment=DBUS_STARTER_ADDRESS=" DEFAULT_SYSTEM_BUS_ADDRESS "\n");
91 else if (streq(type, "session")) {
92 char *run;
93
94 run = getenv("XDG_RUNTIME_DIR");
95 if (!run) {
96 log_error("XDG_RUNTIME_DIR not set.");
97 return -EINVAL;
98 }
99
100 fprintf(f, "Environment=DBUS_STARTER_ADDRESS="KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT "\n",
101 getuid(), run);
102 }
103 }
104
105 r = fflush_and_check(f);
106 if (r < 0)
107 return log_error_errno(r, "Failed to write %s: %m", a);
108
109 f = safe_fclose(f);
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 return log_error_errno(errno, "Failed to create %s: %m", b);
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 r = fflush_and_check(f);
138 if (r < 0)
139 return log_error_errno(r, "Failed to write %s: %m", b);
140
141 lnk = strjoin(arg_dest_late, "/" SPECIAL_BUSNAMES_TARGET ".wants/", name, ".busname", NULL);
142 if (!lnk)
143 return log_oom();
144
145 mkdir_parents_label(lnk, 0755);
146 if (symlink(b, lnk))
147 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
148
149 return 0;
150 }
151
152 static int add_dbus(const char *path, const char *fname, const char *type) {
153 _cleanup_free_ char *name = NULL, *exec = NULL, *user = NULL, *service = NULL;
154
155 const ConfigTableItem table[] = {
156 { "D-BUS Service", "Name", config_parse_string, 0, &name },
157 { "D-BUS Service", "Exec", config_parse_string, 0, &exec },
158 { "D-BUS Service", "User", config_parse_string, 0, &user },
159 { "D-BUS Service", "SystemdService", config_parse_string, 0, &service },
160 { },
161 };
162
163 char *p;
164 int r;
165
166 assert(path);
167 assert(fname);
168
169 p = strjoina(path, "/", fname);
170 r = config_parse(NULL, p, NULL,
171 "D-BUS Service\0",
172 config_item_table_lookup, table,
173 true, false, true, 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, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) {
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_errno(errno, "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_errno(errno, "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 = strjoina(units, "/" SPECIAL_BUSNAMES_TARGET);
256 t = strjoina(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET);
257
258 mkdir_parents_label(t, 0755);
259 if (symlink(f, t) < 0)
260 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
261
262 return 0;
263 }
264
265 static int link_compatibility(const char *units) {
266 const char *f, *t;
267
268 f = strjoina(units, "/systemd-bus-proxyd.socket");
269 t = strjoina(arg_dest, "/" SPECIAL_DBUS_SOCKET);
270 mkdir_parents_label(t, 0755);
271 if (symlink(f, t) < 0)
272 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
273
274 f = strjoina(units, "/systemd-bus-proxyd.socket");
275 t = strjoina(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket");
276 mkdir_parents_label(t, 0755);
277 if (symlink(f, t) < 0)
278 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
279
280 t = strjoina(arg_dest, "/" SPECIAL_DBUS_SERVICE);
281 if (symlink("/dev/null", t) < 0)
282 return log_error_errno(errno, "Failed to mask %s: %m", t);
283
284 return 0;
285 }
286
287 int main(int argc, char *argv[]) {
288 const char *path, *type, *units;
289 int r, q;
290
291 if (argc > 1 && argc != 4) {
292 log_error("This program takes three or no arguments.");
293 return EXIT_FAILURE;
294 }
295
296 if (argc > 1) {
297 arg_dest = argv[1];
298 arg_dest_late = argv[3];
299 }
300
301 log_set_target(LOG_TARGET_SAFE);
302 log_parse_environment();
303 log_open();
304
305 umask(0022);
306
307 if (!is_kdbus_available())
308 return 0;
309
310 r = cg_pid_get_owner_uid(0, NULL);
311 if (r >= 0) {
312 path = "/usr/share/dbus-1/services";
313 type = "session";
314 units = USER_DATA_UNIT_PATH;
315 } else if (r == -ENXIO) {
316 path = "/usr/share/dbus-1/system-services";
317 type = "system";
318 units = SYSTEM_DATA_UNIT_PATH;
319 } else
320 return log_error_errno(r, "Failed to determine whether we are running as user or system instance: %m");
321
322 r = parse_dbus_fragments(path, type);
323
324 /* FIXME: One day this should just be pulled in statically from basic.target */
325 q = link_busnames_target(units);
326 if (q < 0)
327 r = q;
328
329 q = link_compatibility(units);
330 if (q < 0)
331 r = q;
332
333 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
334 }