Dimitri John Ledkov has proposed merging lp:~xnox/upstart/systemd-local-bridge 
into lp:upstart.

Requested reviews:
  Upstart Reviewers (upstart-reviewers)

For more details, see:
https://code.launchpad.net/~xnox/upstart/systemd-local-bridge/+merge/246726
-- 
Your team Upstart Reviewers is requested to review the proposed merge of 
lp:~xnox/upstart/systemd-local-bridge into lp:upstart.
=== modified file 'extra/Makefile.am'
--- extra/Makefile.am	2013-10-31 18:19:46 +0000
+++ extra/Makefile.am	2015-01-16 15:15:53 +0000
@@ -23,17 +23,18 @@
 	conf-session/upstart-event-bridge.conf \
 	conf-session/upstart-file-bridge.conf \
 	conf-session/re-exec.conf \
+	conf-session/upstart-udev-bridge.conf \
 	conf-session/upstart-dbus-session-bridge.conf \
 	conf-session/upstart-dbus-system-bridge.conf
 
 sbin_PROGRAMS = \
 	upstart-event-bridge \
+	upstart-systemd-job-event-bridge \
 	upstart-file-bridge \
 	upstart-dbus-bridge
 
 dist_init_DATA = \
 	conf/upstart-socket-bridge.conf \
-	conf/upstart-event-bridge.conf \
 	conf/upstart-file-bridge.conf \
 	conf/upstart-dbus-bridge.conf
 
@@ -77,6 +78,19 @@
 	$(NIH_DBUS_LIBS) \
 	$(DBUS_LIBS)
 
+upstart_systemd_job_event_bridge_SOURCES = \
+	upstart-systemd-job-event-bridge.c
+nodist_upstart_systemd_job_event_bridge_SOURCES = \
+	$(com_ubuntu_Upstart_OUTPUTS) \
+	$(com_ubuntu_Upstart_Job_OUTPUTS) \
+	$(org_freedesktop_systemd1_OUTPUTS) \
+	$(org_freedesktop_systemd1_Job_OUTPUTS)
+upstart_systemd_job_event_bridge_LDADD = \
+	$(LTLIBINTL) \
+	$(NIH_LIBS) \
+	$(NIH_DBUS_LIBS) \
+	$(DBUS_LIBS)
+
 upstart_file_bridge_SOURCES = \
 	upstart-file-bridge.c
 nodist_upstart_file_bridge_SOURCES = \
@@ -106,6 +120,7 @@
 upstart_local_bridge_SOURCES = \
 	upstart-local-bridge.c
 nodist_upstart_local_bridge_SOURCES = \
+	$(org_freedesktop_systemd1_OUTPUTS) \
 	$(com_ubuntu_Upstart_OUTPUTS) \
 	$(com_ubuntu_Upstart_Job_OUTPUTS)
 upstart_local_bridge_LDADD = \
@@ -224,15 +239,49 @@
 		--output=$@ $<
 
 
+org_freedesktop_systemd1_OUTPUTS = \
+	org.freedesktop.systemd1.c \
+	org.freedesktop.systemd1.h
+
+org_freedesktop_systemd1_XML = \
+	org.freedesktop.systemd1.xml
+
+$(org_freedesktop_systemd1_OUTPUTS): $(org_freedesktop_systemd1_XML)
+	$(AM_V_GEN)$(NIH_DBUS_TOOL) \
+		--package=$(PACKAGE) \
+		--mode=proxy --prefix=systemd \
+		--default-interface=org.freedesktop.systemd1.Manager \
+		--output=$@ $<
+
+
+org_freedesktop_systemd1_Job_OUTPUTS = \
+	org.freedesktop.systemd1.job.c \
+	org.freedesktop.systemd1.job.h
+
+org_freedesktop_systemd1_Job_XML = \
+	org.freedesktop.systemd1.Job.xml
+
+$(org_freedesktop_systemd1_Job_OUTPUTS): $(org_freedesktop_systemd1_Job_XML)
+	$(AM_V_GEN)$(NIH_DBUS_TOOL) \
+		--package=$(PACKAGE) \
+		--mode=proxy --prefix=systemd_job \
+		--default-interface=org.freedesktop.systemd1.Job \
+		--output=$@ $<
+
+
 # These have to be built sources because we can't compile object files
 # without the header file existing first
 BUILT_SOURCES = \
 	$(com_ubuntu_Upstart_OUTPUTS) \
-	$(com_ubuntu_Upstart_Job_OUTPUTS)
+	$(com_ubuntu_Upstart_Job_OUTPUTS) \
+	$(org_freedesktop_systemd1_OUTPUTS) \
+	$(org_freedesktop_systemd1_Job_OUTPUTS)
 
 CLEANFILES = \
 	$(com_ubuntu_Upstart_OUTPUTS) \
-	$(com_ubuntu_Upstart_Job_OUTPUTS)
+	$(com_ubuntu_Upstart_Job_OUTPUTS) \
+	$(org_freedesktop_systemd1_OUTPUTS) \
+	$(org_freedesktop_systemd1_Job_OUTPUTS)
 
 
 clean-local:

=== added file 'extra/conf-session/upstart-udev-bridge.conf'
--- extra/conf-session/upstart-udev-bridge.conf	1970-01-01 00:00:00 +0000
+++ extra/conf-session/upstart-udev-bridge.conf	2015-01-16 15:15:53 +0000
@@ -0,0 +1,22 @@
+# upstart-udev-bridge - Bridge udev events into session upstart
+#
+# This helper daemon receives udev events from the netlink socket and
+# emits equivalent Upstart events.
+
+description	"Bridge udev events into upstart"
+
+# From upstart-udev-bridge itself
+emits *-device-added
+emits *-device-removed
+emits *-device-changed
+# From http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/libudev-udev-device.html
+emits *-device-online
+emits *-device-offline
+
+start on starting udev
+stop on stopped udev
+
+expect daemon
+respawn
+
+exec upstart-udev-bridge --daemon --user

=== removed file 'extra/conf/upstart-event-bridge.conf'
--- extra/conf/upstart-event-bridge.conf	2013-01-22 20:08:29 +0000
+++ extra/conf/upstart-event-bridge.conf	1970-01-01 00:00:00 +0000
@@ -1,15 +0,0 @@
-# upstart-event-bridge - Bridge system upstarts events into session upstart
-#
-# This helper daemon receives system upstart events from the DBus system bus
-# and emits equivalent events (with a :sys:) prefix to the session bus
-
-description	"Bridge Upstart system events into session Upstart"
-
-emits :sys:*
-
-start on started dbus
-stop on stopped dbus
-
-respawn
-
-exec upstart-event-bridge

=== added file 'extra/org.freedesktop.systemd1.Job.xml'
--- extra/org.freedesktop.systemd1.Job.xml	1970-01-01 00:00:00 +0000
+++ extra/org.freedesktop.systemd1.Job.xml	2015-01-16 15:15:53 +0000
@@ -0,0 +1,11 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+<node>
+ <interface name="org.freedesktop.systemd1.Job">
+  <method name="Cancel"/>
+  <property name="Id" type="u" access="read"/>
+  <property name="Unit" type="(so)" access="read"/>
+  <property name="JobType" type="s" access="read"/>
+  <property name="State" type="s" access="read"/>
+ </interface>
+</node>

=== added file 'extra/org.freedesktop.systemd1.xml'
--- extra/org.freedesktop.systemd1.xml	1970-01-01 00:00:00 +0000
+++ extra/org.freedesktop.systemd1.xml	2015-01-16 15:15:53 +0000
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+     org.freedesktop.systemd1- interface definition for systemd manager object
+
+     Public Domain
+
+     Generated file using gdbus introspect call against systemd1 instance
+  -->
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+<node>
+ <interface name="org.freedesktop.systemd1.Manager">
+  <method name="StartUnit">
+   <arg name="name" type="s" direction="in"/>
+   <arg name="mode" type="s" direction="in"/>
+   <arg name="job" type="o" direction="out"/>
+  </method>
+  <method name="StopUnit">
+   <arg name="name" type="s" direction="in"/>
+   <arg name="mode" type="s" direction="in"/>
+   <arg name="job" type="o" direction="out"/>
+  </method>
+  <method name="GetJob">
+   <arg name="id" type="u" direction="in"/>
+   <arg name="job" type="o" direction="out"/>
+  </method>
+  <method name="ListJobs">
+   <arg name="jobs" type="a(usssoo)" direction="out"/>
+  </method>
+  <method name="Subscribe"/>
+  <method name="Unsubscribe"/>
+  <signal name="JobNew">
+   <arg name="id" type="u"/>
+   <arg name="job" type="o"/>
+   <arg name="unit" type="s"/>
+  </signal>
+  <signal name="JobRemoved">
+   <arg name="id" type="u"/>
+   <arg name="job" type="o"/>
+   <arg name="unit" type="s"/>
+   <arg name="result" type="s"/>
+  </signal>
+ </interface>
+</node>

=== modified file 'extra/upstart-local-bridge.c'
--- extra/upstart-local-bridge.c	2013-10-04 21:34:25 +0000
+++ extra/upstart-local-bridge.c	2015-01-16 15:15:53 +0000
@@ -22,10 +22,12 @@
 #endif /* HAVE_CONFIG_H */
 
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 
 #include <errno.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
@@ -48,21 +50,12 @@
 
 #include "dbus/upstart.h"
 #include "com.ubuntu.Upstart.h"
-#include "com.ubuntu.Upstart.Job.h"
-
-/**
- * Job:
- *
- * @entry: list header,
- * @path: D-Bus path for a job.
- *
- * Representation of an Upstart Job.
- *
- **/
-typedef struct job {
-	NihList entry;
-	char *path;
-} Job;
+#include "org.freedesktop.systemd1.h"
+
+#define DBUS_ADDRESS_SYSTEMD "unix:path=/run/systemd/private"
+#define DBUS_PATH_SYSTEMD "/org/freedesktop/systemd1"
+#define DBUS_SERVICE_SYSTEMD "org.freedesktop.systemd1"
+
 
 /**
  * Socket:
@@ -100,12 +93,10 @@
 	struct ucred   ucred;
 } ClientConnection;
 
-static void upstart_job_added    (void *data, NihDBusMessage *message,
-				  const char *job);
-static void upstart_job_removed  (void *data, NihDBusMessage *message,
-				  const char *job);
 static void upstart_connect      (void);
-static void upstart_disconnected (DBusConnection *connection);
+static void systemd_connect      (void);
+static int  systemd_booted       (void);
+static void init_disconnected (DBusConnection *connection);
 
 static Socket *create_socket (void *parent);
 
@@ -121,6 +112,8 @@
 
 static void emit_event (ClientConnection *client, const char *pair, size_t len);
 
+static void process_event (ClientConnection *client, const char *pair, size_t len);
+
 static void signal_handler (void *data, NihSignal *signal);
 
 static void cleanup (void);
@@ -134,13 +127,6 @@
 static int daemonise = FALSE;
 
 /**
- * jobs:
- *
- * Jobs that we're monitoring.
- **/
-static NihHash *jobs = NULL;
-
-/**
  * upstart:
  *
  * Proxy to Upstart daemon.
@@ -148,9 +134,17 @@
 static NihDBusProxy *upstart = NULL;
 
 /**
+ * systemd:
+ *
+ * Proxy to systemd daemon.
+ **/
+static NihDBusProxy *systemd = NULL;
+
+/**
  * event_name:
  *
- * Name of event this bridge emits.
+ * upstart: Name of event this bridge emits.
+ * systmed: Name of target this generator creates.
  **/
 static char *event_name = NULL;
 
@@ -202,7 +196,7 @@
 	{ 0, "daemon", N_("Detach and run in the background"),
 	  NULL, NULL, &daemonise, NULL },
 
-	{ 0, "event", N_("specify name of event to emit on receipt of name=value pair"),
+	{ 0, "event", N_("specify name of event to emit / target to generate on receipt of name=value pair"),
 		NULL, "EVENT", &event_name, NULL },
 
 	{ 0, "any-user", N_("allow any user to connect"),
@@ -258,7 +252,7 @@
 
 	nih_main_init (argv[0]);
 
-	nih_option_set_synopsis (_("Local socket Upstart Bridge"));
+	nih_option_set_synopsis (_("Local socket Upstart Bridge & systemd generator"));
 	nih_option_set_help (
 		_("By default, this bridge does not detach from the "
 		  "console and remains in the foreground.  Use the --daemon "
@@ -273,9 +267,6 @@
 		exit (1);
 	}
 
-	/* Allocate jobs hash table */
-	jobs = NIH_MUST (nih_hash_string_new (NULL, 0));
-
 	sock = create_socket (NULL);
 	if (! sock) {
 		nih_fatal ("%s %s",
@@ -286,7 +277,11 @@
 
 	nih_debug ("Connected to socket '%s' on fd %d", socket_name, sock->sock);
 
-	upstart_connect ();
+	if (systemd_booted) {
+		systemd_connect ();
+	} else {
+		upstart_connect ();
+	}
 
 	/* Become daemon */
 	if (daemonise) {
@@ -326,103 +321,14 @@
 	return ret;
 }
 
-static void
-upstart_job_added (void            *data,
-		   NihDBusMessage  *message,
-		   const char      *job_class_path)
-{
-	nih_local NihDBusProxy *job_class = NULL;
-	nih_local char ***start_on = NULL;
-	nih_local char ***stop_on = NULL;
-	Job *job;
-
-	nih_assert (job_class_path != NULL);
-
-	/* Obtain a proxy to the job */
-	job_class = nih_dbus_proxy_new (NULL, upstart->connection,
-					upstart->name, job_class_path,
-					NULL, NULL);
-	if (! job_class) {
-		NihError *err;
-
-		err = nih_error_get ();
-		nih_error ("Could not create proxy for job %s: %s",
-			   job_class_path, err->message);
-		nih_free (err);
-
-		return;
-	}
-
-	job_class->auto_start = FALSE;
-
-	/* Obtain the start_on and stop_on properties of the job */
-	if (job_class_get_start_on_sync (NULL, job_class, &start_on) < 0) {
-		NihError *err;
-
-		err = nih_error_get ();
-		nih_error ("Could not obtain job start condition %s: %s",
-			   job_class_path, err->message);
-		nih_free (err);
-
-		return;
-	}
-
-	if (job_class_get_stop_on_sync (NULL, job_class, &stop_on) < 0) {
-		NihError *err;
-
-		err = nih_error_get ();
-		nih_error ("Could not obtain job stop condition %s: %s",
-			   job_class_path, err->message);
-		nih_free (err);
-
-		return;
-	}
-
-	/* Free any existing record for the job (should never happen,
-	 * but worth being safe).
-	 */
-	job = (Job *)nih_hash_lookup (jobs, job_class_path);
-	if (job)
-		nih_free (job);
-
-	/* Create new record for the job */
-	job = NIH_MUST (nih_new (NULL, Job));
-	job->path = NIH_MUST (nih_strdup (job, job_class_path));
-
-	nih_list_init (&job->entry);
-
-	nih_debug ("Job got added %s", job_class_path);
-
-	nih_alloc_set_destructor (job, nih_list_destroy);
-
-	/* Add all jobs */
-	nih_hash_add (jobs, &job->entry);
-}
-
-static void
-upstart_job_removed (void            *data,
-		     NihDBusMessage  *message,
-		     const char      *job_path)
-{
-	Job *job;
-
-	nih_assert (job_path != NULL);
-
-	job = (Job *)nih_hash_lookup (jobs, job_path);
-	if (job) {
-		nih_debug ("Job went away %s", job_path);
-		nih_free (job);
-	}
-}
 
 static void
 upstart_connect (void)
 {
 	DBusConnection    *connection;
-	char            **job_class_paths;
 
 	/* Initialise the connection to Upstart */
-	connection = NIH_SHOULD (nih_dbus_connect (DBUS_ADDRESS_UPSTART, upstart_disconnected));
+	connection = NIH_SHOULD (nih_dbus_connect (DBUS_ADDRESS_UPSTART, init_disconnected));
 	if (! connection) {
 		NihError *err;
 
@@ -449,55 +355,88 @@
 	}
 
 	nih_debug ("Connected to Upstart");
-
-	/* Connect signals to be notified when jobs come and go */
-	if (! nih_dbus_proxy_connect (upstart, &upstart_com_ubuntu_Upstart0_6, "JobAdded",
-				      (NihDBusSignalHandler)upstart_job_added, NULL)) {
-		NihError *err;
-
-		err = nih_error_get ();
-		nih_fatal ("%s: %s", _("Could not create JobAdded signal connection"),
-			   err->message);
-		nih_free (err);
-
-		exit (1);
-	}
-
-	if (! nih_dbus_proxy_connect (upstart, &upstart_com_ubuntu_Upstart0_6, "JobRemoved",
-				      (NihDBusSignalHandler)upstart_job_removed, NULL)) {
-		NihError *err;
-
-		err = nih_error_get ();
-		nih_fatal ("%s: %s", _("Could not create JobRemoved signal connection"),
-			   err->message);
-		nih_free (err);
-
-		exit (1);
-	}
-
-	/* Request a list of all current jobs */
-	if (upstart_get_all_jobs_sync (NULL, upstart, &job_class_paths) < 0) {
-		NihError *err;
-
-		err = nih_error_get ();
-		nih_fatal ("%s: %s", _("Could not obtain job list"),
-			   err->message);
-		nih_free (err);
-
-		exit (1);
-	}
-
-	for (char **job_class_path = job_class_paths;
-	     job_class_path && *job_class_path; job_class_path++)
-		upstart_job_added (NULL, NULL, *job_class_path);
-
-	nih_free (job_class_paths);
-}
-
-static void
-upstart_disconnected (DBusConnection *connection)
-{
-	nih_fatal (_("Disconnected from Upstart"));
+}
+
+static void
+systemd_connect (void)
+{
+	DBusConnection    *connection;
+
+	/* Initialise the connection to systemd */
+	/* /run/systemd/private is supposedly "private" end-point
+	 * which systemctl & libsystemd use */
+	connection = NIH_SHOULD (nih_dbus_connect (DBUS_ADDRESS_SYSTEMD, init_disconnected));
+	if (! connection) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not connect to systemd"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	systemd = NIH_SHOULD (nih_dbus_proxy_new (NULL, connection,
+						  NULL, DBUS_PATH_SYSTEMD,
+						  NULL, NULL));
+	if (! systemd) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not create systemd proxy"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	FILE *fp = NULL;
+	nih_local char *template_name = NULL;
+	
+	template_name = NIH_MUST (nih_sprintf (NULL, "/run/systemd/system/%[email protected]", event_name));
+
+	fp = NIH_SHOULD (fopen(template_name, "we"));
+	if (!fp) {
+		nih_fatal ("%s %s", _("Failed to create target template"),
+			  strerror (errno));
+		exit (1);
+	}
+	fprintf (fp,
+		"# Automatically generated by %s\n\n"
+		"[Unit]\n"
+		"Description=Local bridge key value pairs\n"
+		"Documentation=man:%s\n",
+		program_name, program_name);
+	fflush (fp);
+	if (ferror (fp)) {
+		nih_fatal ("%s %s", _("Failed to write target template"),
+			   strerror (errno));
+		exit (1);
+	}
+	fclose (fp);
+
+	nih_debug ("Connected to systemd");
+}
+
+static int
+systemd_booted (void)
+{
+	struct stat st;
+
+	if (lstat ("/run/systemd/systemd/", &st) == 0) {
+		if (S_ISDIR(st.st_mode)) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+init_disconnected (DBusConnection *connection)
+{
+	nih_fatal (_("Disconnected from init"));
 	nih_main_loop_exit (1);
 }
 
@@ -645,7 +584,7 @@
 		if (used_len < min_len)
 			continue;
 
-		emit_event (client, pair, used_len);
+		process_event (client, pair, used_len);
 	}
 
 	/* Consume the entire length */
@@ -724,6 +663,8 @@
 	/* Handle abstract names */
 	if (sock->sun_addr.sun_path[0] == '@')
 		sock->sun_addr.sun_path[0] = '\0';
+	else
+		(void) unlink(sock->sun_addr.sun_path);
 
 	sock->sock = socket (sock->addr.sa_family, SOCK_STREAM, 0);
 	if (sock->sock < 0) {
@@ -844,3 +785,70 @@
 
 	dbus_pending_call_unref (pending_call);
 }
+
+static void
+systemd_launch_instance (ClientConnection  *client,
+	    const char        *pair,
+	    size_t             len)
+{
+	nih_local char     *safe_pair = NULL;
+	nih_local char    **key_value = NULL;
+	nih_local char     *group_name = NULL;
+	nih_local char     *unit_name = NULL;
+	nih_local char     *job_name = NULL;
+
+	nih_assert  (client);
+	nih_assert  (pair);
+	nih_assert  (len);
+
+	/* Why is pair not null-terminated?! */
+	safe_pair = NIH_MUST (nih_strndup (NULL, pair, len));
+
+	/* Get key val from the key=val pair */
+	key_value = NIH_MUST (nih_str_split (NULL, safe_pair, "=", TRUE));
+
+	/* Construct systemd event@key=*.target group name */
+	group_name = NIH_MUST (nih_sprintf (NULL, "%s@%s\\x3d%s.target",
+					   event_name, key_value[0],
+					    "*"));
+
+	/* Construct systemd event@key=value.target unit name */
+	unit_name = NIH_MUST (nih_sprintf (NULL, "%s@%s\\x3d%s.target",
+					   event_name, key_value[0],
+					   key_value[1]));
+
+	/* Stop group */
+	if (systemd_stop_unit_sync (NULL, systemd, group_name, "replace", &job_name)) {
+		NihError *err;
+		err = nih_error_get ();
+		nih_warn ("%s", err->message);
+		nih_free (err);
+	}
+
+	if (job_name) {
+		nih_free (job_name);
+		job_name = NULL;
+	}
+
+	/* Start unit */
+	if (systemd_start_unit_sync (NULL, systemd, unit_name, "replace", &job_name)) {
+		NihError *err;
+		err = nih_error_get ();
+		nih_warn ("%s", err->message);
+		nih_free (err);
+	}
+}
+
+
+static void
+process_event (ClientConnection  *client,
+	    const char        *pair,
+	    size_t             len)
+{
+	if (upstart) {
+		emit_event (client, pair, len);
+	}
+	if (systemd) {
+		systemd_launch_instance (client, pair, len);
+	}
+}

=== added file 'extra/upstart-systemd-job-event-bridge.c'
--- extra/upstart-systemd-job-event-bridge.c	1970-01-01 00:00:00 +0000
+++ extra/upstart-systemd-job-event-bridge.c	2015-01-16 15:15:53 +0000
@@ -0,0 +1,507 @@
+/* upstart
+ *
+ * Copyright © 2012-2013 Canonical Ltd.
+ * Author: Stéphane Graber <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <nih/macros.h>
+#include <nih/alloc.h>
+#include <nih/string.h>
+#include <nih/hash.h>
+#include <nih/io.h>
+#include <nih/option.h>
+#include <nih/main.h>
+#include <nih/logging.h>
+#include <nih/error.h>
+
+#include <nih-dbus/dbus_connection.h>
+#include <nih-dbus/dbus_proxy.h>
+
+#include "dbus/upstart.h"
+#include "com.ubuntu.Upstart.h"
+#include "org.freedesktop.systemd1.h"
+#include "org.freedesktop.systemd1.job.h"
+
+/* Defines for constants */
+#define SYSTEMD_EVENT "systemd"
+#define DBUS_PATH_SYSTEMD "/org/freedesktop/systemd1"
+#define DBUS_SERVICE_SYSTEMD "org.freedesktop.systemd1"
+
+/* Structure we use for tracking systemd jobs */
+typedef struct systemd_job {
+	NihList entry;
+	char *path;
+	char *job_type;
+} SystemdJob;
+
+/* Prototypes for static functions */
+static void dbus_disconnected (DBusConnection *connection);
+static void upstart_forward_event    (void *data, NihDBusMessage *message,
+				  const char *path);
+static void emit_event (const char *unit, const char *job_type);
+static void emit_event_error     (void *data, NihDBusMessage *message);
+
+
+static void systemd_job_new (void *data, NihDBusMessage *message, uint32_t id, const char *job, const char *unit);
+
+static void systemd_job_remove (void *data, NihDBusMessage *message, uint32_t id, const char *job, const char *unit, const char *result);
+
+static SystemdJob *job_new (const char *path, const uint32_t id, const char *unit, const char *job_type)
+	__attribute__ ((warn_unused_result));
+
+static int *job_destroy (SystemdJob *job)
+	__attribute__ ((warn_unused_result));
+
+/**
+ * daemonise:
+ *
+ * Set to TRUE if we should become a daemon, rather than just running
+ * in the foreground.
+ **/
+static int daemonise = FALSE;
+
+/**
+ * systemd_jobs:
+ *
+ * Hash of systemd jobs that we're monitoring.
+ **/
+static NihHash *systemd_jobs = NULL;
+
+/**
+ * systemd:
+ *
+ * Proxy to systemd daemon.
+ **/
+static NihDBusProxy *systemd = NULL;
+
+/**
+ * user_upstart:
+ *
+ * Proxy to user Upstart daemon instance.
+ **/
+static NihDBusProxy *user_upstart = NULL;
+
+/**
+ * systemd_connection:
+ *
+ * System DBus connection.
+ **/
+static DBusConnection *system_connection = NULL;
+
+/**
+ * options:
+ *
+ * Command-line options accepted by this program.
+ **/
+static NihOption options[] = {
+	{ 0, "daemon", N_("Detach and run in the background"),
+	  NULL, NULL, &daemonise, NULL },
+
+	NIH_OPTION_LAST
+};
+
+
+int
+main (int   argc,
+      char *argv[])
+{
+	char **              args;
+	DBusConnection *     user_connection;
+	int                  ret;
+	char *               pidfile_path = NULL;
+	char *               pidfile = NULL;
+	char *               user_session_addr = NULL;
+	nih_local char **    user_session_path = NULL;
+	char *               path_element = NULL;
+
+	nih_main_init (argv[0]);
+
+	nih_option_set_synopsis (_("Bridge system upstart events into the user session upstart"));
+	nih_option_set_help (
+		_("By default, upstart-event-bridge does not detach from the "
+		  "console and remains in the foreground.  Use the --daemon "
+		  "option to have it detach."));
+
+	args = nih_option_parser (NULL, argc, argv, options, FALSE);
+	if (! args)
+		exit (1);
+
+	user_session_addr = getenv ("UPSTART_SESSION");
+	if (! user_session_addr) {
+		nih_fatal (_("UPSTART_SESSION isn't set in environment"));
+		exit (1);
+	}
+
+	/* Allocate jobs hash table */
+	systemd_jobs = NIH_MUST (nih_hash_string_new (NULL, 0));
+
+	/* Initialise the connection to user session Upstart */
+	user_connection = NIH_SHOULD (nih_dbus_connect (user_session_addr, dbus_disconnected));
+
+	if (! user_connection) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not connect to the user session Upstart"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	user_upstart = NIH_SHOULD (nih_dbus_proxy_new (NULL, user_connection,
+						  NULL, DBUS_PATH_UPSTART,
+						  NULL, NULL));
+	if (! user_upstart) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not create Upstart proxy"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	/* Initialise the connection to system systemd */
+	system_connection = NIH_SHOULD (nih_dbus_bus (DBUS_BUS_SYSTEM, dbus_disconnected));
+
+	if (! system_connection) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not connect to system DBus"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	systemd = NIH_SHOULD (nih_dbus_proxy_new (NULL, system_connection,
+						  DBUS_SERVICE_SYSTEMD, DBUS_PATH_SYSTEMD,
+						  NULL, NULL));
+	if (! systemd) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not create Upstart proxy"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	if (! nih_dbus_proxy_connect (systemd, &systemd_org_freedesktop_systemd1_Manager, "JobNew",
+				      (NihDBusSignalHandler)systemd_job_new, NULL)) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not create JobNew signal connection"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	if (! nih_dbus_proxy_connect (systemd, &systemd_org_freedesktop_systemd1_Manager, "JobRemoved",
+				      (NihDBusSignalHandler)systemd_job_remove, NULL)) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not create JobRemove signal connection"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	if (systemd_subscribe_sync (NULL, systemd)) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_fatal ("%s: %s", _("Could not subscribe as a client"),
+			   err->message);
+		nih_free (err);
+
+		exit (1);
+	}
+
+	/* Become daemon */
+	if (daemonise) {
+		/* Deal with the pidfile location when becoming a daemon.
+		 * We need to be able to run one bridge per upstart daemon.
+		 * Store the PID file in XDG_RUNTIME_DIR or HOME and include the pid of
+		 * the Upstart instance (last part of the DBus path) in the filename.
+		 */
+
+		/* Extract PID from UPSTART_SESSION */
+		user_session_path = nih_str_split (NULL, user_session_addr, "/", TRUE);
+
+		for (int i = 0; user_session_path && user_session_path[i]; i++)
+			path_element = user_session_path[i];
+
+		if (! path_element) {
+			nih_fatal (_("Invalid value for UPSTART_SESSION"));
+			exit (1);
+		}
+
+		pidfile_path = getenv ("XDG_RUNTIME_DIR");
+		if (!pidfile_path)
+			pidfile_path = getenv ("HOME");
+
+		if (pidfile_path) {
+			NIH_MUST (nih_strcat_sprintf (&pidfile, NULL, "%s/%s.%s.pid",
+						      pidfile_path, program_invocation_short_name, path_element));
+			nih_main_set_pidfile (pidfile);
+		}
+
+		if (nih_main_daemonise () < 0) {
+			NihError *err;
+
+			err = nih_error_get ();
+			nih_fatal ("%s: %s", _("Unable to become daemon"),
+				   err->message);
+			nih_free (err);
+
+			exit (1);
+		}
+	}
+
+	/* Handle TERM and INT signals gracefully */
+	nih_signal_set_handler (SIGTERM, nih_signal_handler);
+	NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, nih_main_term_signal, NULL));
+
+	if (! daemonise) {
+		nih_signal_set_handler (SIGINT, nih_signal_handler);
+		NIH_MUST (nih_signal_add_handler (NULL, SIGINT, nih_main_term_signal, NULL));
+	}
+
+	ret = nih_main_loop ();
+
+	/* Destroy any PID file we may have created */
+	if (daemonise) {
+		nih_main_unlink_pidfile();
+	}
+
+	return ret;
+}
+
+static void
+dbus_disconnected (DBusConnection *connection)
+{
+	nih_fatal (_("Disconnected from DBus"));
+	nih_main_loop_exit (1);
+}
+
+static void
+upstart_forward_event (void *          data,
+		     NihDBusMessage *message,
+		     const char *    path)
+{
+	char *              event_name = NULL;
+	nih_local char *    new_event_name = NULL;
+	char **             event_env = NULL;
+	int                 event_env_count = 0;
+	DBusError           error;
+	DBusPendingCall *   pending_call;
+
+	dbus_error_init (&error);
+
+	/* Extract information from the original event */
+	if (!dbus_message_get_args (message->message, &error,
+	        DBUS_TYPE_STRING, &event_name,
+	        DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &event_env, &event_env_count,
+	        DBUS_TYPE_INVALID)) {
+		nih_error("DBUS error: %s", error.message);
+		dbus_error_free(&error);
+		return;
+	}
+
+	nih_assert (event_name != NULL);
+
+	/* Build the new event name */
+	NIH_MUST (nih_strcat_sprintf (&new_event_name, NULL, ":sys:%s", event_name));
+
+	/* Re-transmit the event */
+	pending_call = upstart_emit_event (user_upstart,
+			new_event_name, event_env, FALSE,
+			NULL, emit_event_error, NULL,
+			NIH_DBUS_TIMEOUT_NEVER);
+
+	if (! pending_call) {
+		NihError *err;
+		err = nih_error_get ();
+		nih_warn ("%s", err->message);
+		nih_free (err);
+	}
+
+	dbus_pending_call_unref (pending_call);
+	dbus_free_string_array (event_env);
+}
+
+static void
+emit_event (const char *unit,
+	    const char *job_type)
+{
+	DBusPendingCall *pending_call;
+	nih_local char **env = NULL;
+	nih_local char *var = NULL;
+	size_t env_len = 0;
+	
+	env = NIH_MUST (nih_str_array_new (NULL));
+	
+	var = NIH_MUST (nih_sprintf (NULL, "UNIT=%s", unit));
+	NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var));
+	
+	var = NIH_MUST (nih_sprintf (NULL, "JOBTYPE=%s", job_type));
+	NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var));
+	
+	pending_call = NIH_SHOULD (upstart_emit_event
+				   (user_upstart, SYSTEMD_EVENT, env, FALSE,
+				    NULL, emit_event_error, NULL,
+				    NIH_DBUS_TIMEOUT_NEVER));
+	
+	if (! pending_call) {
+		NihError *err;
+		
+		err = nih_error_get ();
+		nih_warn ("%s", err->message);
+		nih_free (err);
+	}
+}
+
+static void
+emit_event_error (void *          data,
+		  NihDBusMessage *message)
+{
+	NihError *err;
+
+	err = nih_error_get ();
+	nih_warn ("%s", err->message);
+	nih_free (err);
+}
+
+static void
+systemd_job_new (void *data, NihDBusMessage *message, uint32_t id, const char *job, const char *unit)
+{
+	nih_local NihDBusProxy *job_proxy = NULL;
+	nih_local char *job_type = NULL;
+
+	// get job proxy
+	job_proxy = NIH_SHOULD (nih_dbus_proxy_new (NULL, system_connection,
+						  DBUS_SERVICE_SYSTEMD, job,
+						  NULL, NULL));
+	if (! job_proxy) {
+		NihError *err;
+
+		err = nih_error_get ();
+		nih_error ("%s: %s", _("Could not get job_proxy"),
+			   err->message);
+		nih_free (err);
+		goto notype;
+	}
+
+	if (systemd_job_get_job_type_sync (NULL, job_proxy, &job_type) < 0) {
+		NihError *err;
+		err = nih_error_get ();
+		nih_error ("%s: %s", _("Could not get JobType"),
+			   err->message);
+		nih_free (err);
+		goto notype;
+	}
+
+	// create our job object in the jobs hash
+	NIH_SHOULD (job_new (job, id, unit, job_type));
+	return;
+
+notype:
+	// create our job object in the jobs hash
+	NIH_SHOULD (job_new (job, id, unit, "unknown"));
+	return;
+}
+
+static void
+systemd_job_remove (void *data, NihDBusMessage *message, uint32_t id, const char *job, const char *unit, const char *result)
+{
+
+	nih_local SystemdJob *systemd_job = NULL;
+
+	// pop from hashtable
+	systemd_job = (SystemdJob *)nih_hash_lookup (systemd_jobs, job);
+
+	if (!systemd_job)
+		return;
+
+	// if done, emit event
+	if (! strcmp("done", result)) {
+		emit_event (unit, systemd_job->job_type);
+	}
+}
+
+/**
+ * job_new:
+ *
+ * @id: Systemd Job ID
+ * @unit: Name of the associated unit
+ * @job_type: Job type
+ *
+ * Create a new Job object representing an inflight systemd job.
+ *
+ * Returns: job, or NULL on insufficient memory.
+ **/
+static SystemdJob *
+job_new (const char *path, const uint32_t id, const char *unit, const char *job_type)
+{
+	SystemdJob *job = NULL;
+
+	nih_assert (id);
+	nih_assert (unit);
+
+	job = nih_new (NULL, SystemdJob);
+	if (! job)
+		return NULL;
+
+	nih_list_init (&job->entry);
+
+	nih_alloc_set_destructor (job, nih_list_destroy);
+
+	job->path = nih_strdup (job, path);
+	if (! job->path)
+		goto error;
+
+	job->job_type = nih_strdup (job, job_type);
+	if (! job->job_type)
+		goto error;
+
+	if (! nih_hash_add_unique (systemd_jobs, &job->entry))
+		goto error;
+
+	return job;
+
+error:
+	nih_free (job);
+	return NULL;
+}

=== modified file 'extra/upstart-udev-bridge.c'
--- extra/upstart-udev-bridge.c	2011-12-15 16:14:06 +0000
+++ extra/upstart-udev-bridge.c	2015-01-16 15:15:53 +0000
@@ -69,6 +69,15 @@
 static NihDBusProxy *upstart = NULL;
 
 /**
+ * user:
+ *
+ * If TRUE, run in User Session mode connecting to the Session Init
+ * rather than PID 1. In this mode, certain relative paths are also
+ * expanded.
+ **/
+static int user = FALSE;
+
+/**
  * no_strip_udev_data:
  *
  * If TRUE, do not modify any udev message data (old behaviour).
@@ -86,6 +95,8 @@
 	  NULL, NULL, &daemonise, NULL },
 	{ 0, "no-strip", N_("Do not strip non-printable bytes from udev message data"),
 	  NULL, NULL, &no_strip_udev_data, NULL },
+	{ 0, "user", N_("Connect to user session"),
+	  NULL, NULL, &user, NULL },
 
 	NIH_OPTION_LAST
 };
@@ -97,6 +108,11 @@
 {
 	char **              args;
 	DBusConnection *     connection;
+	char *               pidfile_path = NULL;
+	char *               pidfile = NULL;
+	char                *user_session_addr = NULL;
+	nih_local char **    user_session_path = NULL;
+	char *               path_element = NULL;
 	struct udev *        udev;
 	struct udev_monitor *udev_monitor;
 	int                  ret;
@@ -113,8 +129,19 @@
 	if (! args)
 		exit (1);
 
+	if (user) {
+		user_session_addr = getenv ("UPSTART_SESSION");
+		if (! user_session_addr) {
+			nih_fatal (_("UPSTART_SESSION isn't set in environment"));
+			exit (EXIT_FAILURE);
+		}
+	}
+
 	/* Initialise the connection to Upstart */
-	connection = NIH_SHOULD (nih_dbus_connect (DBUS_ADDRESS_UPSTART, upstart_disconnected));
+	connection = NIH_SHOULD (nih_dbus_connect (user
+				? user_session_addr
+				: DBUS_ADDRESS_UPSTART,
+				upstart_disconnected));
 	if (! connection) {
 		NihError *err;
 
@@ -153,6 +180,35 @@
 
 	/* Become daemon */
 	if (daemonise) {
+		/* Deal with the pidfile location when becoming a daemon.
+		 * We need to be able to run one bridge per upstart daemon.
+		 * Store the PID file in XDG_RUNTIME_DIR or HOME and include the pid of
+		 * the Upstart instance (last part of the DBus path) in the filename.
+		 */
+
+		if (user) {
+			/* Extract PID from UPSTART_SESSION */
+			user_session_path = nih_str_split (NULL, user_session_addr, "/", TRUE);
+			
+			for (int i = 0; user_session_path && user_session_path[i]; i++)
+				path_element = user_session_path[i];
+
+			if (! path_element) {
+				nih_fatal (_("Invalid value for UPSTART_SESSION"));
+				exit (1);
+			}
+
+			pidfile_path = getenv ("XDG_RUNTIME_DIR");
+			if (!pidfile_path)
+				pidfile_path = getenv ("HOME");
+
+			if (pidfile_path) {
+				NIH_MUST (nih_strcat_sprintf (&pidfile, NULL, "%s/%s.%s.pid",
+							      pidfile_path, program_invocation_short_name, path_element));
+				nih_main_set_pidfile (pidfile);
+			}
+		}
+
 		if (nih_main_daemonise () < 0) {
 			NihError *err;
 
@@ -163,10 +219,12 @@
 
 			exit (1);
 		}
-
-		/* Send all logging output to syslog */
-		openlog (program_name, LOG_PID, LOG_DAEMON);
-		nih_log_set_logger (nih_logger_syslog);
+		
+		if (!user) {
+			/* Send all logging output to syslog for system bridge */
+			openlog (program_name, LOG_PID, LOG_DAEMON);
+			nih_log_set_logger (nih_logger_syslog);
+		}
 	}
 
 	/* Handle TERM and INT signals gracefully */

-- 
upstart-devel mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/upstart-devel

Reply via email to