]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/dbus1-generator/dbus1-generator.c
util-lib: split out allocation calls into alloc-util.[ch]
[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 "alloc-util.h"
23 #include "bus-internal.h"
24 #include "bus-util.h"
25 #include "cgroup-util.h"
26 #include "conf-parser.h"
27 #include "dirent-util.h"
28 #include "fd-util.h"
29 #include "fileio.h"
30 #include "mkdir.h"
31 #include "special.h"
32 #include "unit-name.h"
33 #include "util.h"
34
35 static const char *arg_dest_late = "/tmp", *arg_dest = "/tmp";
36
37 static int create_dbus_files(
38 const char *path,
39 const char *name,
40 const char *service,
41 const char *exec,
42 const char *user,
43 const char *type) {
44
45 _cleanup_free_ char *b = NULL, *s = NULL, *lnk = NULL;
46 _cleanup_fclose_ FILE *f = NULL;
47 int r;
48
49 assert(path);
50 assert(name);
51 assert(service || exec);
52
53 if (!service) {
54 _cleanup_free_ char *a = NULL;
55
56 s = strjoin("dbus-", name, ".service", NULL);
57 if (!s)
58 return log_oom();
59
60 a = strjoin(arg_dest_late, "/", s, NULL);
61 if (!a)
62 return log_oom();
63
64 f = fopen(a, "wxe");
65 if (!f)
66 return log_error_errno(errno, "Failed to create %s: %m", a);
67
68 fprintf(f,
69 "# Automatically generated by systemd-dbus1-generator\n\n"
70 "[Unit]\n"
71 "SourcePath=%s\n"
72 "Description=DBUS1: %s\n"
73 "Documentation=man:systemd-dbus1-generator(8)\n\n"
74 "[Service]\n"
75 "ExecStart=%s\n"
76 "Type=dbus\n"
77 "BusName=%s\n",
78 path,
79 name,
80 exec,
81 name);
82
83 if (user)
84 fprintf(f, "User=%s\n", user);
85
86
87 if (type) {
88 fprintf(f, "Environment=DBUS_STARTER_BUS_TYPE=%s\n", type);
89
90 if (streq(type, "system"))
91 fprintf(f, "Environment=DBUS_STARTER_ADDRESS=" DEFAULT_SYSTEM_BUS_ADDRESS "\n");
92 else if (streq(type, "session")) {
93 char *run;
94
95 run = getenv("XDG_RUNTIME_DIR");
96 if (!run) {
97 log_error("XDG_RUNTIME_DIR not set.");
98 return -EINVAL;
99 }
100
101 fprintf(f, "Environment=DBUS_STARTER_ADDRESS="KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT "\n",
102 getuid(), run);
103 }
104 }
105
106 r = fflush_and_check(f);
107 if (r < 0)
108 return log_error_errno(r, "Failed to write %s: %m", a);
109
110 f = safe_fclose(f);
111
112 service = s;
113 }
114
115 b = strjoin(arg_dest_late, "/", name, ".busname", NULL);
116 if (!b)
117 return log_oom();
118
119 f = fopen(b, "wxe");
120 if (!f)
121 return log_error_errno(errno, "Failed to create %s: %m", b);
122
123 fprintf(f,
124 "# Automatically generated by systemd-dbus1-generator\n\n"
125 "[Unit]\n"
126 "SourcePath=%s\n"
127 "Description=DBUS1: %s\n"
128 "Documentation=man:systemd-dbus1-generator(8)\n\n"
129 "[BusName]\n"
130 "Name=%s\n"
131 "Service=%s\n"
132 "AllowWorld=talk\n",
133 path,
134 name,
135 name,
136 service);
137
138 r = fflush_and_check(f);
139 if (r < 0)
140 return log_error_errno(r, "Failed to write %s: %m", b);
141
142 lnk = strjoin(arg_dest_late, "/" SPECIAL_BUSNAMES_TARGET ".wants/", name, ".busname", NULL);
143 if (!lnk)
144 return log_oom();
145
146 mkdir_parents_label(lnk, 0755);
147 if (symlink(b, lnk))
148 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
149
150 return 0;
151 }
152
153 static int add_dbus(const char *path, const char *fname, const char *type) {
154 _cleanup_free_ char *name = NULL, *exec = NULL, *user = NULL, *service = NULL;
155
156 const ConfigTableItem table[] = {
157 { "D-BUS Service", "Name", config_parse_string, 0, &name },
158 { "D-BUS Service", "Exec", config_parse_string, 0, &exec },
159 { "D-BUS Service", "User", config_parse_string, 0, &user },
160 { "D-BUS Service", "SystemdService", config_parse_string, 0, &service },
161 { },
162 };
163
164 char *p;
165 int r;
166
167 assert(path);
168 assert(fname);
169
170 p = strjoina(path, "/", fname);
171 r = config_parse(NULL, p, NULL,
172 "D-BUS Service\0",
173 config_item_table_lookup, table,
174 true, false, true, NULL);
175 if (r < 0)
176 return r;
177
178 if (!name) {
179 log_warning("Activation file %s lacks name setting, ignoring.", p);
180 return 0;
181 }
182
183 if (!service_name_is_valid(name)) {
184 log_warning("Bus service name %s is not valid, ignoring.", name);
185 return 0;
186 }
187
188 if (streq(name, "org.freedesktop.systemd1")) {
189 log_debug("Skipping %s, identified as systemd.", p);
190 return 0;
191 }
192
193 if (service) {
194 if (!unit_name_is_valid(service, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) {
195 log_warning("Unit name %s is not valid, ignoring.", service);
196 return 0;
197 }
198 if (!endswith(service, ".service")) {
199 log_warning("Bus names can only activate services, ignoring %s.", p);
200 return 0;
201 }
202 } else {
203 if (streq(exec, "/bin/false") || !exec) {
204 log_warning("Neither service name nor binary path specified, ignoring %s.", p);
205 return 0;
206 }
207
208 if (exec[0] != '/') {
209 log_warning("Exec= in %s does not start with an absolute path, ignoring.", p);
210 return 0;
211 }
212 }
213
214 return create_dbus_files(p, name, service, exec, user, type);
215 }
216
217 static int parse_dbus_fragments(const char *path, const char *type) {
218 _cleanup_closedir_ DIR *d = NULL;
219 struct dirent *de;
220 int r;
221
222 assert(path);
223 assert(type);
224
225 d = opendir(path);
226 if (!d) {
227 if (errno == -ENOENT)
228 return 0;
229
230 log_error_errno(errno, "Failed to enumerate D-Bus activated services: %m");
231 return -errno;
232 }
233
234 r = 0;
235 FOREACH_DIRENT(de, d, goto fail) {
236 int q;
237
238 if (!endswith(de->d_name, ".service"))
239 continue;
240
241 q = add_dbus(path, de->d_name, type);
242 if (q < 0)
243 r = q;
244 }
245
246 return r;
247
248 fail:
249 log_error_errno(errno, "Failed to read D-Bus services directory: %m");
250 return -errno;
251 }
252
253 static int link_busnames_target(const char *units) {
254 const char *f, *t;
255
256 f = strjoina(units, "/" SPECIAL_BUSNAMES_TARGET);
257 t = strjoina(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET);
258
259 mkdir_parents_label(t, 0755);
260 if (symlink(f, t) < 0)
261 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
262
263 return 0;
264 }
265
266 static int link_compatibility(const char *units) {
267 const char *f, *t;
268
269 f = strjoina(units, "/systemd-bus-proxyd.socket");
270 t = strjoina(arg_dest, "/" SPECIAL_DBUS_SOCKET);
271 mkdir_parents_label(t, 0755);
272 if (symlink(f, t) < 0)
273 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
274
275 f = strjoina(units, "/systemd-bus-proxyd.socket");
276 t = strjoina(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket");
277 mkdir_parents_label(t, 0755);
278 if (symlink(f, t) < 0)
279 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
280
281 t = strjoina(arg_dest, "/" SPECIAL_DBUS_SERVICE);
282 if (symlink("/dev/null", t) < 0)
283 return log_error_errno(errno, "Failed to mask %s: %m", t);
284
285 return 0;
286 }
287
288 int main(int argc, char *argv[]) {
289 const char *path, *type, *units;
290 int r, q;
291
292 if (argc > 1 && argc != 4) {
293 log_error("This program takes three or no arguments.");
294 return EXIT_FAILURE;
295 }
296
297 if (argc > 1) {
298 arg_dest = argv[1];
299 arg_dest_late = argv[3];
300 }
301
302 log_set_target(LOG_TARGET_SAFE);
303 log_parse_environment();
304 log_open();
305
306 umask(0022);
307
308 if (!is_kdbus_available())
309 return 0;
310
311 r = cg_pid_get_owner_uid(0, NULL);
312 if (r >= 0) {
313 path = "/usr/share/dbus-1/services";
314 type = "session";
315 units = USER_DATA_UNIT_PATH;
316 } else if (r == -ENXIO) {
317 path = "/usr/share/dbus-1/system-services";
318 type = "system";
319 units = SYSTEM_DATA_UNIT_PATH;
320 } else
321 return log_error_errno(r, "Failed to determine whether we are running as user or system instance: %m");
322
323 r = parse_dbus_fragments(path, type);
324
325 /* FIXME: One day this should just be pulled in statically from basic.target */
326 q = link_busnames_target(units);
327 if (q < 0)
328 r = q;
329
330 q = link_compatibility(units);
331 if (q < 0)
332 r = q;
333
334 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
335 }