Merge lp:~jamesodhunt/upstart/upstart-dbus-bridge into lp:upstart
- upstart-dbus-bridge
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1486 |
Proposed branch: | lp:~jamesodhunt/upstart/upstart-dbus-bridge |
Merge into: | lp:upstart |
Diff against target: |
887 lines (+829/-3) 6 files modified
ChangeLog (+16/-0) extra/Makefile.am (+19/-3) extra/conf/upstart-dbus-bridge.conf (+17/-0) extra/man/dbus-event.7 (+53/-0) extra/man/upstart-dbus-bridge.8 (+84/-0) extra/upstart-dbus-bridge.c (+640/-0) |
To merge this branch: | bzr merge lp:~jamesodhunt/upstart/upstart-dbus-bridge |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Dimitri John Ledkov | Approve | ||
Review via email: mp+161772@code.launchpad.net |
Commit message
Description of the change
D-Bus bridge that can connect to either the session bus or system bus to monitor D-Bus signal events and forward them on as Upstart events. Emits the dbus event.
To run at the system level:
$ sudo upstart-dbus-bridge --system
To run at the Session Init level:
$ upstart-dbus-bridge --session --user
By default, the bridge will not emit any events unless jobs are interested in the dbus event. However, this behaviour can be changed by specifying '--always':
$ upstart-dbus-bridge --session --user --always
- 1479. By James Hunt
-
* extra/upstart-
dbus-bridge. c:upstart_ job_added( ): Only display debug
message if job cares about DBUS_EVENT.
Ted Gould (ted) wrote : | # |
Ted Gould (ted) wrote : | # |
Okay, next question :-)
I don't see how a job can get the parameter of a signal. So for instance if my service emits a signal "NewFoo" and then has an argument of "/foo/foo2342" how can I get that argument in my job?
Stéphane Graber (stgraber) wrote : | # |
On Thu, Jun 20, 2013 at 08:54:23PM -0000, Ted Gould wrote:
> Looking through this I don't see a way to specify which bus you want the event to come from. So will there be two dbus bridges in the user session? So I could do:
>
> start on dbus SENDER=foo
>
> or
>
> start on dbus-system SENDER=foo
>
> Not quite sure how that'll work.
> --
> https:/
> Your team Upstart Developers is subscribed to branch lp:upstart.
I'd expect one instance to run at the system level for the system bus
and another to run in the user session for the session bus.
So in practice, you'd have:
start on :sys:dbus SENDER=foo
For the system bus and:
start on dbus SENDER=foo
For the session bus.
--
Stéphane Graber
Ubuntu developer
http://
Steve Langasek (vorlon) wrote : | # |
On Thu, Jun 20, 2013 at 08:54:23PM -0000, Ted Gould wrote:
> Looking through this I don't see a way to specify which bus you want the
> event to come from. So will there be two dbus bridges in the user
> session? So I could do:
> start on dbus SENDER=foo
> or
> start on dbus-system SENDER=foo
> Not quite sure how that'll work.
An event emitted by the system dbus bridge will be seen by user upstart jobs
with the :sys: prefix. Presumably we would just want a single bridge
running for the system bus, not one bridge per user for the system bus.
In that case, you would have:
start on dbus SENDER=foo
for the session bus, or
start on :sys:dbus SENDER=foo
for the system bus.
Though, I don't know how that squares performance-wise with --always, which
would presumably be needed at the system level since we don't have a way for
user init to subscribe to particular dbus events on the system bridge (and
shouldn't, for security reasons).
Ted Gould (ted) wrote : | # |
So, besides the security implications, as long as there's a way to do it, I'm happy :-)
Ted Gould (ted) wrote : | # |
Just to clarify a bit, I think this should probably land in the state that it is currently in, along with a conf file to add a system bus event bridge in the user session.
The reason that I think the system event bridge needs to be in the user session is so that the bus is connected to as the user, so the usual protections there (AppArmor for instance) will be able to monitor that connection. I don't think that using the system dbus event bridge is a good idea, because it could result in these mechanisms being subverted. That doesn't mean I think it couldn't be fixed, but I don't think it should block the feature landing because the work around of having two event bridges per session will work and isn't that expensive (the event bridge is small).
In the next version of the dbus event bridge I think that it needs to have some way for the job to get the parameters of the signal. Both for the start criteria and to get them in the job. I'd recommend doing it similar to the DBus filter syntax where instead of parameters environment variables are set to ARG0, ARG2, etc. Also, limiting it to 32 like dbus filter rules.
Dimitri John Ledkov (xnox) : | # |
Steve Langasek (vorlon) wrote : | # |
On Tue, Jun 25, 2013 at 08:22:28PM -0000, Ted Gould wrote:
> The reason that I think the system event bridge needs to be in the user
> session is so that the bus is connected to as the user, so the usual
> protections there (AppArmor for instance) will be able to monitor that
> connection. I don't think that using the system dbus event bridge is a
> good idea, because it could result in these mechanisms being subverted.
> That doesn't mean I think it couldn't be fixed, but I don't think it
> should block the feature landing because the work around of having two
> event bridges per session will work and isn't that expensive (the event
> bridge is small).
Note that, *if* we have a dbus bridge running at the system level, and there
are system jobs configured to want certain dbus events, these dbus events
will by default leak across the other bridge into the user session as
:sys:dbus events. So if visibility of these events is truly a concern, we
probably need to discuss with the security team how to make this happen.
Preview Diff
1 | === modified file 'ChangeLog' |
2 | --- ChangeLog 2013-04-30 23:26:25 +0000 |
3 | +++ ChangeLog 2013-05-01 05:49:25 +0000 |
4 | @@ -1,3 +1,19 @@ |
5 | +2013-04-30 James Hunt <james.hunt@ubuntu.com> |
6 | + |
7 | + * extra/Makefile.am: Add man pages and conf file. |
8 | + * extra/upstart-dbus-bridge.c: Only emit events if any jobs care |
9 | + about them. |
10 | + * extra/conf/upstart-dbus-bridge.conf: New configuration file. |
11 | + * extra/man/dbus-event.7: New man page. |
12 | + * extra/man/upstart-dbus-bridge.8: New man page. |
13 | + |
14 | +2013-04-25 James Hunt <james.hunt@ubuntu.com> |
15 | + |
16 | + * extra/upstart-dbus-bridge.c: |
17 | + - Comments and formatting. |
18 | + - main(): Default to an appropriate bus. |
19 | + - signal_filter(): Display signal details when run with --debug. |
20 | + |
21 | 2013-04-22 James Hunt <james.hunt@ubuntu.com> |
22 | |
23 | * Typo and doc changes. |
24 | |
25 | === modified file 'extra/Makefile.am' |
26 | --- extra/Makefile.am 2013-03-11 20:02:18 +0000 |
27 | +++ extra/Makefile.am 2013-05-01 05:49:25 +0000 |
28 | @@ -19,19 +19,24 @@ |
29 | sbin_PROGRAMS = \ |
30 | upstart-socket-bridge \ |
31 | upstart-event-bridge \ |
32 | - upstart-file-bridge |
33 | + upstart-file-bridge \ |
34 | + upstart-dbus-bridge |
35 | |
36 | dist_init_DATA = \ |
37 | conf/upstart-socket-bridge.conf \ |
38 | conf/upstart-event-bridge.conf \ |
39 | - conf/upstart-file-bridge.conf |
40 | + conf/upstart-file-bridge.conf \ |
41 | + conf/upstart-dbus-bridge.conf |
42 | |
43 | +# FIXME: add man/upstart-dbus-bridge.8 |
44 | dist_man_MANS = \ |
45 | man/upstart-socket-bridge.8 \ |
46 | man/upstart-event-bridge.8 \ |
47 | man/upstart-file-bridge.8 \ |
48 | + man/upstart-dbus-bridge.8 \ |
49 | man/socket-event.7 \ |
50 | - man/file-event.7 |
51 | + man/file-event.7 \ |
52 | + man/dbus-event.7 |
53 | |
54 | upstart_socket_bridge_SOURCES = \ |
55 | upstart-socket-bridge.c |
56 | @@ -66,6 +71,17 @@ |
57 | $(NIH_DBUS_LIBS) \ |
58 | $(DBUS_LIBS) |
59 | |
60 | +upstart_dbus_bridge_SOURCES = \ |
61 | + upstart-dbus-bridge.c |
62 | +nodist_upstart_dbus_bridge_SOURCES = \ |
63 | + $(com_ubuntu_Upstart_OUTPUTS) \ |
64 | + $(com_ubuntu_Upstart_Job_OUTPUTS) |
65 | +upstart_dbus_bridge_LDADD = \ |
66 | + $(LTLIBINTL) \ |
67 | + $(NIH_LIBS) \ |
68 | + $(NIH_DBUS_LIBS) \ |
69 | + $(DBUS_LIBS) |
70 | + |
71 | if HAVE_UDEV |
72 | dist_init_DATA += \ |
73 | conf/upstart-udev-bridge.conf |
74 | |
75 | === added file 'extra/conf/upstart-dbus-bridge.conf' |
76 | --- extra/conf/upstart-dbus-bridge.conf 1970-01-01 00:00:00 +0000 |
77 | +++ extra/conf/upstart-dbus-bridge.conf 2013-05-01 05:49:25 +0000 |
78 | @@ -0,0 +1,17 @@ |
79 | +# upstart-dbus-bridge - Bridge D-Bus signal events into upstart |
80 | +# |
81 | +# This helper daemon receives D-Bus signal events and |
82 | +# emits equivalent Upstart events. |
83 | + |
84 | +description "Bridge D-Bus signal events into upstart" |
85 | + |
86 | +emits dbus |
87 | + |
88 | +start on startup |
89 | + |
90 | +stop on runlevel [!2345] |
91 | + |
92 | +expect daemon |
93 | +respawn |
94 | + |
95 | +exec upstart-dbus-bridge --daemon --system |
96 | |
97 | === added file 'extra/man/dbus-event.7' |
98 | --- extra/man/dbus-event.7 1970-01-01 00:00:00 +0000 |
99 | +++ extra/man/dbus-event.7 2013-05-01 05:49:25 +0000 |
100 | @@ -0,0 +1,53 @@ |
101 | +.TH dbus\-event 7 2013-04-25 upstart |
102 | +.\" |
103 | +.SH NAME |
104 | +dbus \- event signalling that a dbus signal has been emitted |
105 | +.\" |
106 | +.SH SYNOPSIS |
107 | +.B dbus |
108 | +.BI SIGNAL\fR= SIGNAL |
109 | +.BI INTERFACE\fR= INTERFACE |
110 | +.BI PATH\fR= PATH |
111 | +.BI SENDER\fR= SENDER |
112 | +.BI DESTINATION\fR= DESTINATION |
113 | +.\" |
114 | +.SH DESCRIPTION |
115 | + |
116 | +The |
117 | +.B dbus |
118 | +event is generated by the |
119 | +.BR upstart\-dbus\-bridge (8) |
120 | +daemon when a D-Bus signal is emitted whose details match the |
121 | +.I dbus |
122 | +event condition and environment specified in a job's |
123 | +.B start on |
124 | +or |
125 | +.B stop on |
126 | +stanza is modified. |
127 | + |
128 | +.\" |
129 | +.SH EXAMPLES |
130 | +.\" |
131 | +.IP "start on dbus SIGNAL=NameAcquired INTERFACE=org.freedesktop.DBus PATH=/org/freedesktop/DBus SENDER=org.freedesktop.DBus" |
132 | +Start job when a particular |
133 | +.I NameAcquired |
134 | +D-Bus signal is received. |
135 | +.\" |
136 | +.SH AUTHOR |
137 | +Written by James Hunt |
138 | +.RB < james.hunt@canonical.com > |
139 | +.\" |
140 | +.SH BUGS |
141 | +Report bugs at |
142 | +.RB < https://launchpad.net/upstart/+bugs > |
143 | +.\" |
144 | +.SH COPYRIGHT |
145 | +Copyright \(co 2013 Canonical Ltd. |
146 | +.PP |
147 | +This is free software; see the source for copying conditions. There is NO |
148 | +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
149 | +.\" |
150 | +.SH SEE ALSO |
151 | +.BR init (5) |
152 | +.BR init (8) |
153 | +.BR upstart\-dbus\-bridge (8) |
154 | |
155 | === added file 'extra/man/upstart-dbus-bridge.8' |
156 | --- extra/man/upstart-dbus-bridge.8 1970-01-01 00:00:00 +0000 |
157 | +++ extra/man/upstart-dbus-bridge.8 2013-05-01 05:49:25 +0000 |
158 | @@ -0,0 +1,84 @@ |
159 | +.TH upstart\-dbus\-bridge 8 2013-04-25 upstart |
160 | +.\" |
161 | +.SH NAME |
162 | +upstart\-dbus\-bridge \- Bridge between Upstart and D-Bus |
163 | +.\" |
164 | +.SH SYNOPSIS |
165 | +.B upstart\-dbus\-bridge |
166 | +.RI [ OPTIONS ]... |
167 | +.\" |
168 | +.SH DESCRIPTION |
169 | +.B upstart\-dbus\-bridge |
170 | +receives information about D-Bus signals |
171 | +and creates |
172 | +.BR init (8) |
173 | +events for them. |
174 | + |
175 | +With no options, monitors signals on the D-Bus system bus and emits |
176 | +an Upstart event called |
177 | +.I dbus |
178 | +via a D-Bus system bus connection to Upstart. |
179 | + |
180 | +When run with \fB\-\-user\fP, monitors signals on the users D-Bus session bus |
181 | +and emits Upstart events via the private D-Bus connection to the users Session Init. |
182 | + |
183 | +See \fBdbus\-daemon\fP(1) and for further details. |
184 | + |
185 | +.\" |
186 | +.SH OPTIONS |
187 | +.\" |
188 | +.TP |
189 | +.B \-\-always |
190 | +Always emit events on receipt of D-Bus signal regardless of whether jobs |
191 | +care about them. |
192 | +.TP |
193 | +.B \-\-daemon |
194 | +Detach and run in the background. |
195 | +.\" |
196 | +.TP |
197 | +.B \-\-debug |
198 | +Enable debugging output. |
199 | +.\" |
200 | +.TP |
201 | +.B \-\-help |
202 | +Show brief usage summary. |
203 | +.\" |
204 | +.TP |
205 | +.B \-\-session |
206 | +Monitor signals on the D-Bus session bus. |
207 | +.\" |
208 | +.TP |
209 | +.B \-\-system |
210 | +Monitor signals on the D-Bus system bus. |
211 | +.\" |
212 | +.TP |
213 | +.B \-\-user |
214 | +User-session mode: connect to Upstart via the user session rather than |
215 | +over the D\-Bus system bus. |
216 | +.\" |
217 | +.TP |
218 | +.B \-\-verbose |
219 | +Enable verbose output. |
220 | +.\" |
221 | +.SH RESTRICTIONS |
222 | +D-Bus signals emitted by Upstart itself are ignored. |
223 | + |
224 | +.\" |
225 | +.SH AUTHOR |
226 | +Written by James Hunt |
227 | +.RB < james.hunt@canonical.com > |
228 | +.\" |
229 | +.SH BUGS |
230 | +Report bugs at |
231 | +.RB < https://launchpad.net/ubuntu/+source/upstart/+bugs > |
232 | +.\" |
233 | +.SH COPYRIGHT |
234 | +Copyright \(co 2013 Canonical Ltd. |
235 | +.PP |
236 | +This is free software; see the source for copying conditions. There is NO |
237 | +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
238 | +.SH SEE ALSO |
239 | +.BR dbus\-daemon (1) |
240 | +.BR dbus\-event (7) |
241 | +.BR init (5) |
242 | +.BR init (8) |
243 | |
244 | === added file 'extra/upstart-dbus-bridge.c' |
245 | --- extra/upstart-dbus-bridge.c 1970-01-01 00:00:00 +0000 |
246 | +++ extra/upstart-dbus-bridge.c 2013-05-01 05:49:25 +0000 |
247 | @@ -0,0 +1,640 @@ |
248 | +/* upstart |
249 | + * |
250 | + * Copyright © 2013 Canonical Ltd. |
251 | + * Author: James Hunt <james.hunt@ubuntu.com> |
252 | + * |
253 | + * This program is free software; you can redistribute it and/or modify |
254 | + * it under the terms of the GNU General Public License version 2, as |
255 | + * published by the Free Software Foundation. |
256 | + * |
257 | + * This program is distributed in the hope that it will be useful, |
258 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
259 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
260 | + * GNU General Public License for more details. |
261 | + * |
262 | + * You should have received a copy of the GNU General Public License along |
263 | + * with this program; if not, write to the Free Software Foundation, Inc., |
264 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
265 | + */ |
266 | + |
267 | +#ifdef HAVE_CONFIG_H |
268 | +# include <config.h> |
269 | +#endif /* HAVE_CONFIG_H */ |
270 | + |
271 | + |
272 | +#include <stdio.h> |
273 | +#include <stdlib.h> |
274 | +#include <string.h> |
275 | +#include <ctype.h> |
276 | + |
277 | +#include <nih/macros.h> |
278 | +#include <nih/alloc.h> |
279 | +#include <nih/string.h> |
280 | +#include <nih/hash.h> |
281 | +#include <nih/io.h> |
282 | +#include <nih/option.h> |
283 | +#include <nih/main.h> |
284 | +#include <nih/logging.h> |
285 | +#include <nih/error.h> |
286 | + |
287 | +#include <nih-dbus/dbus_connection.h> |
288 | +#include <nih-dbus/dbus_proxy.h> |
289 | + |
290 | +#include "dbus/upstart.h" |
291 | +#include "com.ubuntu.Upstart.h" |
292 | +#include "com.ubuntu.Upstart.Job.h" |
293 | + |
294 | +/** |
295 | + * DBUS_EVENT: |
296 | + * |
297 | + * Name of event this program handles. |
298 | + **/ |
299 | +#define DBUS_EVENT "dbus" |
300 | + |
301 | +/* Prototypes for static functions */ |
302 | +static int dbus_bus_setter (NihOption *option, const char *arg); |
303 | +static void dbus_disconnected (DBusConnection *connection); |
304 | +static void upstart_disconnected (DBusConnection *connection); |
305 | +static DBusHandlerResult signal_filter (DBusConnection *connection, |
306 | + DBusMessage *message, void *user_data); |
307 | +static void emit_event_error (void *data, NihDBusMessage *message); |
308 | +static void upstart_job_added (void *data, NihDBusMessage *message, |
309 | + const char *job); |
310 | +static void upstart_job_removed (void *data, NihDBusMessage *message, |
311 | + const char *job); |
312 | + |
313 | +/** |
314 | + * daemonise: |
315 | + * |
316 | + * Set to TRUE if we should become a daemon, rather than just running |
317 | + * in the foreground. |
318 | + **/ |
319 | +static int daemonise = FALSE; |
320 | + |
321 | +/** |
322 | + * upstart: |
323 | + * |
324 | + * Proxy to Upstart daemon. |
325 | + **/ |
326 | +static NihDBusProxy *upstart = NULL; |
327 | + |
328 | +/** |
329 | + * user_mode: |
330 | + * |
331 | + * If TRUE, connect to Session Init rather than PID 1. |
332 | + **/ |
333 | +static int user_mode = FALSE; |
334 | + |
335 | +/** |
336 | + * dbus_bus: |
337 | + * |
338 | + * type of D-Bus bus to connect to. |
339 | + **/ |
340 | +DBusBusType dbus_bus = (DBusBusType)-1; |
341 | + |
342 | +/** |
343 | + * Structure we use for tracking jobs |
344 | + * |
345 | + * @entry: list header, |
346 | + * @path: D-Bus path of job being tracked. |
347 | + **/ |
348 | +typedef struct job { |
349 | + NihList entry; |
350 | + char *path; |
351 | +} Job; |
352 | + |
353 | +/** |
354 | + * jobs: |
355 | + * |
356 | + * Jobs that we're monitoring. |
357 | + **/ |
358 | +static NihHash *jobs = NULL; |
359 | + |
360 | +/** |
361 | + * always: |
362 | + * |
363 | + * If TRUE, always emit Upstart events, regardless of whether |
364 | + * existing jobs care about DBUS_EVENT. |
365 | + */ |
366 | +static int always = FALSE; |
367 | + |
368 | +/** |
369 | + * options: |
370 | + * |
371 | + * Command-line options accepted by this program. |
372 | + **/ |
373 | +static NihOption options[] = { |
374 | + { 0, "always", N_("Always emit an event on receipt of D-Bus signal"), |
375 | + NULL, NULL, &always, NULL }, |
376 | + { 0, "daemon", N_("Detach and run in the background"), |
377 | + NULL, NULL, &daemonise, NULL }, |
378 | + { 0, "user", N_("Connect to user session"), |
379 | + NULL, NULL, &user_mode, NULL }, |
380 | + { 0, "session", N_("Use D-Bus session bus"), |
381 | + NULL, NULL, NULL, dbus_bus_setter }, |
382 | + { 0, "system", N_("Use D-Bus system bus"), |
383 | + NULL, NULL, NULL, dbus_bus_setter }, |
384 | + |
385 | + NIH_OPTION_LAST |
386 | +}; |
387 | + |
388 | + |
389 | +int |
390 | +main (int argc, |
391 | + char *argv[]) |
392 | +{ |
393 | + char **args; |
394 | + DBusConnection *dbus_connection; |
395 | + DBusConnection *connection; |
396 | + int ret; |
397 | + char *pidfile_path = NULL; |
398 | + char *pidfile = NULL; |
399 | + char *user_session_addr = NULL; |
400 | + nih_local char **user_session_path = NULL; |
401 | + char *path_element = NULL; |
402 | + DBusError error; |
403 | + char **job_class_paths; |
404 | + |
405 | + nih_main_init (argv[0]); |
406 | + |
407 | + nih_option_set_synopsis (_("Bridge D-Bus signals into upstart")); |
408 | + nih_option_set_help ( |
409 | + _("By default, upstart-event-bridge does not detach from the " |
410 | + "console and remains in the foreground. Use the --daemon " |
411 | + "option to have it detach.")); |
412 | + |
413 | + args = nih_option_parser (NULL, argc, argv, options, FALSE); |
414 | + if (! args) |
415 | + exit (EXIT_FAILURE); |
416 | + |
417 | + dbus_error_init (&error); |
418 | + |
419 | + /* Default to an appropriate bus */ |
420 | + if (dbus_bus == (DBusBusType)-1) |
421 | + dbus_bus = user_mode ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM; |
422 | + |
423 | + /* Connect to the chosen D-Bus bus */ |
424 | + dbus_connection = NIH_SHOULD (nih_dbus_bus (dbus_bus, dbus_disconnected)); |
425 | + |
426 | + if (! dbus_connection) { |
427 | + NihError *err; |
428 | + |
429 | + err = nih_error_get (); |
430 | + nih_fatal ("%s: %s", _("Could not connect to D-Bus"), |
431 | + err->message); |
432 | + nih_free (err); |
433 | + |
434 | + exit (EXIT_FAILURE); |
435 | + } |
436 | + |
437 | + dbus_bus_add_match (dbus_connection, "type='signal'", &error); |
438 | + |
439 | + if (dbus_error_is_set (&error)) { |
440 | + nih_fatal ("%s: %s %s", _("Could not add D-Bus signal match"), |
441 | + error.name, error.message); |
442 | + dbus_error_free (&error); |
443 | + |
444 | + exit (EXIT_FAILURE); |
445 | + } |
446 | + |
447 | + dbus_connection_add_filter (dbus_connection, signal_filter, NULL, NULL); |
448 | + |
449 | + dbus_error_free (&error); |
450 | + |
451 | + if (user_mode) { |
452 | + user_session_addr = getenv ("UPSTART_SESSION"); |
453 | + if (! user_session_addr) { |
454 | + nih_fatal (_("UPSTART_SESSION is not set in environment")); |
455 | + exit (EXIT_FAILURE); |
456 | + } |
457 | + } |
458 | + |
459 | + connection = NIH_SHOULD (nih_dbus_connect (user_mode |
460 | + ? user_session_addr |
461 | + : DBUS_ADDRESS_UPSTART, |
462 | + upstart_disconnected)); |
463 | + |
464 | + if (! connection) { |
465 | + NihError *err; |
466 | + |
467 | + err = nih_error_get (); |
468 | + nih_fatal ("%s: %s", _("Could not connect to Upstart"), |
469 | + err->message); |
470 | + nih_free (err); |
471 | + |
472 | + exit (EXIT_FAILURE); |
473 | + } |
474 | + |
475 | + /* Allocate jobs hash table */ |
476 | + jobs = NIH_MUST (nih_hash_string_new (NULL, 0)); |
477 | + |
478 | + upstart = NIH_SHOULD (nih_dbus_proxy_new (NULL, connection, |
479 | + NULL, DBUS_PATH_UPSTART, |
480 | + NULL, NULL)); |
481 | + |
482 | + if (! upstart) { |
483 | + NihError *err; |
484 | + |
485 | + err = nih_error_get (); |
486 | + nih_fatal ("%s: %s", _("Could not create Upstart proxy"), |
487 | + err->message); |
488 | + nih_free (err); |
489 | + |
490 | + exit (EXIT_FAILURE); |
491 | + } |
492 | + |
493 | + /* Connect signals to be notified when jobs come and go */ |
494 | + if (! nih_dbus_proxy_connect (upstart, &upstart_com_ubuntu_Upstart0_6, "JobAdded", |
495 | + (NihDBusSignalHandler)upstart_job_added, NULL)) { |
496 | + NihError *err; |
497 | + |
498 | + err = nih_error_get (); |
499 | + nih_fatal ("%s: %s", _("Could not create JobAdded signal connection"), |
500 | + err->message); |
501 | + nih_free (err); |
502 | + |
503 | + exit (EXIT_FAILURE); |
504 | + } |
505 | + |
506 | + if (! nih_dbus_proxy_connect (upstart, &upstart_com_ubuntu_Upstart0_6, "JobRemoved", |
507 | + (NihDBusSignalHandler)upstart_job_removed, NULL)) { |
508 | + NihError *err; |
509 | + |
510 | + err = nih_error_get (); |
511 | + nih_fatal ("%s: %s", _("Could not create JobRemoved signal connection"), |
512 | + err->message); |
513 | + nih_free (err); |
514 | + |
515 | + exit (EXIT_FAILURE); |
516 | + } |
517 | + |
518 | + /* Request a list of all current jobs */ |
519 | + if (upstart_get_all_jobs_sync (NULL, upstart, &job_class_paths) < 0) { |
520 | + NihError *err; |
521 | + |
522 | + err = nih_error_get (); |
523 | + nih_fatal ("%s: %s", _("Could not obtain job list"), |
524 | + err->message); |
525 | + nih_free (err); |
526 | + |
527 | + exit (EXIT_FAILURE); |
528 | + } |
529 | + |
530 | + for (char **job_class_path = job_class_paths; |
531 | + job_class_path && *job_class_path; job_class_path++) |
532 | + upstart_job_added (NULL, NULL, *job_class_path); |
533 | + |
534 | + nih_free (job_class_paths); |
535 | + |
536 | + /* Become daemon */ |
537 | + if (daemonise) { |
538 | + /* Deal with the pidfile location when becoming a daemon. |
539 | + * We need to be able to run one bridge per upstart daemon. |
540 | + * Store the PID file in XDG_RUNTIME_DIR or HOME and include the pid of |
541 | + * the Upstart instance (last part of the DBus path) in the filename. |
542 | + */ |
543 | + |
544 | + if (user_mode) { |
545 | + /* Extract PID from UPSTART_SESSION */ |
546 | + user_session_path = nih_str_split (NULL, user_session_addr, "/", TRUE); |
547 | + for (int i = 0; user_session_path[i] != NULL; i++) |
548 | + path_element = user_session_path[i]; |
549 | + |
550 | + if (! path_element) { |
551 | + nih_fatal (_("Invalid value for UPSTART_SESSION")); |
552 | + exit (EXIT_FAILURE); |
553 | + } |
554 | + |
555 | + pidfile_path = getenv ("XDG_RUNTIME_DIR"); |
556 | + if (! pidfile_path) |
557 | + pidfile_path = getenv ("HOME"); |
558 | + |
559 | + if (pidfile_path) { |
560 | + NIH_MUST (nih_strcat_sprintf (&pidfile, NULL, "%s/upstart-dbus-bridge.%s.pid", |
561 | + pidfile_path, path_element)); |
562 | + nih_main_set_pidfile (pidfile); |
563 | + } |
564 | + } |
565 | + |
566 | + if (nih_main_daemonise () < 0) { |
567 | + NihError *err; |
568 | + |
569 | + err = nih_error_get (); |
570 | + nih_fatal ("%s: %s", _("Unable to become daemon"), |
571 | + err->message); |
572 | + nih_free (err); |
573 | + |
574 | + exit (EXIT_FAILURE); |
575 | + } |
576 | + } |
577 | + |
578 | + /* Handle TERM and INT signals gracefully */ |
579 | + nih_signal_set_handler (SIGTERM, nih_signal_handler); |
580 | + NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, nih_main_term_signal, NULL)); |
581 | + |
582 | + if (! daemonise) { |
583 | + nih_signal_set_handler (SIGINT, nih_signal_handler); |
584 | + NIH_MUST (nih_signal_add_handler (NULL, SIGINT, nih_main_term_signal, NULL)); |
585 | + } |
586 | + |
587 | + ret = nih_main_loop (); |
588 | + |
589 | + /* Destroy any PID file we may have created */ |
590 | + if (daemonise) { |
591 | + nih_main_unlink_pidfile(); |
592 | + } |
593 | + |
594 | + return ret; |
595 | +} |
596 | + |
597 | +/** |
598 | + * dbus_disconnected: |
599 | + * |
600 | + * @connection: connection to a D-Bus bus. |
601 | + * |
602 | + * Handler called when bridge disconnected from D-Bus. |
603 | + **/ |
604 | +static void |
605 | +dbus_disconnected (DBusConnection *connection) |
606 | +{ |
607 | + nih_fatal (_("Disconnected from D-Bus")); |
608 | + nih_main_loop_exit (EXIT_FAILURE); |
609 | +} |
610 | + |
611 | +/** |
612 | + * upstart_disconnected: |
613 | + * |
614 | + * @connection: connection to Upstart. |
615 | + * |
616 | + * Handler called when bridge disconnected from Upstart. |
617 | + **/ |
618 | +static void |
619 | +upstart_disconnected (DBusConnection *connection) |
620 | +{ |
621 | + nih_fatal (_("Disconnected from Upstart")); |
622 | + nih_main_loop_exit (EXIT_FAILURE); |
623 | +} |
624 | + |
625 | +/** |
626 | + * NihOption setter function to handle selection of D-Bus bus type. |
627 | + * |
628 | + * Returns: 0 on success, -1 on invalid console type. |
629 | + **/ |
630 | +static int |
631 | +dbus_bus_setter (NihOption *option, const char *arg) |
632 | +{ |
633 | + nih_assert (option); |
634 | + nih_assert (option->long_option); |
635 | + |
636 | + if (! strcmp (option->long_option, "session")) { |
637 | + dbus_bus = DBUS_BUS_SESSION; |
638 | + } else { |
639 | + dbus_bus = DBUS_BUS_SYSTEM; |
640 | + } |
641 | + |
642 | + return 0; |
643 | +} |
644 | + |
645 | +/** |
646 | + * signal_filter: |
647 | + * @connection: D-Bus connection, |
648 | + * @message: D-Bus message, |
649 | + * @user_data: unused. |
650 | + * |
651 | + * Handle D-Bus signal message by emitting an Upstart event |
652 | + * containing pertinent details from the original message. |
653 | + * |
654 | + * Returns: DBUS_HANDLER_RESULT_HANDLED always. |
655 | + **/ |
656 | +static DBusHandlerResult |
657 | +signal_filter (DBusConnection *connection, |
658 | + DBusMessage *message, |
659 | + void *user_data) |
660 | +{ |
661 | + int emit = FALSE; |
662 | + DBusPendingCall *pending_call; |
663 | + DBusError error; |
664 | + nih_local char **env = NULL; |
665 | + const char *sender; |
666 | + const char *destination; |
667 | + const char *interface; |
668 | + const char *signal; |
669 | + const char *path; |
670 | + size_t env_len = 0; |
671 | + |
672 | + nih_assert (connection); |
673 | + nih_assert (message); |
674 | + |
675 | + if (! always) { |
676 | + NIH_HASH_FOREACH (jobs, iter) { |
677 | + emit = TRUE; |
678 | + break; |
679 | + } |
680 | + |
681 | + /* No jobs care about DBUS_EVENT, so ignore it */ |
682 | + if (! emit) |
683 | + goto out; |
684 | + } |
685 | + |
686 | + dbus_error_init (&error); |
687 | + |
688 | + sender = dbus_message_get_sender (message); |
689 | + signal = dbus_message_get_member (message); |
690 | + interface = dbus_message_get_interface (message); |
691 | + path = dbus_message_get_path (message); |
692 | + destination = dbus_message_get_destination (message); |
693 | + |
694 | + /* Don't react to D-Bus signals generated by Upstart |
695 | + * to avoid a possible feedback loop: for example, imagine a job |
696 | + * that emits an event when it detects (via this bridge) that |
697 | + * Upstart has emitted an event by considering Upstarts |
698 | + * "EventEmitted" D-Bus signal interface... |
699 | + */ |
700 | + if ((sender && ! strcmp (sender, DBUS_SERVICE_UPSTART)) || |
701 | + (interface && ! strcmp (interface, DBUS_INTERFACE_UPSTART))) { |
702 | + nih_debug ("ignoring signal originating from upstart itself"); |
703 | + goto out; |
704 | + } |
705 | + |
706 | + env = NIH_MUST (nih_str_array_new (NULL)); |
707 | + |
708 | + if (signal) { |
709 | + nih_local char *var = NULL; |
710 | + var = NIH_MUST (nih_sprintf (NULL, "SIGNAL=%s", signal)); |
711 | + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
712 | + } else { |
713 | + /* We need something to work with */ |
714 | + nih_debug ("Ignoring message with no signal name"); |
715 | + goto out; |
716 | + } |
717 | + |
718 | + if (interface) { |
719 | + nih_local char *var = NULL; |
720 | + var = NIH_MUST (nih_sprintf (NULL, "INTERFACE=%s", interface)); |
721 | + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
722 | + } |
723 | + |
724 | + if (path) { |
725 | + nih_local char *var = NULL; |
726 | + var = NIH_MUST (nih_sprintf (NULL, "PATH=%s", path)); |
727 | + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
728 | + } |
729 | + |
730 | + if (sender) { |
731 | + nih_local char *var = NULL; |
732 | + var = NIH_MUST (nih_sprintf (NULL, "SENDER=%s", sender)); |
733 | + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
734 | + } |
735 | + |
736 | + if (destination) { |
737 | + nih_local char *var = NULL; |
738 | + var = NIH_MUST (nih_sprintf (NULL, "DESTINATION=%s", destination)); |
739 | + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
740 | + } |
741 | + |
742 | + nih_debug ("Received D-Bus signal: %s " |
743 | + "(sender=%s, destination=%s, interface=%s, path=%s)", |
744 | + signal ? signal : "", |
745 | + sender ? sender : "", |
746 | + destination ? destination : "", |
747 | + interface ? interface : "", |
748 | + path ? path : ""); |
749 | + |
750 | + pending_call = upstart_emit_event (upstart, |
751 | + DBUS_EVENT, env, FALSE, |
752 | + NULL, emit_event_error, NULL, |
753 | + NIH_DBUS_TIMEOUT_NEVER); |
754 | + |
755 | + if (! pending_call) { |
756 | + NihError *err; |
757 | + err = nih_error_get (); |
758 | + nih_warn ("%s", err->message); |
759 | + nih_free (err); |
760 | + } |
761 | + |
762 | + dbus_pending_call_unref (pending_call); |
763 | + |
764 | +out: |
765 | + return DBUS_HANDLER_RESULT_HANDLED; |
766 | +} |
767 | + |
768 | +static void |
769 | +emit_event_error (void *data, |
770 | + NihDBusMessage *message) |
771 | +{ |
772 | + NihError *err; |
773 | + |
774 | + err = nih_error_get (); |
775 | + nih_warn ("%s", err->message); |
776 | + nih_free (err); |
777 | +} |
778 | + |
779 | +static void |
780 | +upstart_job_added (void *data, |
781 | + NihDBusMessage *message, |
782 | + const char *job_class_path) |
783 | +{ |
784 | + /* set to TRUE if jobs start/stop conditions specify |
785 | + * DBUS_EVENT. Used to restrict emission of events |
786 | + * unnecessarily. Note that event environment matching |
787 | + * though is handled by Upstart. |
788 | + */ |
789 | + int add = FALSE; |
790 | + |
791 | + Job *job; |
792 | + nih_local NihDBusProxy *job_class = NULL; |
793 | + nih_local char ***start_on = NULL; |
794 | + nih_local char ***stop_on = NULL; |
795 | + |
796 | + nih_assert (job_class_path != NULL); |
797 | + |
798 | + /* Obtain a proxy to the job */ |
799 | + job_class = nih_dbus_proxy_new (NULL, upstart->connection, |
800 | + upstart->name, job_class_path, |
801 | + NULL, NULL); |
802 | + if (! job_class) { |
803 | + NihError *err; |
804 | + |
805 | + err = nih_error_get (); |
806 | + nih_error ("Could not create proxy for job %s: %s", |
807 | + job_class_path, err->message); |
808 | + nih_free (err); |
809 | + |
810 | + return; |
811 | + } |
812 | + |
813 | + job_class->auto_start = FALSE; |
814 | + |
815 | + /* Obtain the start_on and stop_on properties of the job */ |
816 | + if (job_class_get_start_on_sync (NULL, job_class, &start_on) < 0) { |
817 | + NihError *err; |
818 | + |
819 | + err = nih_error_get (); |
820 | + nih_error ("Could not obtain job start condition %s: %s", |
821 | + job_class_path, err->message); |
822 | + nih_free (err); |
823 | + |
824 | + return; |
825 | + } |
826 | + |
827 | + if (job_class_get_stop_on_sync (NULL, job_class, &stop_on) < 0) { |
828 | + NihError *err; |
829 | + |
830 | + err = nih_error_get (); |
831 | + nih_error ("Could not obtain job stop condition %s: %s", |
832 | + job_class_path, err->message); |
833 | + nih_free (err); |
834 | + |
835 | + return; |
836 | + } |
837 | + |
838 | + /* Find out whether this job listens for any socket events */ |
839 | + for (char ***event = start_on; event && *event && **event; event++) |
840 | + if (! strcmp (**event, DBUS_EVENT)) { |
841 | + add = TRUE; |
842 | + break; |
843 | + } |
844 | + |
845 | + for (char ***event = stop_on; ! add && event && *event && **event; event++) |
846 | + if (! strcmp (**event, DBUS_EVENT)) { |
847 | + add = TRUE; |
848 | + break; |
849 | + } |
850 | + |
851 | + if (! add) |
852 | + return; |
853 | + |
854 | + nih_debug ("Job got added %s for event %s", job_class_path, DBUS_EVENT); |
855 | + |
856 | + /* Free any existing record for the job (should never happen, |
857 | + * but worth being safe). |
858 | + */ |
859 | + job = (Job *)nih_hash_lookup (jobs, job_class_path); |
860 | + if (job) |
861 | + nih_free (job); |
862 | + |
863 | + /* Create new record for the job */ |
864 | + job = NIH_MUST (nih_new (NULL, Job)); |
865 | + job->path = NIH_MUST (nih_strdup (job, job_class_path)); |
866 | + |
867 | + nih_list_init (&job->entry); |
868 | + nih_alloc_set_destructor (job, nih_list_destroy); |
869 | + nih_hash_add (jobs, &job->entry); |
870 | +} |
871 | + |
872 | +static void |
873 | +upstart_job_removed (void *data, |
874 | + NihDBusMessage *message, |
875 | + const char *job_path) |
876 | +{ |
877 | + Job *job; |
878 | + |
879 | + nih_assert (job_path != NULL); |
880 | + |
881 | + job = (Job *)nih_hash_lookup (jobs, job_path); |
882 | + if (job) { |
883 | + nih_debug ("Job went away %s", job_path); |
884 | + nih_free (job); |
885 | + } |
886 | +} |
887 | + |
Looking through this I don't see a way to specify which bus you want the event to come from. So will there be two dbus bridges in the user session? So I could do:
start on dbus SENDER=foo
or
start on dbus-system SENDER=foo
Not quite sure how that'll work.