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

Proposed by James Hunt on 2013-07-30
Status: Merged
Merged at revision: 1519
Proposed branch: lp:~jamesodhunt/upstart/upstart-dconf-bridge
Merge into: lp:upstart
Diff against target: 922 lines (+826/-5)
7 files modified
ChangeLog (+46/-3)
configure.ac (+11/-0)
extra/Makefile.am (+33/-2)
extra/conf-session/upstart-dconf-bridge.conf (+10/-0)
extra/man/dconf-event.7 (+52/-0)
extra/man/upstart-dconf-bridge.8 (+66/-0)
extra/upstart-dconf-bridge.c (+608/-0)
To merge this branch: bzr merge lp:~jamesodhunt/upstart/upstart-dconf-bridge
Reviewer Review Type Date Requested Status
Upstart Reviewers 2013-07-30 Pending
Review via email: mp+177650@code.launchpad.net

Description of the change

New user bridge that emits Upstart events on dconf key changes.

To post a comment you must log in.
1472. By James Hunt on 2013-08-15

* extra/Makefile.am:
  - Add missing DCONF_CFLAGS.
  - Ensure upstart-dconf-bridge sources are always distributed,
    regardless of whether the local system is able to build it, or has
    disabled building it.

1473. By James Hunt on 2013-08-15

* Sync with lp:upstart.

1474. By James Hunt on 2013-08-15

* Sync with lp:upstart taking care to set extra/Makefile.am's EXTRA_DIST
  correctly.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2013-08-15 09:51:55 +0000
3+++ ChangeLog 2013-08-15 13:38:35 +0000
4@@ -2,9 +2,14 @@
5
6 * configure.ac:
7 - Allow udev bridge to be disabled.
8- * extra/Makefile.am: Ensure source for upstart-udev-bridge is
9- distributed, regardless of whether the local system is able to build
10- it, or has disabled building it.
11+ * extra/Makefile.am:
12+ - Ensure source for upstart-udev-bridge is distributed,
13+ regardless of whether the local system is able to build it,
14+ or has disabled building it.
15+ - Add missing DCONF_CFLAGS.
16+ - Ensure upstart-dconf-bridge sources are always distributed,
17+ regardless of whether the local system is able to build it, or has
18+ disabled building it.
19
20 2013-07-31 James Hunt <james.hunt@ubuntu.com>
21
22@@ -12,6 +17,18 @@
23 since the NIH main loop may be iterated once more before
24 shutdown.
25
26+2013-07-30 James Hunt <james.hunt@ubuntu.com>
27+
28+ * extra/man/dconf-event.7: New man page.
29+ * extra/man/upstart-dconf-bridge.8: New man page.
30+ * extra/upstart-dconf-bridge.c: Bridge will now only emit
31+ events on dconf changes if any jobs care about them
32+ (unless --always is specified).
33+ * extra/conf-session/upstart-dconf-bridge.conf: Sample conf
34+ file for dconf bridge.
35+ * extra/Makefile.am: Added dconf bridge man pages and sample
36+ conf to distribution.
37+
38 2013-07-25 James Hunt <james.hunt@ubuntu.com>
39
40 * extra/Makefile.am: Renamed to upstart-local-bridge.
41@@ -124,6 +141,32 @@
42 part of the earlier Session deserialisation. (LP: #1199778)
43 * init/state.c: Formatting.
44
45+2013-07-09 James Hunt <james.hunt@ubuntu.com>
46+
47+ * {configure.ac,Makefile.am}: Allow upstart-dconf-bridge to be
48+ disabled even if dependencies are available (--disable-dconf-bridge).
49+ * extra/upstart-dconf-bridge.c: Changed event name to simply 'dconf'
50+ with fixed first argument of 'TYPE=changed' for consistency with
51+ other bridge events and to accommodate future dconf API changes.
52+
53+2013-07-08 James Hunt <james.hunt@ubuntu.com>
54+
55+ * extra/upstart-dconf-bridge.c: dconf_changed():
56+ - Simplified logic after desrt clarified 'changed' signal
57+ behaviour.
58+
59+2013-07-05 James Hunt <james.hunt@ubuntu.com>
60+
61+ * extra/upstart-dconf-bridge.c:
62+ - Added DCONF_EVENT.
63+ - Reformatted to be more consistent with other bridges.
64+ - main():
65+ - Connect to UPSTART_SESSION rather than D-Bus session bus.
66+ - Handle generation of pidfile name.
67+ - dconf_changed():
68+ - Correct path logic and ensure that if the changes array contains
69+ values that they are separated correctly from the prefix.
70+
71 2013-07-04 James Hunt <james.hunt@ubuntu.com>
72
73 * NEWS: Release 1.9.1
74
75=== modified file 'configure.ac'
76--- configure.ac 2013-08-15 09:51:55 +0000
77+++ configure.ac 2013-08-15 13:38:35 +0000
78@@ -30,7 +30,9 @@
79 PKG_CHECK_MODULES([NIH], [libnih >= 1.0.2])
80 PKG_CHECK_MODULES([NIH_DBUS], [libnih-dbus >= 1.0.0])
81 PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16])
82+PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.36], [have_gio=yes], [have_gio=no])
83 PKG_CHECK_MODULES([UDEV], [libudev >= 146], [have_udev=yes], [have_udev=no])
84+PKG_CHECK_MODULES([DCONF], [dconf >= 0.14], [have_dconf=yes], [have_dconf=no])
85
86 AC_ARG_ENABLE([udev-bridge],
87 AS_HELP_STRING([--disable-udev-bridge],
88@@ -39,6 +41,15 @@
89
90 AM_CONDITIONAL([ENABLE_UDEV_BRIDGE], [test "$have_udev" = yes && test "$udev_bridge" = yes])
91
92+AC_ARG_ENABLE([dconf-bridge],
93+ AS_HELP_STRING([--disable-dconf-bridge],
94+ [Disable building of upstart-dconf-bridge even if required dependencies available]),
95+ [dconf_bridge=no], [dconf_bridge=yes])
96+
97+AM_CONDITIONAL([ENABLE_DCONF_BRIDGE], [test "$have_dconf" = yes &&
98+ test "$have_gio" = yes &&
99+ test "$dconf_bridge" = yes])
100+
101 # Reasons for requiring this library version:
102 #
103 # 1) RFC 4627, the JSON "memo" (it is *NOT* a standard!) helpfully fails
104
105=== modified file 'extra/Makefile.am'
106--- extra/Makefile.am 2013-08-15 09:51:55 +0000
107+++ extra/Makefile.am 2013-08-15 13:38:35 +0000
108@@ -1,10 +1,14 @@
109 ## Process this file with automake to produce Makefile.in
110
111+# Required to allow conditional appends below
112+EXTRA_DIST =
113+
114 AM_CFLAGS = \
115 $(NIH_CFLAGS) \
116 $(NIH_DBUS_CFLAGS) \
117 $(DBUS_CFLAGS) \
118- $(UDEV_CFLAGS)
119+ $(UDEV_CFLAGS) \
120+ $(DCONF_CFLAGS)
121
122 AM_CPPFLAGS = \
123 -DLOCALEDIR="\"$(localedir)\"" \
124@@ -102,6 +106,33 @@
125 $(NIH_DBUS_LIBS) \
126 $(DBUS_LIBS)
127
128+dist_sessions_DATA += \
129+ conf-session/upstart-dconf-bridge.conf
130+
131+dist_man_MANS += \
132+ man/upstart-dconf-bridge.8 \
133+ man/dconf-event.7
134+
135+if ENABLE_DCONF_BRIDGE
136+sbin_PROGRAMS += \
137+ upstart-dconf-bridge
138+
139+upstart_dconf_bridge_SOURCES = \
140+ upstart-dconf-bridge.c
141+nodist_upstart_dconf_bridge_SOURCES = \
142+ $(com_ubuntu_Upstart_OUTPUTS)
143+upstart_dconf_bridge_LDADD = \
144+ $(LTLIBINTL) \
145+ $(NIH_LIBS) \
146+ $(NIH_DBUS_LIBS) \
147+ $(DBUS_LIBS) \
148+ $(GIO_LIBS) \
149+ $(DCONF_LIBS)
150+else
151+EXTRA_DIST += \
152+ upstart-dconf-bridge.c
153+endif
154+
155 dist_init_DATA += \
156 conf/upstart-udev-bridge.conf
157
158@@ -136,7 +167,7 @@
159 done
160
161 else
162-EXTRA_DIST = \
163+EXTRA_DIST += \
164 upstart-udev-bridge.c \
165 man/upstart-udev-bridge.8
166 endif
167
168=== added file 'extra/conf-session/upstart-dconf-bridge.conf'
169--- extra/conf-session/upstart-dconf-bridge.conf 1970-01-01 00:00:00 +0000
170+++ extra/conf-session/upstart-dconf-bridge.conf 2013-08-15 13:38:35 +0000
171@@ -0,0 +1,10 @@
172+description "Bridge dconf events into session upstart"
173+
174+emits dconf
175+
176+start on started dbus
177+stop on stopped dbus
178+
179+respawn
180+
181+exec upstart-dconf-bridge
182
183=== added file 'extra/man/dconf-event.7'
184--- extra/man/dconf-event.7 1970-01-01 00:00:00 +0000
185+++ extra/man/dconf-event.7 2013-08-15 13:38:35 +0000
186@@ -0,0 +1,52 @@
187+.TH dconf\-event 7 2013-07-09 upstart
188+.\"
189+.SH NAME
190+dconf \- event signalling that a dconf key has been changed
191+.\"
192+.SH SYNOPSIS
193+.B dconf
194+.BI TYPE\fR= changed
195+.BI KEY\fR= KEY
196+.BI VALUE\fR= VALUE
197+.\"
198+.SH DESCRIPTION
199+
200+The
201+.B dconf
202+event is generated by the
203+.BR upstart\-dconf\-bridge (8)
204+daemon when a dconf key changes whose details match the
205+.I dconf
206+event condition and environment specified in a job's
207+.B start on
208+or
209+.B stop on
210+stanza.
211+
212+.\"
213+.SH EXAMPLES
214+.\"
215+.IP "start on dconf TYPE=changed KEY=/desktop/gnome/remote-access/notify-on-connect VALUE=true"
216+
217+Start job when the user allows remote access to their desktop.
218+.\"
219+.SH AUTHOR
220+Written by James Hunt
221+.RB < james.hunt@canonical.com >
222+.\"
223+.SH BUGS
224+Report bugs at
225+.RB < https://launchpad.net/upstart/+bugs >
226+.\"
227+.SH COPYRIGHT
228+Copyright \(co 2013 Canonical Ltd.
229+.PP
230+This is free software; see the source for copying conditions. There is NO
231+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
232+.\"
233+.SH SEE ALSO
234+.BR dconf (7)
235+.BR gsettings (1)
236+.BR init (5)
237+.BR init (8)
238+.BR upstart\-dconf\-bridge (8)
239
240=== added file 'extra/man/upstart-dconf-bridge.8'
241--- extra/man/upstart-dconf-bridge.8 1970-01-01 00:00:00 +0000
242+++ extra/man/upstart-dconf-bridge.8 2013-08-15 13:38:35 +0000
243@@ -0,0 +1,66 @@
244+.TH upstart\-dconf\-bridge 8 2013-07-09 upstart
245+.\"
246+.SH NAME
247+upstart\-dconf\-bridge \- Bridge between Upstart and dconf/gsettings
248+.\"
249+.SH SYNOPSIS
250+.B upstart\-dconf\-bridge
251+.RI [ OPTIONS ]...
252+.\"
253+.SH DESCRIPTION
254+.B upstart\-dconf\-bridge
255+receives information about dconf/gsettings changes
256+and creates
257+.BR init (8)
258+events for them.
259+
260+With no options (and if there are jobs which have registered an interest
261+in the event), monitors dconf changes and emits
262+an Upstart event called
263+.I dconf
264+with details of the dconf change.
265+
266+See \fBdconf\fP(7) and for further details.
267+
268+.\"
269+.SH OPTIONS
270+.\"
271+.TP
272+.B \-\-always
273+Always emit events on receipt of dconf changes regardless of whether jobs
274+care about them.
275+.TP
276+.B \-\-daemon
277+Detach and run in the background.
278+.\"
279+.TP
280+.B \-\-debug
281+Enable debugging output.
282+.\"
283+.TP
284+.B \-\-help
285+Show brief usage summary.
286+.\"
287+.TP
288+.B \-\-verbose
289+Enable verbose output.
290+.\"
291+.SH AUTHOR
292+Written by James Hunt
293+.RB < james.hunt@canonical.com >
294+.\"
295+.SH BUGS
296+Report bugs at
297+.RB < https://launchpad.net/ubuntu/+source/upstart/+bugs >
298+.\"
299+.SH COPYRIGHT
300+Copyright \(co 2013 Canonical Ltd.
301+.PP
302+This is free software; see the source for copying conditions. There is NO
303+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
304+.SH SEE ALSO
305+.BR dconf (7)
306+.BR dconf\-event (7)
307+.BR gsettings (1)
308+.BR init (5)
309+.BR init (8)
310
311=== added file 'extra/upstart-dconf-bridge.c'
312--- extra/upstart-dconf-bridge.c 1970-01-01 00:00:00 +0000
313+++ extra/upstart-dconf-bridge.c 2013-08-15 13:38:35 +0000
314@@ -0,0 +1,608 @@
315+/* upstart-dconf-bridge
316+ *
317+ * Copyright © 2012-2013 Canonical Ltd.
318+ * Author: Stéphane Graber <stgraber@ubuntu.com>.
319+ * Author: Thomas Bechtold <thomasbechtold@jpberlin.de>.
320+ * Author: James Hunt <james.hunt@ubuntu.com>.
321+ *
322+ * This program is free software; you can redistribute it and/or modify
323+ * it under the terms of the GNU General Public License version 2, as
324+ * published by the Free Software Foundation.
325+ *
326+ * This program is distributed in the hope that it will be useful,
327+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
328+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
329+ * GNU General Public License for more details.
330+ *
331+ * You should have received a copy of the GNU General Public License along
332+ * with this program; if not, write to the Free Software Foundation, Inc.,
333+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
334+ */
335+
336+#ifdef HAVE_CONFIG_H
337+# include <config.h>
338+#endif /* HAVE_CONFIG_H */
339+
340+#include <dconf.h>
341+#include <gio/gio.h>
342+
343+#include <stdlib.h>
344+#include <string.h>
345+#include <syslog.h>
346+#include <ctype.h>
347+
348+#include <nih/macros.h>
349+#include <nih/alloc.h>
350+#include <nih/string.h>
351+#include <nih/hash.h>
352+#include <nih/io.h>
353+#include <nih/option.h>
354+#include <nih/main.h>
355+#include <nih/logging.h>
356+#include <nih/error.h>
357+
358+#include <nih-dbus/dbus_connection.h>
359+#include <nih-dbus/dbus_proxy.h>
360+
361+#include "dbus/upstart.h"
362+#include "com.ubuntu.Upstart.h"
363+#include "com.ubuntu.Upstart.Job.h"
364+
365+/**
366+ * DCONF_EVENT:
367+ *
368+ * Name of event this program emits.
369+ **/
370+#define DCONF_EVENT "dconf"
371+
372+/* Prototypes for static functions */
373+static void dconf_changed (DConfClient *client, const gchar *prefix,
374+ const gchar * const *changes, const gchar *tag,
375+ GDBusProxy *upstart);
376+
377+static void handle_upstart_job (GDBusProxy *proxy, gchar *sender_name,
378+ gchar *signal_name, GVariant *parameters,
379+ gpointer user_data);
380+
381+static int handle_existing_jobs (GDBusProxy *upstart_proxy)
382+ __attribute__ ((warn_unused_result));
383+
384+static int job_needs_event (const char *object_path)
385+ __attribute__ ((warn_unused_result));
386+
387+static int jobs_need_event (void)
388+ __attribute__ ((warn_unused_result));
389+
390+/**
391+ * Structure we use for tracking jobs
392+ *
393+ * @entry: list header,
394+ * @path: D-Bus path of job being tracked.
395+ **/
396+typedef struct job {
397+ NihList entry;
398+ char *path;
399+} Job;
400+
401+/**
402+ * daemonise:
403+ *
404+ * Set to TRUE if we should become a daemon, rather than just running
405+ * in the foreground.
406+ **/
407+static int daemonise = FALSE;
408+
409+/**
410+ * always:
411+ *
412+ * If TRUE, always emit Upstart events, regardless of whether
413+ * existing jobs care about DBUS_EVENT.
414+ */
415+static int always = FALSE;
416+
417+/**
418+ * jobs:
419+ *
420+ * Jobs that we're monitoring.
421+ **/
422+static NihHash *jobs = NULL;
423+
424+/**
425+ * connection:
426+ *
427+ * D-Bus connection to Upstart.
428+ **/
429+GDBusConnection *connection = NULL;
430+
431+/**
432+ * options:
433+ *
434+ * Command-line options accepted by this program.
435+ **/
436+static NihOption options[] = {
437+ { 0, "always", N_("Always emit an event on a dconf change"),
438+ NULL, NULL, &always, NULL },
439+ { 0, "daemon", N_("Detach and run in the background"),
440+ NULL, NULL, &daemonise, NULL },
441+ NIH_OPTION_LAST
442+};
443+
444+int
445+main (int argc,
446+ char *argv[])
447+{
448+ char **args;
449+ DConfClient *client;
450+ GMainLoop *mainloop;
451+ GDBusProxy *upstart_proxy;
452+ GError *error = NULL;
453+ char *user_session_addr = NULL;
454+ nih_local char **user_session_path = NULL;
455+ char *path_element = NULL;
456+ char *pidfile_path = NULL;
457+ char *pidfile = NULL;
458+
459+ client = dconf_client_new ();
460+ mainloop = g_main_loop_new (NULL, FALSE);
461+
462+ /* Use NIH to parse the arguments */
463+ nih_main_init (argv[0]);
464+
465+ nih_option_set_synopsis (_("Bridge dconf events into upstart"));
466+ nih_option_set_help (
467+ _("By default, upstart-dconf-bridge does not detach from the "
468+ "console and remains in the foreground. Use the --daemon "
469+ "option to have it detach."));
470+
471+ args = nih_option_parser (NULL, argc, argv, options, FALSE);
472+ if (! args)
473+ exit (1);
474+
475+ user_session_addr = getenv ("UPSTART_SESSION");
476+ if (! user_session_addr) {
477+ nih_fatal (_("UPSTART_SESSION isn't set in environment"));
478+ exit (1);
479+ }
480+
481+ /* Connect to the Upstart session */
482+ connection = g_dbus_connection_new_for_address_sync (user_session_addr,
483+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
484+ NULL, /* GDBusAuthObserver*/
485+ NULL, /* GCancellable */
486+ &error);
487+
488+ if (! connection) {
489+ g_error ("D-BUS Upstart session init error: %s",
490+ (error && error->message) ? error->message : "Unknown error");
491+ g_clear_error (&error);
492+ exit (1);
493+ }
494+
495+ /* Allocate jobs hash table */
496+ jobs = NIH_MUST (nih_hash_string_new (NULL, 0));
497+
498+ /* Get an Upstart proxy object */
499+ upstart_proxy = g_dbus_proxy_new_sync (connection,
500+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
501+ NULL, /* GDBusInterfaceInfo */
502+ NULL, /* name */
503+ "/com/ubuntu/Upstart",
504+ "com.ubuntu.Upstart0_6",
505+ NULL, /* GCancellable */
506+ &error);
507+
508+ if (! upstart_proxy) {
509+ g_error ("D-BUS Upstart proxy error: %s",
510+ (error && error->message) ? error->message : "Unknown error");
511+ g_clear_error (&error);
512+ exit (1);
513+ }
514+
515+ /* Connect signal to be notified when jobs come and go */
516+ g_signal_connect (upstart_proxy, "g-signal", (GCallback) handle_upstart_job, NULL);
517+
518+ if (! handle_existing_jobs (upstart_proxy))
519+ exit (1);
520+
521+ if (daemonise) {
522+ /* Deal with the pidfile location when becoming a daemon.
523+ * We need to be able to run one bridge per upstart daemon.
524+ * Store the PID file in XDG_RUNTIME_DIR or HOME and include the pid of
525+ * the Upstart instance (last part of the DBus path) in the filename.
526+ */
527+
528+ /* Extract PID from UPSTART_SESSION */
529+ user_session_path = nih_str_split (NULL, user_session_addr, "/", TRUE);
530+
531+ for (int i = 0; user_session_path && user_session_path[i]; i++)
532+ path_element = user_session_path[i];
533+
534+ if (! path_element) {
535+ nih_fatal (_("Invalid value for UPSTART_SESSION"));
536+ exit (1);
537+ }
538+
539+ pidfile_path = getenv ("XDG_RUNTIME_DIR");
540+ if (!pidfile_path)
541+ pidfile_path = getenv ("HOME");
542+
543+ if (pidfile_path) {
544+ NIH_MUST (nih_strcat_sprintf (&pidfile, NULL,
545+ "%s/upstart-dconf-bridge.%s.pid",
546+ pidfile_path, path_element));
547+ nih_main_set_pidfile (pidfile);
548+ }
549+
550+ if (nih_main_daemonise () < 0) {
551+ NihError *err;
552+
553+ err = nih_error_get ();
554+ nih_fatal ("%s: %s", _("Unable to become daemon"),
555+ err->message);
556+ nih_free (err);
557+
558+ exit (1);
559+ }
560+ }
561+
562+ /* Handle TERM and INT signals gracefully */
563+ nih_signal_set_handler (SIGTERM, nih_signal_handler);
564+ NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, nih_main_term_signal, NULL));
565+
566+ /* Listen for any dconf change */
567+ g_signal_connect (client, "changed", (GCallback) dconf_changed, upstart_proxy);
568+ dconf_client_watch_sync (client, "/");
569+
570+ /* Start the glib mainloop */
571+ g_main_loop_run (mainloop);
572+
573+ g_object_unref (client);
574+ g_object_unref (upstart_proxy);
575+ g_object_unref (connection);
576+ g_main_loop_unref (mainloop);
577+
578+ exit (0);
579+}
580+
581+/**
582+ * handle_upstart_job:
583+ *
584+ * Called when an Upstart D-Bus signal is emitted.
585+ **/
586+static void
587+handle_upstart_job (GDBusProxy *proxy,
588+ gchar *sender_name,
589+ gchar *signal_name,
590+ GVariant *parameters,
591+ gpointer user_data)
592+{
593+ GVariantIter iter;
594+ GVariant *child;
595+ const gchar *job_class_path;
596+ Job *job;
597+ int add;
598+
599+ nih_assert (signal_name);
600+ nih_assert (parameters);
601+ nih_assert (jobs);
602+
603+ if (! strcmp (signal_name, "JobAdded")) {
604+ add = 1;
605+ } else if (! strcmp (signal_name, "JobRemoved")) {
606+ add = 0;
607+ } else {
608+ return;
609+ }
610+
611+ nih_assert (g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
612+
613+ g_variant_iter_init (&iter, parameters);
614+
615+ nih_assert (g_variant_iter_n_children (&iter) == 1);
616+
617+ child = g_variant_iter_next_value (&iter);
618+
619+ job_class_path = g_variant_get_string (child, NULL);
620+ nih_assert (g_variant_is_object_path (job_class_path));
621+
622+ /* Free any existing record for the job if we are adding
623+ * (should never happen, but worth being safe).
624+ */
625+ job = (Job *)nih_hash_lookup (jobs, job_class_path);
626+ if (job)
627+ nih_free (job);
628+
629+ /* Job isn't interested in DCONF_EVENT */
630+ if (add && ! job_needs_event (job_class_path))
631+ goto out;
632+
633+ if (add)
634+ nih_debug ("Job got added %s for event %s", job_class_path, DCONF_EVENT);
635+ else
636+ nih_debug ("Job went away %s", job_class_path);
637+
638+ /* We're removing, so job done */
639+ if (! add)
640+ goto out;
641+
642+ /* Create new record for the job */
643+ job = NIH_MUST (nih_new (NULL, Job));
644+ job->path = NIH_MUST (nih_strdup (job, job_class_path));
645+
646+ nih_list_init (&job->entry);
647+ nih_alloc_set_destructor (job, nih_list_destroy);
648+ nih_hash_add (jobs, &job->entry);
649+
650+out:
651+ g_variant_unref (child);
652+}
653+
654+/**
655+ * dconf_changed:
656+ *
657+ * Emit an Upstart event corresponding to a dconf key change.
658+ **/
659+static void
660+dconf_changed (DConfClient *client,
661+ const gchar *prefix,
662+ const gchar * const *changes,
663+ const gchar *tag,
664+ GDBusProxy *upstart)
665+{
666+ GVariant *value;
667+ gchar *value_str = NULL;
668+ gchar *path = NULL;
669+ gchar *env_key = NULL;
670+ gchar *env_value = NULL;
671+ GVariant *event;
672+ GVariantBuilder builder;
673+ int i = 0;
674+
675+ /* dconf currently only currently supports the changed signal,
676+ * but parameterise to allow for a future API change.
677+ */
678+ const gchar *event_type = "TYPE=changed";
679+
680+ if (! jobs_need_event () && ! always)
681+ return;
682+
683+ /* Iterate through the various changes */
684+ while (changes[i] != NULL) {
685+ path = g_strconcat (prefix, changes[i], NULL);
686+
687+ value = dconf_client_read (client, path);
688+ value_str = g_variant_print (value, FALSE);
689+
690+ env_key = g_strconcat ("KEY=", path, NULL);
691+ env_value = g_strconcat ("VALUE=", value_str, NULL);
692+
693+ /* Build event environment as GVariant */
694+ g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
695+
696+ g_variant_builder_add (&builder, "s", DCONF_EVENT);
697+
698+ g_variant_builder_open (&builder, G_VARIANT_TYPE_ARRAY);
699+ g_variant_builder_add (&builder, "s", event_type);
700+ g_variant_builder_add (&builder, "s", env_key);
701+ g_variant_builder_add (&builder, "s", env_value);
702+ g_variant_builder_close (&builder);
703+
704+ g_variant_builder_add (&builder, "b", FALSE);
705+ event = g_variant_builder_end (&builder);
706+
707+ /* Send the event */
708+ g_dbus_proxy_call (upstart,
709+ "EmitEvent",
710+ event,
711+ G_DBUS_CALL_FLAGS_NONE,
712+ -1,
713+ NULL,
714+ NULL, /* GAsyncReadyCallback
715+ we don't care about the answer */
716+ NULL);
717+
718+ g_variant_builder_clear (&builder);
719+ g_variant_unref (value);
720+ g_free (path);
721+ g_free (value_str);
722+ g_free (env_key);
723+ g_free (env_value);
724+
725+ i += 1;
726+ }
727+}
728+
729+/**
730+ * jobs_need_event:
731+ *
732+ * Returns: TRUE if any jobs need DCONF_EVENT, else FALSE.
733+ **/
734+static int
735+jobs_need_event (void)
736+{
737+ NIH_HASH_FOREACH (jobs, iter) {
738+ return TRUE;
739+ }
740+
741+ return FALSE;
742+}
743+
744+/**
745+ * job_needs_event:
746+ * @object_path: Full D-Bus object path for job.
747+ *
748+ * Returns: TRUE if job specified by @object_path specifies DCONF_EVENT
749+ * in its 'start on' or 'stop on' stanza, else FALSE.
750+ **/
751+static int
752+job_needs_event (const char *class_path)
753+{
754+ GDBusProxy *job_proxy;
755+ GError *error = NULL;
756+ GVariantIter iter;
757+ const gchar *event_name;
758+ int ret = FALSE;
759+
760+ /* Arrays of arrays of strings (aas) */
761+ GVariant *start_on = NULL;
762+ GVariant *stop_on = NULL;
763+
764+ /* Array containing event name and optional environment
765+ * variable elements.
766+ */
767+ GVariant *event_element;
768+
769+ /* Either an event name or "/AND" or "/OR" */
770+ GVariant *event;
771+
772+ nih_assert (class_path);
773+
774+ job_proxy = g_dbus_proxy_new_sync (connection,
775+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
776+ NULL, /* GDBusInterfaceInfo */
777+ NULL, /* name */
778+ class_path,
779+ "com.ubuntu.Upstart0_6.Job",
780+ NULL, /* GCancellable */
781+ &error);
782+
783+ start_on = g_dbus_proxy_get_cached_property (job_proxy, "start_on");
784+ nih_assert (g_variant_is_of_type (start_on, G_VARIANT_TYPE_ARRAY));
785+
786+ g_variant_iter_init (&iter, start_on);
787+
788+ while ((event_element = g_variant_iter_next_value (&iter))) {
789+ nih_assert (g_variant_is_of_type (event_element, G_VARIANT_TYPE_ARRAY));
790+
791+ /* First element is always the event name */
792+ event = g_variant_get_child_value (event_element, 0);
793+ nih_assert (g_variant_is_of_type (event, G_VARIANT_TYPE_STRING));
794+
795+ event_name = g_variant_get_string (event, NULL);
796+
797+ if (! strcmp (event_name, DCONF_EVENT))
798+ ret = TRUE;
799+
800+ g_variant_unref (event_element);
801+ g_variant_unref (event);
802+
803+ if (ret)
804+ goto out;
805+ }
806+
807+ /* Now handle stop on */
808+ stop_on = g_dbus_proxy_get_cached_property (job_proxy, "stop_on");
809+ nih_assert (g_variant_is_of_type (stop_on, G_VARIANT_TYPE_ARRAY));
810+
811+ g_variant_iter_init (&iter, stop_on);
812+
813+ while ((event_element = g_variant_iter_next_value (&iter))) {
814+ nih_assert (g_variant_is_of_type (event_element, G_VARIANT_TYPE_ARRAY));
815+
816+ /* First element is always the event name */
817+ event = g_variant_get_child_value (event_element, 0);
818+ nih_assert (g_variant_is_of_type (event, G_VARIANT_TYPE_STRING));
819+
820+ event_name = g_variant_get_string (event, NULL);
821+
822+ if (! strcmp (event_name, DCONF_EVENT))
823+ ret = TRUE;
824+
825+ g_variant_unref (event_element);
826+ g_variant_unref (event);
827+
828+ if (ret)
829+ goto out;
830+ }
831+
832+out:
833+ if (start_on)
834+ g_variant_unref (start_on);
835+
836+ if (stop_on)
837+ g_variant_unref (stop_on);
838+
839+ g_object_unref (job_proxy);
840+
841+ return ret;
842+}
843+
844+/**
845+ * handle_existing_jobs:
846+ *
847+ * @upstart_proxy: Upstart proxy.
848+ *
849+ * Add all existing jobs which specify DCONF_EVENT to the list
850+ * of tracked jobs.
851+ *
852+ * Returns: TRUE or FALSE on error.
853+ **/
854+static int
855+handle_existing_jobs (GDBusProxy *upstart_proxy)
856+{
857+ GVariant *result;
858+ GVariant *child;
859+ GVariant *proxy_job;
860+ const gchar *job_class_path;
861+ GError *error = NULL;
862+ GVariantIter iter;
863+ Job *job;
864+
865+ nih_assert (upstart_proxy);
866+
867+ result = g_dbus_proxy_call_sync (upstart_proxy,
868+ "GetAllJobs",
869+ NULL,
870+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
871+ -1,
872+ NULL,
873+ &error);
874+
875+ if (! result) {
876+ g_error ("D-BUS Upstart proxy error: %s",
877+ (error && error->message)
878+ ? error->message
879+ : "Unknown error");
880+ g_clear_error (&error);
881+ return FALSE;
882+ }
883+
884+ nih_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_TUPLE));
885+ nih_assert (g_variant_n_children (result) == 1);
886+
887+ child = g_variant_get_child_value (result, 0);
888+
889+ nih_assert (g_variant_is_of_type (child, G_VARIANT_TYPE_OBJECT_PATH_ARRAY));
890+
891+ g_variant_iter_init (&iter, child);
892+
893+ while ((proxy_job = g_variant_iter_next_value (&iter))) {
894+ job_class_path = g_variant_get_string (proxy_job, NULL);
895+
896+ /* Free any existing record for the job if we are adding
897+ * (should never happen, but worth being safe).
898+ */
899+ job = (Job *)nih_hash_lookup (jobs, job_class_path);
900+ if (job)
901+ nih_free (job);
902+
903+ if (job_needs_event (job_class_path)) {
904+ /* Create new record for the job */
905+ job = NIH_MUST (nih_new (NULL, Job));
906+ job->path = NIH_MUST (nih_strdup (job, job_class_path));
907+
908+ nih_list_init (&job->entry);
909+ nih_alloc_set_destructor (job, nih_list_destroy);
910+ nih_hash_add (jobs, &job->entry);
911+
912+ nih_debug ("Job added %s for event %s", job_class_path, DCONF_EVENT);
913+ }
914+
915+ g_variant_unref (proxy_job);
916+ }
917+
918+ g_variant_unref (child);
919+ g_variant_unref (result);
920+
921+ return TRUE;
922+}

Subscribers

People subscribed via source and target branches