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