chiark / gitweb /
logind: add a debug message in case the session already exists
[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
44         assert(path);
45         assert(name);
46         assert(service || exec);
47
48         if (!service) {
49                 _cleanup_free_ char *a = NULL;
50
51                 s = strjoin("dbus-", name, ".service", NULL);
52                 if (!s)
53                         return log_oom();
54
55                 a = strjoin(arg_dest_late, "/", s, NULL);
56                 if (!a)
57                         return log_oom();
58
59                 f = fopen(a, "wxe");
60                 if (!f) {
61                         log_error("Failed to create %s: %m", a);
62                         return -errno;
63                 }
64
65                 fprintf(f,
66                         "# Automatically generated by systemd-dbus1-generator\n\n"
67                         "[Unit]\n"
68                         "SourcePath=%s\n"
69                         "Description=DBUS1: %s\n"
70                         "Documentation=man:systemd-dbus1-generator(8)\n\n"
71                         "[Service]\n"
72                         "ExecStart=%s\n"
73                         "Type=dbus\n"
74                         "BusName=%s\n",
75                         path,
76                         name,
77                         exec,
78                         name);
79
80                 if (user)
81                         fprintf(f, "User=%s\n", user);
82
83
84                 if (type) {
85                         fprintf(f, "Environment=DBUS_STARTER_BUS_TYPE=%s\n", type);
86
87                         if (streq(type, "system"))
88                                 fprintf(f, "Environment=DBUS_STARTER_ADDRESS=" DEFAULT_SYSTEM_BUS_PATH "\n");
89                         else if (streq(type, "session")) {
90                                 char *run;
91
92                                 run = getenv("XDG_RUNTIME_DIR");
93                                 if (!run) {
94                                         log_error("XDG_RUNTIME_DIR not set.");
95                                         return -EINVAL;
96                                 }
97
98                                 fprintf(f, "Environment=DBUS_STARTER_ADDRESS="KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT "\n",
99                                         (unsigned long) getuid(), run);
100                         }
101                 }
102
103                 fflush(f);
104                 if (ferror(f)) {
105                         log_error("Failed to write %s: %m", a);
106                         return -errno;
107                 }
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                 log_error("Failed to create %s: %m", b);
119                 return -errno;
120         }
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         fflush(f);
138         if (ferror(f)) {
139                 log_error("Failed to write %s: %m", b);
140                 return -errno;
141         }
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("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         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         _cleanup_fclose_ FILE *f = NULL;
167         _cleanup_free_ char *p = NULL;
168         int r;
169
170         assert(path);
171         assert(fname);
172
173         p = strjoin(path, "/", fname, NULL);
174         if (!p)
175                 return log_oom();
176
177         f = fopen(p, "re");
178         if (!f) {
179                 if (errno == -ENOENT)
180                         return 0;
181
182                 log_error("Failed to read %s: %m", p);
183                 return -errno;
184         }
185
186         r = config_parse(NULL, p, f, "D-BUS Service\0", config_item_table_lookup, table, true, false, NULL);
187         if (r < 0)
188                 return r;
189
190         if (!name) {
191                 log_warning("Activation file %s lacks name setting, ignoring.", p);
192                 return 0;
193         }
194
195         if (!service_name_is_valid(name)) {
196                 log_warning("Bus service name %s is not valid, ignoring.", name);
197                 return 0;
198         }
199
200         if (streq(name, "org.freedesktop.systemd1")) {
201                 log_debug("Skipping %s, identified as systemd.", p);
202                 return 0;
203         }
204
205         if (service) {
206                 if (!unit_name_is_valid(service, TEMPLATE_INVALID)) {
207                         log_warning("Unit name %s is not valid, ignoring.", service);
208                         return 0;
209                 }
210                 if (!endswith(service, ".service")) {
211                         log_warning("Bus names can only activate services, ignoring %s.", p);
212                         return 0;
213                 }
214         } else {
215                 if (streq(exec, "/bin/false") || !exec) {
216                         log_warning("Neither service name nor binary path specified, ignoring %s.", p);
217                         return 0;
218                 }
219
220                 if (exec[0] != '/') {
221                         log_warning("Exec= in %s does not start with an absolute path, ignoring.", p);
222                         return 0;
223                 }
224         }
225
226         return create_dbus_files(p, name, service, exec, user, type);
227 }
228
229 static int parse_dbus_fragments(const char *path, const char *type) {
230         _cleanup_closedir_ DIR *d = NULL;
231         struct dirent *de;
232         int r;
233
234         assert(path);
235         assert(type);
236
237         d = opendir(path);
238         if (!d) {
239                 if (errno == -ENOENT)
240                         return 0;
241
242                 log_error("Failed to enumerate D-Bus activated services: %m");
243                 return -errno;
244         }
245
246         r = 0;
247         FOREACH_DIRENT(de, d, goto fail) {
248                 int q;
249
250                 if (!endswith(de->d_name, ".service"))
251                         continue;
252
253                 q = add_dbus(path, de->d_name, type);
254                 if (q < 0)
255                         r = q;
256         }
257
258         return r;
259
260 fail:
261         log_error("Failed to read D-Bus services directory: %m");
262         return -errno;
263 }
264
265 static int link_busnames_target(const char *units) {
266         const char *f, *t;
267
268         f = strappenda(units, "/" SPECIAL_BUSNAMES_TARGET);
269         t = strappenda(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET);
270
271         mkdir_parents_label(t, 0755);
272         if (symlink(f, t) < 0) {
273                 log_error("Failed to create symlink %s: %m", t);
274                 return -errno;
275         }
276
277         return 0;
278 }
279
280 static int link_compatibility(const char *units) {
281         const char *f, *t;
282
283         f = strappenda(units, "/systemd-bus-proxyd.socket");
284         t = strappenda(arg_dest, "/" SPECIAL_DBUS_SOCKET);
285         mkdir_parents_label(t, 0755);
286         if (symlink(f, t) < 0) {
287                 log_error("Failed to create symlink %s: %m", t);
288                 return -errno;
289         }
290
291         f = strappenda(units, "/systemd-bus-proxyd.socket");
292         t = strappenda(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket");
293         mkdir_parents_label(t, 0755);
294         if (symlink(f, t) < 0) {
295                 log_error("Failed to create symlink %s: %m", t);
296                 return -errno;
297         }
298
299         t = strappenda(arg_dest, "/" SPECIAL_DBUS_SERVICE);
300         if (symlink("/dev/null", t) < 0) {
301                 log_error("Failed to mask %s: %m", t);
302                 return -errno;
303         }
304
305         return 0;
306 }
307
308 int main(int argc, char *argv[]) {
309         const char *path, *type, *units;
310         int r, q;
311
312         if (argc > 1 && argc != 4) {
313                 log_error("This program takes three or no arguments.");
314                 return EXIT_FAILURE;
315         }
316
317         if (argc > 1) {
318                 arg_dest = argv[1];
319                 arg_dest_late = argv[3];
320         }
321
322         log_set_target(LOG_TARGET_SAFE);
323         log_parse_environment();
324         log_open();
325
326         umask(0022);
327
328         if (access("/dev/kdbus/control", F_OK) < 0)
329                 return 0;
330
331         r = cg_pid_get_owner_uid(0, NULL);
332         if (r >= 0) {
333                 path = "/usr/share/dbus-1/services";
334                 type = "session";
335                 units = USER_DATA_UNIT_PATH;
336         } else if (r == -ENOENT) {
337                 path = "/usr/share/dbus-1/system-services";
338                 type = "system";
339                 units = SYSTEM_DATA_UNIT_PATH;
340         } else {
341                 log_error("Failed to determine whether we are running as user or system instance: %s", strerror(-r));
342                 return r;
343         }
344
345         r = parse_dbus_fragments(path, type);
346
347         /* FIXME: One day this should just be pulled in statically from basic.target */
348         q = link_busnames_target(units);
349         if (q < 0)
350                 r = q;
351
352         q = link_compatibility(units);
353         if (q < 0)
354                 r = q;
355
356         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
357 }