chiark / gitweb /
machined: move logic for bind mounting into containers from machinectl to machined
[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
161         char *p;
162         int r;
163
164         assert(path);
165         assert(fname);
166
167         p = strjoina(path, "/", fname);
168         r = config_parse(NULL, p, NULL,
169                          "D-BUS Service\0",
170                          config_item_table_lookup, table,
171                          true, false, true, NULL);
172         if (r < 0)
173                 return r;
174
175         if (!name) {
176                 log_warning("Activation file %s lacks name setting, ignoring.", p);
177                 return 0;
178         }
179
180         if (!service_name_is_valid(name)) {
181                 log_warning("Bus service name %s is not valid, ignoring.", name);
182                 return 0;
183         }
184
185         if (streq(name, "org.freedesktop.systemd1")) {
186                 log_debug("Skipping %s, identified as systemd.", p);
187                 return 0;
188         }
189
190         if (service) {
191                 if (!unit_name_is_valid(service, TEMPLATE_INVALID)) {
192                         log_warning("Unit name %s is not valid, ignoring.", service);
193                         return 0;
194                 }
195                 if (!endswith(service, ".service")) {
196                         log_warning("Bus names can only activate services, ignoring %s.", p);
197                         return 0;
198                 }
199         } else {
200                 if (streq(exec, "/bin/false") || !exec) {
201                         log_warning("Neither service name nor binary path specified, ignoring %s.", p);
202                         return 0;
203                 }
204
205                 if (exec[0] != '/') {
206                         log_warning("Exec= in %s does not start with an absolute path, ignoring.", p);
207                         return 0;
208                 }
209         }
210
211         return create_dbus_files(p, name, service, exec, user, type);
212 }
213
214 static int parse_dbus_fragments(const char *path, const char *type) {
215         _cleanup_closedir_ DIR *d = NULL;
216         struct dirent *de;
217         int r;
218
219         assert(path);
220         assert(type);
221
222         d = opendir(path);
223         if (!d) {
224                 if (errno == -ENOENT)
225                         return 0;
226
227                 log_error_errno(errno, "Failed to enumerate D-Bus activated services: %m");
228                 return -errno;
229         }
230
231         r = 0;
232         FOREACH_DIRENT(de, d, goto fail) {
233                 int q;
234
235                 if (!endswith(de->d_name, ".service"))
236                         continue;
237
238                 q = add_dbus(path, de->d_name, type);
239                 if (q < 0)
240                         r = q;
241         }
242
243         return r;
244
245 fail:
246         log_error_errno(errno, "Failed to read D-Bus services directory: %m");
247         return -errno;
248 }
249
250 static int link_busnames_target(const char *units) {
251         const char *f, *t;
252
253         f = strjoina(units, "/" SPECIAL_BUSNAMES_TARGET);
254         t = strjoina(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET);
255
256         mkdir_parents_label(t, 0755);
257         if (symlink(f, t) < 0)
258                 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
259
260         return 0;
261 }
262
263 static int link_compatibility(const char *units) {
264         const char *f, *t;
265
266         f = strjoina(units, "/systemd-bus-proxyd.socket");
267         t = strjoina(arg_dest, "/" SPECIAL_DBUS_SOCKET);
268         mkdir_parents_label(t, 0755);
269         if (symlink(f, t) < 0)
270                 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
271
272         f = strjoina(units, "/systemd-bus-proxyd.socket");
273         t = strjoina(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket");
274         mkdir_parents_label(t, 0755);
275         if (symlink(f, t) < 0)
276                 return log_error_errno(errno, "Failed to create symlink %s: %m", t);
277
278         t = strjoina(arg_dest, "/" SPECIAL_DBUS_SERVICE);
279         if (symlink("/dev/null", t) < 0)
280                 return log_error_errno(errno, "Failed to mask %s: %m", t);
281
282         return 0;
283 }
284
285 int main(int argc, char *argv[]) {
286         const char *path, *type, *units;
287         int r, q;
288
289         if (argc > 1 && argc != 4) {
290                 log_error("This program takes three or no arguments.");
291                 return EXIT_FAILURE;
292         }
293
294         if (argc > 1) {
295                 arg_dest = argv[1];
296                 arg_dest_late = argv[3];
297         }
298
299         log_set_target(LOG_TARGET_SAFE);
300         log_parse_environment();
301         log_open();
302
303         umask(0022);
304
305         if (access("/sys/fs/kdbus/control", F_OK) < 0)
306                 return 0;
307
308         r = cg_pid_get_owner_uid(0, NULL);
309         if (r >= 0) {
310                 path = "/usr/share/dbus-1/services";
311                 type = "session";
312                 units = USER_DATA_UNIT_PATH;
313         } else if (r == -ENOENT) {
314                 path = "/usr/share/dbus-1/system-services";
315                 type = "system";
316                 units = SYSTEM_DATA_UNIT_PATH;
317         } else
318                 return log_error_errno(r, "Failed to determine whether we are running as user or system instance: %m");
319
320         r = parse_dbus_fragments(path, type);
321
322         /* FIXME: One day this should just be pulled in statically from basic.target */
323         q = link_busnames_target(units);
324         if (q < 0)
325                 r = q;
326
327         q = link_compatibility(units);
328         if (q < 0)
329                 r = q;
330
331         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
332 }