Merge lp:~jamesodhunt/upstart/upstart-dbus-bridge into lp:upstart

Proposed by James Hunt
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
Reviewer Review Type Date Requested Status
Dimitri John Ledkov Approve
Review via email: mp+161772@code.launchpad.net

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

To post a comment you must log in.
1479. By James Hunt

* extra/upstart-dbus-bridge.c:upstart_job_added(): Only display debug
  message if job cares about DBUS_EVENT.

Revision history for this message
Ted Gould (ted) 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.

Revision history for this message
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?

Revision history for this message
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://code.launchpad.net/~jamesodhunt/upstart/upstart-dbus-bridge/+merge/161772
> 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://www.ubuntu.com

Revision history for this message
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).

Revision history for this message
Ted Gould (ted) wrote :

So, besides the security implications, as long as there's a way to do it, I'm happy :-)

Revision history for this message
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.

Revision history for this message
Dimitri John Ledkov (xnox) :
review: Approve
Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ChangeLog'
--- ChangeLog 2013-04-30 23:26:25 +0000
+++ ChangeLog 2013-05-01 05:49:25 +0000
@@ -1,3 +1,19 @@
12013-04-30 James Hunt <james.hunt@ubuntu.com>
2
3 * extra/Makefile.am: Add man pages and conf file.
4 * extra/upstart-dbus-bridge.c: Only emit events if any jobs care
5 about them.
6 * extra/conf/upstart-dbus-bridge.conf: New configuration file.
7 * extra/man/dbus-event.7: New man page.
8 * extra/man/upstart-dbus-bridge.8: New man page.
9
102013-04-25 James Hunt <james.hunt@ubuntu.com>
11
12 * extra/upstart-dbus-bridge.c:
13 - Comments and formatting.
14 - main(): Default to an appropriate bus.
15 - signal_filter(): Display signal details when run with --debug.
16
12013-04-22 James Hunt <james.hunt@ubuntu.com>172013-04-22 James Hunt <james.hunt@ubuntu.com>
218
3 * Typo and doc changes.19 * Typo and doc changes.
420
=== modified file 'extra/Makefile.am'
--- extra/Makefile.am 2013-03-11 20:02:18 +0000
+++ extra/Makefile.am 2013-05-01 05:49:25 +0000
@@ -19,19 +19,24 @@
19sbin_PROGRAMS = \19sbin_PROGRAMS = \
20 upstart-socket-bridge \20 upstart-socket-bridge \
21 upstart-event-bridge \21 upstart-event-bridge \
22 upstart-file-bridge22 upstart-file-bridge \
23 upstart-dbus-bridge
2324
24dist_init_DATA = \25dist_init_DATA = \
25 conf/upstart-socket-bridge.conf \26 conf/upstart-socket-bridge.conf \
26 conf/upstart-event-bridge.conf \27 conf/upstart-event-bridge.conf \
27 conf/upstart-file-bridge.conf28 conf/upstart-file-bridge.conf \
29 conf/upstart-dbus-bridge.conf
2830
31# FIXME: add man/upstart-dbus-bridge.8
29dist_man_MANS = \32dist_man_MANS = \
30 man/upstart-socket-bridge.8 \33 man/upstart-socket-bridge.8 \
31 man/upstart-event-bridge.8 \34 man/upstart-event-bridge.8 \
32 man/upstart-file-bridge.8 \35 man/upstart-file-bridge.8 \
36 man/upstart-dbus-bridge.8 \
33 man/socket-event.7 \37 man/socket-event.7 \
34 man/file-event.738 man/file-event.7 \
39 man/dbus-event.7
3540
36upstart_socket_bridge_SOURCES = \41upstart_socket_bridge_SOURCES = \
37 upstart-socket-bridge.c42 upstart-socket-bridge.c
@@ -66,6 +71,17 @@
66 $(NIH_DBUS_LIBS) \71 $(NIH_DBUS_LIBS) \
67 $(DBUS_LIBS)72 $(DBUS_LIBS)
6873
74upstart_dbus_bridge_SOURCES = \
75 upstart-dbus-bridge.c
76nodist_upstart_dbus_bridge_SOURCES = \
77 $(com_ubuntu_Upstart_OUTPUTS) \
78 $(com_ubuntu_Upstart_Job_OUTPUTS)
79upstart_dbus_bridge_LDADD = \
80 $(LTLIBINTL) \
81 $(NIH_LIBS) \
82 $(NIH_DBUS_LIBS) \
83 $(DBUS_LIBS)
84
69if HAVE_UDEV85if HAVE_UDEV
70dist_init_DATA += \86dist_init_DATA += \
71 conf/upstart-udev-bridge.conf87 conf/upstart-udev-bridge.conf
7288
=== added file 'extra/conf/upstart-dbus-bridge.conf'
--- extra/conf/upstart-dbus-bridge.conf 1970-01-01 00:00:00 +0000
+++ extra/conf/upstart-dbus-bridge.conf 2013-05-01 05:49:25 +0000
@@ -0,0 +1,17 @@
1# upstart-dbus-bridge - Bridge D-Bus signal events into upstart
2#
3# This helper daemon receives D-Bus signal events and
4# emits equivalent Upstart events.
5
6description "Bridge D-Bus signal events into upstart"
7
8emits dbus
9
10start on startup
11
12stop on runlevel [!2345]
13
14expect daemon
15respawn
16
17exec upstart-dbus-bridge --daemon --system
018
=== added file 'extra/man/dbus-event.7'
--- extra/man/dbus-event.7 1970-01-01 00:00:00 +0000
+++ extra/man/dbus-event.7 2013-05-01 05:49:25 +0000
@@ -0,0 +1,53 @@
1.TH dbus\-event 7 2013-04-25 upstart
2.\"
3.SH NAME
4dbus \- event signalling that a dbus signal has been emitted
5.\"
6.SH SYNOPSIS
7.B dbus
8.BI SIGNAL\fR= SIGNAL
9.BI INTERFACE\fR= INTERFACE
10.BI PATH\fR= PATH
11.BI SENDER\fR= SENDER
12.BI DESTINATION\fR= DESTINATION
13.\"
14.SH DESCRIPTION
15
16The
17.B dbus
18event is generated by the
19.BR upstart\-dbus\-bridge (8)
20daemon when a D-Bus signal is emitted whose details match the
21.I dbus
22event condition and environment specified in a job's
23.B start on
24or
25.B stop on
26stanza is modified.
27
28.\"
29.SH EXAMPLES
30.\"
31.IP "start on dbus SIGNAL=NameAcquired INTERFACE=org.freedesktop.DBus PATH=/org/freedesktop/DBus SENDER=org.freedesktop.DBus"
32Start job when a particular
33.I NameAcquired
34D-Bus signal is received.
35.\"
36.SH AUTHOR
37Written by James Hunt
38.RB < james.hunt@canonical.com >
39.\"
40.SH BUGS
41Report bugs at
42.RB < https://launchpad.net/upstart/+bugs >
43.\"
44.SH COPYRIGHT
45Copyright \(co 2013 Canonical Ltd.
46.PP
47This is free software; see the source for copying conditions. There is NO
48warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
49.\"
50.SH SEE ALSO
51.BR init (5)
52.BR init (8)
53.BR upstart\-dbus\-bridge (8)
054
=== added file 'extra/man/upstart-dbus-bridge.8'
--- extra/man/upstart-dbus-bridge.8 1970-01-01 00:00:00 +0000
+++ extra/man/upstart-dbus-bridge.8 2013-05-01 05:49:25 +0000
@@ -0,0 +1,84 @@
1.TH upstart\-dbus\-bridge 8 2013-04-25 upstart
2.\"
3.SH NAME
4upstart\-dbus\-bridge \- Bridge between Upstart and D-Bus
5.\"
6.SH SYNOPSIS
7.B upstart\-dbus\-bridge
8.RI [ OPTIONS ]...
9.\"
10.SH DESCRIPTION
11.B upstart\-dbus\-bridge
12receives information about D-Bus signals
13and creates
14.BR init (8)
15events for them.
16
17With no options, monitors signals on the D-Bus system bus and emits
18an Upstart event called
19.I dbus
20via a D-Bus system bus connection to Upstart.
21
22When run with \fB\-\-user\fP, monitors signals on the users D-Bus session bus
23and emits Upstart events via the private D-Bus connection to the users Session Init.
24
25See \fBdbus\-daemon\fP(1) and for further details.
26
27.\"
28.SH OPTIONS
29.\"
30.TP
31.B \-\-always
32Always emit events on receipt of D-Bus signal regardless of whether jobs
33care about them.
34.TP
35.B \-\-daemon
36Detach and run in the background.
37.\"
38.TP
39.B \-\-debug
40Enable debugging output.
41.\"
42.TP
43.B \-\-help
44Show brief usage summary.
45.\"
46.TP
47.B \-\-session
48Monitor signals on the D-Bus session bus.
49.\"
50.TP
51.B \-\-system
52Monitor signals on the D-Bus system bus.
53.\"
54.TP
55.B \-\-user
56User-session mode: connect to Upstart via the user session rather than
57over the D\-Bus system bus.
58.\"
59.TP
60.B \-\-verbose
61Enable verbose output.
62.\"
63.SH RESTRICTIONS
64D-Bus signals emitted by Upstart itself are ignored.
65
66.\"
67.SH AUTHOR
68Written by James Hunt
69.RB < james.hunt@canonical.com >
70.\"
71.SH BUGS
72Report bugs at
73.RB < https://launchpad.net/ubuntu/+source/upstart/+bugs >
74.\"
75.SH COPYRIGHT
76Copyright \(co 2013 Canonical Ltd.
77.PP
78This is free software; see the source for copying conditions. There is NO
79warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
80.SH SEE ALSO
81.BR dbus\-daemon (1)
82.BR dbus\-event (7)
83.BR init (5)
84.BR init (8)
085
=== added file 'extra/upstart-dbus-bridge.c'
--- extra/upstart-dbus-bridge.c 1970-01-01 00:00:00 +0000
+++ extra/upstart-dbus-bridge.c 2013-05-01 05:49:25 +0000
@@ -0,0 +1,640 @@
1/* upstart
2 *
3 * Copyright © 2013 Canonical Ltd.
4 * Author: James Hunt <james.hunt@ubuntu.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif /* HAVE_CONFIG_H */
23
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <ctype.h>
29
30#include <nih/macros.h>
31#include <nih/alloc.h>
32#include <nih/string.h>
33#include <nih/hash.h>
34#include <nih/io.h>
35#include <nih/option.h>
36#include <nih/main.h>
37#include <nih/logging.h>
38#include <nih/error.h>
39
40#include <nih-dbus/dbus_connection.h>
41#include <nih-dbus/dbus_proxy.h>
42
43#include "dbus/upstart.h"
44#include "com.ubuntu.Upstart.h"
45#include "com.ubuntu.Upstart.Job.h"
46
47/**
48 * DBUS_EVENT:
49 *
50 * Name of event this program handles.
51 **/
52#define DBUS_EVENT "dbus"
53
54/* Prototypes for static functions */
55static int dbus_bus_setter (NihOption *option, const char *arg);
56static void dbus_disconnected (DBusConnection *connection);
57static void upstart_disconnected (DBusConnection *connection);
58static DBusHandlerResult signal_filter (DBusConnection *connection,
59 DBusMessage *message, void *user_data);
60static void emit_event_error (void *data, NihDBusMessage *message);
61static void upstart_job_added (void *data, NihDBusMessage *message,
62 const char *job);
63static void upstart_job_removed (void *data, NihDBusMessage *message,
64 const char *job);
65
66/**
67 * daemonise:
68 *
69 * Set to TRUE if we should become a daemon, rather than just running
70 * in the foreground.
71 **/
72static int daemonise = FALSE;
73
74/**
75 * upstart:
76 *
77 * Proxy to Upstart daemon.
78 **/
79static NihDBusProxy *upstart = NULL;
80
81/**
82 * user_mode:
83 *
84 * If TRUE, connect to Session Init rather than PID 1.
85 **/
86static int user_mode = FALSE;
87
88/**
89 * dbus_bus:
90 *
91 * type of D-Bus bus to connect to.
92 **/
93DBusBusType dbus_bus = (DBusBusType)-1;
94
95/**
96 * Structure we use for tracking jobs
97 *
98 * @entry: list header,
99 * @path: D-Bus path of job being tracked.
100 **/
101typedef struct job {
102 NihList entry;
103 char *path;
104} Job;
105
106/**
107 * jobs:
108 *
109 * Jobs that we're monitoring.
110 **/
111static NihHash *jobs = NULL;
112
113/**
114 * always:
115 *
116 * If TRUE, always emit Upstart events, regardless of whether
117 * existing jobs care about DBUS_EVENT.
118 */
119static int always = FALSE;
120
121/**
122 * options:
123 *
124 * Command-line options accepted by this program.
125 **/
126static NihOption options[] = {
127 { 0, "always", N_("Always emit an event on receipt of D-Bus signal"),
128 NULL, NULL, &always, NULL },
129 { 0, "daemon", N_("Detach and run in the background"),
130 NULL, NULL, &daemonise, NULL },
131 { 0, "user", N_("Connect to user session"),
132 NULL, NULL, &user_mode, NULL },
133 { 0, "session", N_("Use D-Bus session bus"),
134 NULL, NULL, NULL, dbus_bus_setter },
135 { 0, "system", N_("Use D-Bus system bus"),
136 NULL, NULL, NULL, dbus_bus_setter },
137
138 NIH_OPTION_LAST
139};
140
141
142int
143main (int argc,
144 char *argv[])
145{
146 char **args;
147 DBusConnection *dbus_connection;
148 DBusConnection *connection;
149 int ret;
150 char *pidfile_path = NULL;
151 char *pidfile = NULL;
152 char *user_session_addr = NULL;
153 nih_local char **user_session_path = NULL;
154 char *path_element = NULL;
155 DBusError error;
156 char **job_class_paths;
157
158 nih_main_init (argv[0]);
159
160 nih_option_set_synopsis (_("Bridge D-Bus signals into upstart"));
161 nih_option_set_help (
162 _("By default, upstart-event-bridge does not detach from the "
163 "console and remains in the foreground. Use the --daemon "
164 "option to have it detach."));
165
166 args = nih_option_parser (NULL, argc, argv, options, FALSE);
167 if (! args)
168 exit (EXIT_FAILURE);
169
170 dbus_error_init (&error);
171
172 /* Default to an appropriate bus */
173 if (dbus_bus == (DBusBusType)-1)
174 dbus_bus = user_mode ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM;
175
176 /* Connect to the chosen D-Bus bus */
177 dbus_connection = NIH_SHOULD (nih_dbus_bus (dbus_bus, dbus_disconnected));
178
179 if (! dbus_connection) {
180 NihError *err;
181
182 err = nih_error_get ();
183 nih_fatal ("%s: %s", _("Could not connect to D-Bus"),
184 err->message);
185 nih_free (err);
186
187 exit (EXIT_FAILURE);
188 }
189
190 dbus_bus_add_match (dbus_connection, "type='signal'", &error);
191
192 if (dbus_error_is_set (&error)) {
193 nih_fatal ("%s: %s %s", _("Could not add D-Bus signal match"),
194 error.name, error.message);
195 dbus_error_free (&error);
196
197 exit (EXIT_FAILURE);
198 }
199
200 dbus_connection_add_filter (dbus_connection, signal_filter, NULL, NULL);
201
202 dbus_error_free (&error);
203
204 if (user_mode) {
205 user_session_addr = getenv ("UPSTART_SESSION");
206 if (! user_session_addr) {
207 nih_fatal (_("UPSTART_SESSION is not set in environment"));
208 exit (EXIT_FAILURE);
209 }
210 }
211
212 connection = NIH_SHOULD (nih_dbus_connect (user_mode
213 ? user_session_addr
214 : DBUS_ADDRESS_UPSTART,
215 upstart_disconnected));
216
217 if (! connection) {
218 NihError *err;
219
220 err = nih_error_get ();
221 nih_fatal ("%s: %s", _("Could not connect to Upstart"),
222 err->message);
223 nih_free (err);
224
225 exit (EXIT_FAILURE);
226 }
227
228 /* Allocate jobs hash table */
229 jobs = NIH_MUST (nih_hash_string_new (NULL, 0));
230
231 upstart = NIH_SHOULD (nih_dbus_proxy_new (NULL, connection,
232 NULL, DBUS_PATH_UPSTART,
233 NULL, NULL));
234
235 if (! upstart) {
236 NihError *err;
237
238 err = nih_error_get ();
239 nih_fatal ("%s: %s", _("Could not create Upstart proxy"),
240 err->message);
241 nih_free (err);
242
243 exit (EXIT_FAILURE);
244 }
245
246 /* Connect signals to be notified when jobs come and go */
247 if (! nih_dbus_proxy_connect (upstart, &upstart_com_ubuntu_Upstart0_6, "JobAdded",
248 (NihDBusSignalHandler)upstart_job_added, NULL)) {
249 NihError *err;
250
251 err = nih_error_get ();
252 nih_fatal ("%s: %s", _("Could not create JobAdded signal connection"),
253 err->message);
254 nih_free (err);
255
256 exit (EXIT_FAILURE);
257 }
258
259 if (! nih_dbus_proxy_connect (upstart, &upstart_com_ubuntu_Upstart0_6, "JobRemoved",
260 (NihDBusSignalHandler)upstart_job_removed, NULL)) {
261 NihError *err;
262
263 err = nih_error_get ();
264 nih_fatal ("%s: %s", _("Could not create JobRemoved signal connection"),
265 err->message);
266 nih_free (err);
267
268 exit (EXIT_FAILURE);
269 }
270
271 /* Request a list of all current jobs */
272 if (upstart_get_all_jobs_sync (NULL, upstart, &job_class_paths) < 0) {
273 NihError *err;
274
275 err = nih_error_get ();
276 nih_fatal ("%s: %s", _("Could not obtain job list"),
277 err->message);
278 nih_free (err);
279
280 exit (EXIT_FAILURE);
281 }
282
283 for (char **job_class_path = job_class_paths;
284 job_class_path && *job_class_path; job_class_path++)
285 upstart_job_added (NULL, NULL, *job_class_path);
286
287 nih_free (job_class_paths);
288
289 /* Become daemon */
290 if (daemonise) {
291 /* Deal with the pidfile location when becoming a daemon.
292 * We need to be able to run one bridge per upstart daemon.
293 * Store the PID file in XDG_RUNTIME_DIR or HOME and include the pid of
294 * the Upstart instance (last part of the DBus path) in the filename.
295 */
296
297 if (user_mode) {
298 /* Extract PID from UPSTART_SESSION */
299 user_session_path = nih_str_split (NULL, user_session_addr, "/", TRUE);
300 for (int i = 0; user_session_path[i] != NULL; i++)
301 path_element = user_session_path[i];
302
303 if (! path_element) {
304 nih_fatal (_("Invalid value for UPSTART_SESSION"));
305 exit (EXIT_FAILURE);
306 }
307
308 pidfile_path = getenv ("XDG_RUNTIME_DIR");
309 if (! pidfile_path)
310 pidfile_path = getenv ("HOME");
311
312 if (pidfile_path) {
313 NIH_MUST (nih_strcat_sprintf (&pidfile, NULL, "%s/upstart-dbus-bridge.%s.pid",
314 pidfile_path, path_element));
315 nih_main_set_pidfile (pidfile);
316 }
317 }
318
319 if (nih_main_daemonise () < 0) {
320 NihError *err;
321
322 err = nih_error_get ();
323 nih_fatal ("%s: %s", _("Unable to become daemon"),
324 err->message);
325 nih_free (err);
326
327 exit (EXIT_FAILURE);
328 }
329 }
330
331 /* Handle TERM and INT signals gracefully */
332 nih_signal_set_handler (SIGTERM, nih_signal_handler);
333 NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, nih_main_term_signal, NULL));
334
335 if (! daemonise) {
336 nih_signal_set_handler (SIGINT, nih_signal_handler);
337 NIH_MUST (nih_signal_add_handler (NULL, SIGINT, nih_main_term_signal, NULL));
338 }
339
340 ret = nih_main_loop ();
341
342 /* Destroy any PID file we may have created */
343 if (daemonise) {
344 nih_main_unlink_pidfile();
345 }
346
347 return ret;
348}
349
350/**
351 * dbus_disconnected:
352 *
353 * @connection: connection to a D-Bus bus.
354 *
355 * Handler called when bridge disconnected from D-Bus.
356 **/
357static void
358dbus_disconnected (DBusConnection *connection)
359{
360 nih_fatal (_("Disconnected from D-Bus"));
361 nih_main_loop_exit (EXIT_FAILURE);
362}
363
364/**
365 * upstart_disconnected:
366 *
367 * @connection: connection to Upstart.
368 *
369 * Handler called when bridge disconnected from Upstart.
370 **/
371static void
372upstart_disconnected (DBusConnection *connection)
373{
374 nih_fatal (_("Disconnected from Upstart"));
375 nih_main_loop_exit (EXIT_FAILURE);
376}
377
378/**
379 * NihOption setter function to handle selection of D-Bus bus type.
380 *
381 * Returns: 0 on success, -1 on invalid console type.
382 **/
383static int
384dbus_bus_setter (NihOption *option, const char *arg)
385{
386 nih_assert (option);
387 nih_assert (option->long_option);
388
389 if (! strcmp (option->long_option, "session")) {
390 dbus_bus = DBUS_BUS_SESSION;
391 } else {
392 dbus_bus = DBUS_BUS_SYSTEM;
393 }
394
395 return 0;
396}
397
398/**
399 * signal_filter:
400 * @connection: D-Bus connection,
401 * @message: D-Bus message,
402 * @user_data: unused.
403 *
404 * Handle D-Bus signal message by emitting an Upstart event
405 * containing pertinent details from the original message.
406 *
407 * Returns: DBUS_HANDLER_RESULT_HANDLED always.
408 **/
409static DBusHandlerResult
410signal_filter (DBusConnection *connection,
411 DBusMessage *message,
412 void *user_data)
413{
414 int emit = FALSE;
415 DBusPendingCall *pending_call;
416 DBusError error;
417 nih_local char **env = NULL;
418 const char *sender;
419 const char *destination;
420 const char *interface;
421 const char *signal;
422 const char *path;
423 size_t env_len = 0;
424
425 nih_assert (connection);
426 nih_assert (message);
427
428 if (! always) {
429 NIH_HASH_FOREACH (jobs, iter) {
430 emit = TRUE;
431 break;
432 }
433
434 /* No jobs care about DBUS_EVENT, so ignore it */
435 if (! emit)
436 goto out;
437 }
438
439 dbus_error_init (&error);
440
441 sender = dbus_message_get_sender (message);
442 signal = dbus_message_get_member (message);
443 interface = dbus_message_get_interface (message);
444 path = dbus_message_get_path (message);
445 destination = dbus_message_get_destination (message);
446
447 /* Don't react to D-Bus signals generated by Upstart
448 * to avoid a possible feedback loop: for example, imagine a job
449 * that emits an event when it detects (via this bridge) that
450 * Upstart has emitted an event by considering Upstarts
451 * "EventEmitted" D-Bus signal interface...
452 */
453 if ((sender && ! strcmp (sender, DBUS_SERVICE_UPSTART)) ||
454 (interface && ! strcmp (interface, DBUS_INTERFACE_UPSTART))) {
455 nih_debug ("ignoring signal originating from upstart itself");
456 goto out;
457 }
458
459 env = NIH_MUST (nih_str_array_new (NULL));
460
461 if (signal) {
462 nih_local char *var = NULL;
463 var = NIH_MUST (nih_sprintf (NULL, "SIGNAL=%s", signal));
464 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var));
465 } else {
466 /* We need something to work with */
467 nih_debug ("Ignoring message with no signal name");
468 goto out;
469 }
470
471 if (interface) {
472 nih_local char *var = NULL;
473 var = NIH_MUST (nih_sprintf (NULL, "INTERFACE=%s", interface));
474 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var));
475 }
476
477 if (path) {
478 nih_local char *var = NULL;
479 var = NIH_MUST (nih_sprintf (NULL, "PATH=%s", path));
480 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var));
481 }
482
483 if (sender) {
484 nih_local char *var = NULL;
485 var = NIH_MUST (nih_sprintf (NULL, "SENDER=%s", sender));
486 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var));
487 }
488
489 if (destination) {
490 nih_local char *var = NULL;
491 var = NIH_MUST (nih_sprintf (NULL, "DESTINATION=%s", destination));
492 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var));
493 }
494
495 nih_debug ("Received D-Bus signal: %s "
496 "(sender=%s, destination=%s, interface=%s, path=%s)",
497 signal ? signal : "",
498 sender ? sender : "",
499 destination ? destination : "",
500 interface ? interface : "",
501 path ? path : "");
502
503 pending_call = upstart_emit_event (upstart,
504 DBUS_EVENT, env, FALSE,
505 NULL, emit_event_error, NULL,
506 NIH_DBUS_TIMEOUT_NEVER);
507
508 if (! pending_call) {
509 NihError *err;
510 err = nih_error_get ();
511 nih_warn ("%s", err->message);
512 nih_free (err);
513 }
514
515 dbus_pending_call_unref (pending_call);
516
517out:
518 return DBUS_HANDLER_RESULT_HANDLED;
519}
520
521static void
522emit_event_error (void *data,
523 NihDBusMessage *message)
524{
525 NihError *err;
526
527 err = nih_error_get ();
528 nih_warn ("%s", err->message);
529 nih_free (err);
530}
531
532static void
533upstart_job_added (void *data,
534 NihDBusMessage *message,
535 const char *job_class_path)
536{
537 /* set to TRUE if jobs start/stop conditions specify
538 * DBUS_EVENT. Used to restrict emission of events
539 * unnecessarily. Note that event environment matching
540 * though is handled by Upstart.
541 */
542 int add = FALSE;
543
544 Job *job;
545 nih_local NihDBusProxy *job_class = NULL;
546 nih_local char ***start_on = NULL;
547 nih_local char ***stop_on = NULL;
548
549 nih_assert (job_class_path != NULL);
550
551 /* Obtain a proxy to the job */
552 job_class = nih_dbus_proxy_new (NULL, upstart->connection,
553 upstart->name, job_class_path,
554 NULL, NULL);
555 if (! job_class) {
556 NihError *err;
557
558 err = nih_error_get ();
559 nih_error ("Could not create proxy for job %s: %s",
560 job_class_path, err->message);
561 nih_free (err);
562
563 return;
564 }
565
566 job_class->auto_start = FALSE;
567
568 /* Obtain the start_on and stop_on properties of the job */
569 if (job_class_get_start_on_sync (NULL, job_class, &start_on) < 0) {
570 NihError *err;
571
572 err = nih_error_get ();
573 nih_error ("Could not obtain job start condition %s: %s",
574 job_class_path, err->message);
575 nih_free (err);
576
577 return;
578 }
579
580 if (job_class_get_stop_on_sync (NULL, job_class, &stop_on) < 0) {
581 NihError *err;
582
583 err = nih_error_get ();
584 nih_error ("Could not obtain job stop condition %s: %s",
585 job_class_path, err->message);
586 nih_free (err);
587
588 return;
589 }
590
591 /* Find out whether this job listens for any socket events */
592 for (char ***event = start_on; event && *event && **event; event++)
593 if (! strcmp (**event, DBUS_EVENT)) {
594 add = TRUE;
595 break;
596 }
597
598 for (char ***event = stop_on; ! add && event && *event && **event; event++)
599 if (! strcmp (**event, DBUS_EVENT)) {
600 add = TRUE;
601 break;
602 }
603
604 if (! add)
605 return;
606
607 nih_debug ("Job got added %s for event %s", job_class_path, DBUS_EVENT);
608
609 /* Free any existing record for the job (should never happen,
610 * but worth being safe).
611 */
612 job = (Job *)nih_hash_lookup (jobs, job_class_path);
613 if (job)
614 nih_free (job);
615
616 /* Create new record for the job */
617 job = NIH_MUST (nih_new (NULL, Job));
618 job->path = NIH_MUST (nih_strdup (job, job_class_path));
619
620 nih_list_init (&job->entry);
621 nih_alloc_set_destructor (job, nih_list_destroy);
622 nih_hash_add (jobs, &job->entry);
623}
624
625static void
626upstart_job_removed (void *data,
627 NihDBusMessage *message,
628 const char *job_path)
629{
630 Job *job;
631
632 nih_assert (job_path != NULL);
633
634 job = (Job *)nih_hash_lookup (jobs, job_path);
635 if (job) {
636 nih_debug ("Job went away %s", job_path);
637 nih_free (job);
638 }
639}
640

Subscribers

People subscribed via source and target branches