chiark / gitweb /
31ec56e7ec1f00c41690d0d29c1539c3ad6d6006
[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 = "/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
47         if (!service) {
48                 _cleanup_free_ char *a = NULL;
49
50                 s = strjoin("dbus-", name, ".service", NULL);
51                 if (!s)
52                         return log_oom();
53
54                 a = strjoin(arg_dest, "/", s, NULL);
55                 if (!a)
56                         return log_oom();
57
58                 f = fopen(a, "wxe");
59                 if (!f) {
60                         log_error("Failed to create %s: %m", a);
61                         return -errno;
62                 }
63
64                 fprintf(f,
65                         "# Automatically generated by systemd-dbus1-generator\n\n"
66                         "[Unit]\n"
67                         "Source=%s\n"
68                         "Description=DBUS1: %s\n\n"
69                         "[Service]\n"
70                         "ExecStart=%s\n"
71                         "Type=dbus\n"
72                         "BusName=%s\n",
73                         path,
74                         name,
75                         exec,
76                         name);
77
78                 if (user)
79                         fprintf(f, "User=%s\n", user);
80
81
82                 if (type) {
83                         fprintf(f, "Environment=DBUS_STARTER_BUS_TYPE=%s\n", type);
84
85                         if (streq(type, "system"))
86                                 fprintf(f, "Environment=DBUS_STARTER_ADDRESS=kernel:/dev/kdbus/0-system\n");
87                         else if (streq(type, "session"))
88                                 fprintf(f, "Environment=DBUS_STARTER_ADDRESS=kernel:/dev/kdbus/%lu-user\n", (unsigned long) getuid());
89                 }
90
91                 fflush(f);
92                 if (ferror(f)) {
93                         log_error("Failed to write %s: %m", a);
94                         return -errno;
95                 }
96
97                 service = s;
98         }
99
100         b = strjoin(arg_dest, "/", name, ".busname", NULL);
101         if (!b)
102                 return log_oom();
103
104         f = fopen(b, "wxe");
105         if (!f) {
106                 log_error("Failed to create %s: %m", b);
107                 return -errno;
108         }
109
110         fprintf(f,
111                 "# Automatically generated by systemd-dbus1-generator\n\n"
112                 "[Unit]\n"
113                 "Source=%s\n"
114                 "Description=DBUS1: %s\n\n"
115                 "[BusName]\n"
116                 "Name=%s\n"
117                 "Service=%s\n",
118                 path,
119                 name,
120                 name,
121                 service);
122
123         fflush(f);
124         if (ferror(f)) {
125                 log_error("Failed to write %s: %m", b);
126                 return -errno;
127         }
128
129         lnk = strjoin(arg_dest, "/" SPECIAL_BUSNAMES_TARGET ".wants/", name, ".busname", NULL);
130         if (!lnk)
131                 return log_oom();
132
133         mkdir_parents_label(lnk, 0755);
134         if (symlink(b, lnk)) {
135                 log_error("Failed to create symlinks %s: %m", lnk);
136                 return -errno;
137         }
138
139         return 0;
140 }
141
142 static int add_dbus(const char *path, const char *fname, const char *type) {
143         _cleanup_free_ char *name = NULL, *exec = NULL, *user = NULL, *service = NULL;
144
145         ConfigTableItem table[] = {
146                 { "D-BUS Service", "Name", config_parse_string, 0, &name },
147                 { "D-BUS Service", "Exec", config_parse_string, 0, &exec },
148                 { "D-BUS Service", "User", config_parse_string, 0, &user },
149                 { "D-BUS Service", "SystemdService", config_parse_string, 0, &service },
150         };
151
152         _cleanup_fclose_ FILE *f = NULL;
153         _cleanup_free_ char *p = NULL;
154         int r;
155
156         assert(path);
157         assert(fname);
158
159         p = strjoin(path, "/", fname, NULL);
160         if (!p)
161                 return log_oom();
162
163         f = fopen(p, "re");
164         if (!f) {
165                 if (errno == -ENOENT)
166                         return 0;
167
168                 log_error("Failed to read %s: %m", p);
169                 return -errno;
170         }
171
172         r = config_parse(NULL, p, f, "D-BUS Service\0", config_item_table_lookup, table, true, false, NULL);
173         if (r < 0)
174                 return r;
175
176         if (!name) {
177                 log_warning("Activation file %s lacks name setting, ignoring.", p);
178                 return 0;
179         }
180
181         if (!service_name_is_valid(name)) {
182                 log_warning("Bus service name %s is not valid, ignoring.", name);
183                 return 0;
184         }
185
186         if (streq(name, "org.freedesktop.systemd1")) {
187                 log_debug("Skipping %s, identified as systemd.", p);
188                 return 0;
189         }
190
191         if (service) {
192                 if (!unit_name_is_valid(service, false)) {
193                         log_warning("Unit name %s is not valid, ignoring.", service);
194                         return 0;
195                 }
196                 if (!endswith(service, ".service")) {
197                         log_warning("Bus names can only activate services, ignoring %s.", p);
198                         return 0;
199                 }
200         } else {
201                 if (streq(exec, "/bin/false") || !exec) {
202                         log_warning("Neither service name nor binary path specified, ignoring %s.", p);
203                         return 0;
204                 }
205
206                 if (exec[0] != '/') {
207                         log_warning("Exec= in %s does not start with an absolute path, ignoring.", p);
208                         return 0;
209                 }
210         }
211
212         return create_dbus_files(p, name, service, exec, user, type);
213 }
214
215 static int parse_dbus_fragments(void) {
216         _cleanup_closedir_ DIR *d = NULL;
217         struct dirent *de;
218         const char *p, *type;
219         int r;
220
221         r = cg_pid_get_owner_uid(0, NULL);
222         if (r >= 0) {
223                 p = "/usr/share/dbus-1/services";
224                 type = "session";
225         } else if (r == -ENOENT) {
226                 p = "/usr/share/dbus-1/system-services";
227                 type = "system";
228         } else if (r < 0) {
229                 log_error("Failed to determine whether we are running as user or system instance: %s", strerror(-r));
230                 return r;
231         }
232
233         d = opendir(p);
234         if (!d) {
235                 if (errno == -ENOENT)
236                         return 0;
237
238                 log_error("Failed to enumerate D-Bus activated services: %m");
239                 return -errno;
240         }
241
242         r = 0;
243         FOREACH_DIRENT(de, d, goto fail) {
244                 int q;
245
246                 if (!endswith(de->d_name, ".service"))
247                         continue;
248
249                 q = add_dbus(p, de->d_name, type);
250                 if (q < 0)
251                         r = q;
252         }
253
254         return r;
255
256 fail:
257         log_error("Failed to read D-Bus services directory: %m");
258         return -errno;
259 }
260
261 int main(int argc, char *argv[]) {
262         int r;
263
264         if (argc > 1 && argc != 4) {
265                 log_error("This program takes three or no arguments.");
266                 return EXIT_FAILURE;
267         }
268
269         if (argc > 1)
270                 arg_dest = argv[3];
271
272         log_set_target(LOG_TARGET_SAFE);
273         log_parse_environment();
274         log_open();
275
276         umask(0022);
277
278         if (access("/dev/kdbus/control", F_OK) < 0)
279                 return 0;
280
281         r = parse_dbus_fragments();
282
283         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
284 }