chiark / gitweb /
bus: add generator that turns old dbus1 activation files into .busname + .service...
[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 (service) {
187                 if (!unit_name_is_valid(service, false)) {
188                         log_warning("Unit name %s is not valid, ignoring.", service);
189                         return 0;
190                 }
191                 if (!endswith(service, ".service")) {
192                         log_warning("Bus names can only activate services, ignoring %s.", p);
193                         return 0;
194                 }
195         } else {
196                 if (!exec) {
197                         log_warning("Neither service name nor binary path specified, ignoring %s.", p);
198                         return 0;
199                 }
200
201                 if (exec[0] != '/') {
202                         log_warning("Exec= in %s does not start with an absolute path, ignoring.", p);
203                         return 0;
204                 }
205         }
206
207         return create_dbus_files(p, name, service, exec, user, type);
208 }
209
210 static int parse_dbus_fragments(void) {
211         _cleanup_closedir_ DIR *d = NULL;
212         struct dirent *de;
213         const char *p, *type;
214         int r;
215
216         r = cg_pid_get_owner_uid(0, NULL);
217         if (r >= 0) {
218                 p = "/usr/share/dbus-1/services";
219                 type = "session";
220         } else if (r == -ENOENT) {
221                 p = "/usr/share/dbus-1/system-services";
222                 type = "systemd";
223         } else if (r < 0) {
224                 log_error("Failed to determine whether we are running as user or system instance: %s", strerror(-r));
225                 return r;
226         }
227
228         d = opendir(p);
229         if (!d) {
230                 if (errno == -ENOENT)
231                         return 0;
232
233                 log_error("Failed to enumerate D-Bus activated services: %m");
234                 return -errno;
235         }
236
237         r = 0;
238         FOREACH_DIRENT(de, d, goto fail) {
239                 int q;
240
241                 if (!endswith(de->d_name, ".service"))
242                         continue;
243
244                 q = add_dbus(p, de->d_name, type);
245                 if (q < 0)
246                         r = q;
247         }
248
249         return r;
250
251 fail:
252         log_error("Failed to read D-Bus services directory: %m");
253         return -errno;
254 }
255
256 int main(int argc, char *argv[]) {
257         int r;
258
259         if (argc > 1 && argc != 4) {
260                 log_error("This program takes three or no arguments.");
261                 return EXIT_FAILURE;
262         }
263
264         if (argc > 1)
265                 arg_dest = argv[3];
266
267         log_set_target(LOG_TARGET_SAFE);
268         log_parse_environment();
269         log_open();
270
271         umask(0022);
272
273         if (access("/dev/kdbus/control", F_OK) < 0)
274                 return 0;
275
276         r = parse_dbus_fragments();
277
278         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
279 }