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
