| -r 1307 - "Merge of lp:~jamesodhunt/upstart/upstream-udev+socket-bridges." |
| |
| === modified file 'Makefile.am' |
| --- Makefile.am 2011-06-01 14:28:20 +0000 |
| +++ Makefile.am 2011-06-06 17:05:11 +0000 |
| @@ -1,6 +1,6 @@ |
| ## Process this file with automake to produce Makefile.in |
| |
| -SUBDIRS = intl dbus init util conf doc contrib po |
| +SUBDIRS = intl dbus init util extra conf doc contrib po |
| |
| EXTRA_DIST = HACKING |
| |
| |
| === modified file 'configure.ac' |
| --- configure.ac 2011-06-01 14:28:20 +0000 |
| +++ configure.ac 2011-06-06 17:05:11 +0000 |
| @@ -30,6 +30,9 @@ |
| PKG_CHECK_MODULES([NIH], [libnih >= 1.0.2]) |
| PKG_CHECK_MODULES([NIH_DBUS], [libnih-dbus >= 1.0.0]) |
| PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16]) |
| +PKG_CHECK_MODULES([UDEV], [libudev >= 146], [have_udev=yes], [have_udev=no]) |
| + |
| +AM_CONDITIONAL([HAVE_UDEV], [test "$have_udev" = yes]) |
| |
| # Checks for header files. |
| AC_CHECK_HEADERS([valgrind/valgrind.h]) |
| @@ -64,6 +67,6 @@ |
| |
| AC_CONFIG_FILES([ Makefile intl/Makefile |
| dbus/Makefile init/Makefile util/Makefile conf/Makefile |
| - doc/Makefile contrib/Makefile po/Makefile.in ]) |
| + extra/Makefile doc/Makefile contrib/Makefile po/Makefile.in ]) |
| AC_CONFIG_HEADERS([config.h]) |
| AC_OUTPUT |
| |
| === modified file 'dbus/com.ubuntu.Upstart.xml' |
| --- dbus/com.ubuntu.Upstart.xml 2009-07-03 16:08:10 +0000 |
| +++ dbus/com.ubuntu.Upstart.xml 2010-12-10 07:18:34 +0000 |
| @@ -49,6 +49,13 @@ |
| <arg name="env" type="as" direction="in" /> |
| <arg name="wait" type="b" direction="in" /> |
| </method> |
| + <method name="EmitEventWithFile"> |
| + <annotation name="com.netsplit.Nih.Method.Async" value="true" /> |
| + <arg name="name" type="s" direction="in" /> |
| + <arg name="env" type="as" direction="in" /> |
| + <arg name="wait" type="b" direction="in" /> |
| + <arg name="file" type="h" direction="in" /> |
| + </method> |
| |
| <!-- Basic information about Upstart --> |
| <property name="version" type="s" access="read" /> |
| |
| === added directory 'extra' |
| === added file 'extra/Makefile.am' |
| --- extra/Makefile.am 1970-01-01 00:00:00 +0000 |
| +++ extra/Makefile.am 2011-06-06 15:49:43 +0000 |
| @@ -0,0 +1,125 @@ |
| +## Process this file with automake to produce Makefile.in |
| + |
| +AM_CFLAGS = \ |
| + $(NIH_CFLAGS) \ |
| + $(NIH_DBUS_CFLAGS) \ |
| + $(DBUS_CFLAGS) \ |
| + $(UDEV_CFLAGS) |
| + |
| +AM_CPPFLAGS = \ |
| + -DLOCALEDIR="\"$(localedir)\"" \ |
| + -DSBINDIR="\"$(sbindir)\"" \ |
| + -I$(top_builddir) -I$(top_srcdir) -iquote$(builddir) -iquote$(srcdir) \ |
| + -I$(top_srcdir)/intl |
| + |
| + |
| +initdir = $(sysconfdir)/init |
| + |
| + |
| +sbin_PROGRAMS = \ |
| + upstart-socket-bridge |
| + |
| +dist_init_DATA = \ |
| + conf/upstart-socket-bridge.conf |
| + |
| +dist_man_MANS = \ |
| + man/upstart-socket-bridge.8 \ |
| + man/socket-event.7 |
| + |
| +upstart_socket_bridge_SOURCES = \ |
| + upstart-socket-bridge.c |
| +nodist_upstart_socket_bridge_SOURCES = \ |
| + $(com_ubuntu_Upstart_OUTPUTS) \ |
| + $(com_ubuntu_Upstart_Job_OUTPUTS) |
| +upstart_socket_bridge_LDADD = \ |
| + $(LTLIBINTL) \ |
| + $(NIH_LIBS) \ |
| + $(NIH_DBUS_LIBS) \ |
| + $(DBUS_LIBS) |
| + |
| + |
| +if HAVE_UDEV |
| +dist_init_DATA += \ |
| + conf/upstart-udev-bridge.conf |
| + |
| +dist_man_MANS += \ |
| + man/upstart-udev-bridge.8 |
| + |
| +sbin_PROGRAMS += \ |
| + upstart-udev-bridge |
| + |
| +upstart_udev_bridge_SOURCES = \ |
| + upstart-udev-bridge.c |
| +nodist_upstart_udev_bridge_SOURCES = \ |
| + $(com_ubuntu_Upstart_OUTPUTS) |
| +upstart_udev_bridge_LDADD = \ |
| + $(LTLIBINTL) \ |
| + $(NIH_LIBS) \ |
| + $(NIH_DBUS_LIBS) \ |
| + $(DBUS_LIBS) \ |
| + $(UDEV_LIBS) |
| + |
| +install-data-hook: |
| + src=`echo upstart-udev-bridge| sed '$(transform)'`.8; \ |
| + for symlink in \ |
| + net-device-added \ |
| + net-device-removed \ |
| + graphics-device-added \ |
| + drm-device-added; do \ |
| + inst=`echo $$symlink | sed '$(transform)'`.7; \ |
| + echo " ln -sf '$(man8dir)/$$src' '$(DESTDIR)$(man7dir)/$$inst'"; \ |
| + ln -sf "$(man8dir)/$$src" "$(DESTDIR)$(man7dir)/$$inst"; \ |
| + done |
| + |
| +else |
| +EXTRA_DIST = \ |
| + man/upstart-udev-bridge.8 |
| +endif |
| + |
| + |
| +com_ubuntu_Upstart_OUTPUTS = \ |
| + com.ubuntu.Upstart.c \ |
| + com.ubuntu.Upstart.h |
| + |
| +com_ubuntu_Upstart_XML = \ |
| + ../dbus/com.ubuntu.Upstart.xml |
| + |
| +$(com_ubuntu_Upstart_OUTPUTS): $(com_ubuntu_Upstart_XML) |
| + $(AM_V_GEN)$(NIH_DBUS_TOOL) \ |
| + --package=$(PACKAGE) \ |
| + --mode=proxy --prefix=upstart \ |
| + --default-interface=com.ubuntu.Upstart0_6 \ |
| + --output=$@ $< |
| + |
| + |
| +com_ubuntu_Upstart_Job_OUTPUTS = \ |
| + com.ubuntu.Upstart.Job.c \ |
| + com.ubuntu.Upstart.Job.h |
| + |
| +com_ubuntu_Upstart_Job_XML = \ |
| + ../dbus/com.ubuntu.Upstart.Job.xml |
| + |
| +$(com_ubuntu_Upstart_Job_OUTPUTS): $(com_ubuntu_Upstart_Job_XML) |
| + $(AM_V_GEN)$(NIH_DBUS_TOOL) \ |
| + --package=$(PACKAGE) \ |
| + --mode=proxy --prefix=job_class \ |
| + --default-interface=com.ubuntu.Upstart0_6.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) |
| + |
| +CLEANFILES = \ |
| + $(com_ubuntu_Upstart_OUTPUTS) \ |
| + $(com_ubuntu_Upstart_Job_OUTPUTS) |
| + |
| + |
| +clean-local: |
| + rm -f *.gcno *.gcda |
| + |
| +maintainer-clean-local: |
| + rm -f *.gcov |
| |
| === added directory 'extra/conf' |
| === added file 'extra/conf/upstart-socket-bridge.conf' |
| --- extra/conf/upstart-socket-bridge.conf 1970-01-01 00:00:00 +0000 |
| +++ extra/conf/upstart-socket-bridge.conf 2011-05-15 14:21:17 +0000 |
| @@ -0,0 +1,16 @@ |
| +# upstart-socket-bridge - Bridge socket events into upstart |
| +# |
| +# This helper daemon receives socket(7) events and |
| +# emits equivalent Upstart events. |
| + |
| +description "Bridge socket events into upstart" |
| + |
| +emits socket |
| + |
| +start on net-device-up IFACE=lo |
| +stop on runlevel [!2345] |
| + |
| +expect daemon |
| +respawn |
| + |
| +exec upstart-socket-bridge --daemon |
| |
| === added file 'extra/conf/upstart-udev-bridge.conf' |
| --- extra/conf/upstart-udev-bridge.conf 1970-01-01 00:00:00 +0000 |
| +++ extra/conf/upstart-udev-bridge.conf 2011-06-06 15:51:11 +0000 |
| @@ -0,0 +1,16 @@ |
| +# upstart-udev-bridge - Bridge udev events into upstart |
| +# |
| +# This helper daemon receives udev events from the netlink socket and |
| +# emits equivalent Upstart events. |
| + |
| +description "Bridge udev events into upstart" |
| + |
| +emits *-device-* |
| + |
| +start on starting udev |
| +stop on stopped udev |
| + |
| +expect daemon |
| +respawn |
| + |
| +exec upstart-udev-bridge --daemon |
| |
| === added directory 'extra/man' |
| === added file 'extra/man/socket-event.7' |
| --- extra/man/socket-event.7 1970-01-01 00:00:00 +0000 |
| +++ extra/man/socket-event.7 2011-05-15 14:21:17 +0000 |
| @@ -0,0 +1,92 @@ |
| +.TH socket\-event 8 2011-03-08 upstart |
| +.\" |
| +.SH NAME |
| +socket \- event signalling that a socket connection has been made |
| +.\" |
| +.SH SYNOPSIS |
| +.B socket |
| +.BI PROTO\fR= PROTO |
| +.BI PORT\fR= PORT |
| +.BI ADDR\fR= ADDR |
| + |
| +.B socket |
| +.BI PROTO\fR= PROTO |
| +.BI PATH\fR= PATH |
| +.\" |
| +.SH DESCRIPTION |
| + |
| +The |
| +.B socket |
| +event is generated by the |
| +.BR upstart\-socket\-bridge (8) |
| +daemon when a socket connection is made whose details match the |
| +socket event condition and environment specified in a jobs |
| +.B start on |
| +or |
| +.B stop on |
| +stanza. |
| + |
| +When an incoming connection is detected, the file descriptor |
| +representing the socket is passed to the job in question to allow it to |
| +.BR accept (2) |
| +the connection. Additionally, the environment variable |
| +.B UPSTART_JOB |
| +will contain the name of the event ("socket") and the environment |
| +variable |
| +.B UPSTART_FDS |
| +will contain the number of the file descriptor corresponding to the |
| +listening socket. |
| +.\" |
| +.SH EXAMPLES |
| +.\" |
| +.SS Internet socket |
| +Start web server when first client connects from localhost: |
| +.RS |
| +.nf |
| + |
| +start on socket PROTO=inet PORT=80 ADDR=127.0.0.1 |
| +.fi |
| +.RE |
| +.\" |
| +.SS Local socket |
| +.P |
| +.RS |
| +.nf |
| + |
| +start on socket PROTO=unix PATH=/var/run/.s.pgsql.1234 |
| +.fi |
| +.FE |
| +.\" |
| +.SS Abstract socket |
| +.P |
| + |
| +.RS |
| +.nf |
| + |
| +start on socket PROTO=unix PATH=@/at/upstart/example |
| +.fi |
| +.FE |
| +.\" |
| +.SH AUTHOR |
| +Written by Scott James Remnant |
| +.RB < scott@netsplit.com > |
| + |
| +Manual page written by James Hunt |
| +.RB < james.hunt@ubuntu.com > |
| +.\" |
| +.SH BUGS |
| +Report bugs at |
| +.RB < https://launchpad.net/upstart/+bugs > |
| +.\" |
| +.SH COPYRIGHT |
| +Copyright \(co 2011 Canonical Ltd. |
| +.PP |
| +This is free software; see the source for copying conditions. There is NO |
| +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| +.\" |
| +.SH SEE ALSO |
| +.BR init (5) |
| +.BR init (8) |
| +.BR socket (2) |
| +.BR socket (7) |
| +.BR upstart\-socket\-bridge (8) |
| |
| === added file 'extra/man/upstart-socket-bridge.8' |
| --- extra/man/upstart-socket-bridge.8 1970-01-01 00:00:00 +0000 |
| +++ extra/man/upstart-socket-bridge.8 2011-05-15 14:21:17 +0000 |
| @@ -0,0 +1,47 @@ |
| +.TH upstart-socket-bridge 8 2011-03-08 upstart |
| +.\" |
| +.SH NAME |
| +upstart-socket-bridge \- Bridge between Upstart and sockets |
| +.\" |
| +.SH SYNOPSIS |
| +.B upstart-socket-bridge |
| +.RI [ OPTIONS ]... |
| +.\" |
| +.SH DESCRIPTION |
| +The |
| +.B upstart-socket-bridge |
| +queries the Upstart |
| +.BR init (8) |
| +daemon for all job configurations which |
| +.B start on |
| +or |
| +.B stop on |
| +the socket event. It then waits for an incoming connection on each |
| +specified |
| +.BR socket (7) |
| +and when detected emits the socket event (\fBsocket\-event\fP (7)), |
| +setting a number of environment variables for the job to query. |
| +.\" |
| +.SH AUTHOR |
| +Written by Scott James Remnant |
| +.RB < scott@netsplit.com > |
| + |
| +Manual page written by James Hunt |
| +.RB < james.hunt@ubuntu.com > |
| +.\" |
| +.SH BUGS |
| +Report bugs at |
| +.RB < https://launchpad.net/upstart/+bugs > |
| +.\" |
| +.SH COPYRIGHT |
| +Copyright \(co 2011 Canonical Ltd. |
| +.PP |
| +This is free software; see the source for copying conditions. There is NO |
| +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| +.\" |
| +.SH SEE ALSO |
| +.BR init (5) |
| +.BR init (8) |
| +.BR socket (2) |
| +.BR socket (7) |
| +.BR socket\-event (7) |
| |
| === added file 'extra/man/upstart-udev-bridge.8' |
| --- extra/man/upstart-udev-bridge.8 1970-01-01 00:00:00 +0000 |
| +++ extra/man/upstart-udev-bridge.8 2011-06-06 15:52:17 +0000 |
| @@ -0,0 +1,57 @@ |
| +.TH upstart\-udev\-bridge 8 2011-03-08 upstart |
| +.\" |
| +.SH NAME |
| +upstart\-udev\-bridge \- Bridge between Upstart and udev |
| +.\" |
| +.SH SYNOPSIS |
| +.B upstart\-udev\-bridge |
| +.RI [ OPTIONS ]... |
| +.\" |
| +.SH DESCRIPTION |
| +.B upstart\-udev\-bridge |
| +receives information about kernel uevents that |
| +.BR udev (8) |
| +has completed and creates |
| +.BR init (8) |
| +events for them. |
| + |
| +It emits events which match the pattern "\fIS\fP\-device\-\fIA\fP" where |
| +\(aqS\(aq is the udev \fIsubsystem\fP and \(aqA\(aq is the udev \fIaction\fP. |
| +See \fBudev\fP(7) and for further details. |
| + |
| +Assuming \fI/sys\fP is mounted, possible values for \fIsubsystem\fP for |
| +your system are viewable via \fI/sys/class/\fP. |
| + |
| +.\" |
| +.SH EXAMPLES |
| + |
| +.IP net\-device\-added |
| +Event emitted when a network device is added. |
| +.IP net\-device\-removed |
| +Event emitted when a network device is removed. |
| +.IP graphics\-card\-added |
| +Event emitted when a graphics device is available to the system. |
| +.\" |
| +.SH NOTES |
| +This is a temporary tool until |
| +.BR init (8) |
| +itself gains the functionality to read them directly; you should not |
| +rely on its behaviour. |
| +.\" |
| +.SH AUTHOR |
| +Written by Scott James Remnant |
| +.RB < scott@netsplit.com > |
| +.\" |
| +.SH BUGS |
| +Report bugs at |
| +.RB < https://launchpad.net/ubuntu/+source/upstart/+bugs > |
| +.\" |
| +.SH COPYRIGHT |
| +Copyright \(co 2009,2010,2011 Canonical Ltd. |
| +.PP |
| +This is free software; see the source for copying conditions. There is NO |
| +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| +.SH SEE ALSO |
| +.BR init (5) |
| +.BR init (8) |
| +.BR udev (7) |
| |
| === added file 'extra/upstart-socket-bridge.c' |
| --- extra/upstart-socket-bridge.c 1970-01-01 00:00:00 +0000 |
| +++ extra/upstart-socket-bridge.c 2011-05-15 14:21:17 +0000 |
| @@ -0,0 +1,644 @@ |
| +/* upstart |
| + * |
| + * Copyright © 2010 Canonical Ltd. |
| + * Author: Scott James Remnant <scott@netsplit.com>. |
| + * |
| + * 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 <sys/epoll.h> |
| +#include <sys/types.h> |
| +#include <sys/socket.h> |
| +#include <sys/un.h> |
| + |
| +#include <netinet/in.h> |
| +#include <arpa/inet.h> |
| + |
| +#include <errno.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#include <syslog.h> |
| +#include <unistd.h> |
| + |
| +#include <nih/macros.h> |
| +#include <nih/alloc.h> |
| +#include <nih/list.h> |
| +#include <nih/hash.h> |
| +#include <nih/string.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 "com.ubuntu.Upstart.Job.h" |
| + |
| + |
| +/* Structure we use for tracking jobs */ |
| +typedef struct job { |
| + NihList entry; |
| + char *path; |
| + NihList sockets; |
| +} Job; |
| + |
| +/* Structure we use for tracking listening sockets */ |
| +typedef struct socket { |
| + NihList entry; |
| + |
| + union { |
| + struct sockaddr addr; |
| + struct sockaddr_in sin_addr; |
| + struct sockaddr_un sun_addr; |
| + }; |
| + socklen_t addrlen; |
| + |
| + int sock; |
| +} Socket; |
| + |
| + |
| +/* Prototypes for static functions */ |
| +static void epoll_watcher (void *data, NihIoWatch *watch, |
| + NihIoEvents events); |
| +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 job_add_socket (Job *job, char **socket_info); |
| +static void socket_destroy (Socket *socket); |
| +static void upstart_disconnected (DBusConnection *connection); |
| +static void emit_event_reply (Socket *sock, NihDBusMessage *message); |
| +static void emit_event_error (Socket *sock, NihDBusMessage *message); |
| + |
| + |
| +/** |
| + * daemonise: |
| + * |
| + * Set to TRUE if we should become a daemon, rather than just running |
| + * in the foreground. |
| + **/ |
| +static int daemonise = FALSE; |
| + |
| +/** |
| + * epoll_fd: |
| + * |
| + * Shared epoll file descriptor for listening on. |
| + **/ |
| +static int epoll_fd = -1; |
| + |
| +/** |
| + * jobs: |
| + * |
| + * Jobs that we're monitoring. |
| + **/ |
| +static NihHash *jobs = NULL; |
| + |
| +/** |
| + * upstart: |
| + * |
| + * Proxy to Upstart daemon. |
| + **/ |
| +static NihDBusProxy *upstart = 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 *connection; |
| + char ** job_class_paths; |
| + int ret; |
| + |
| + nih_main_init (argv[0]); |
| + |
| + nih_option_set_synopsis (_("Bridge socket events into upstart")); |
| + nih_option_set_help ( |
| + _("By default, upstart-socket-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); |
| + |
| + /* Create an epoll file descriptor for listening on; use this so |
| + * we can do edge triggering rather than level. |
| + */ |
| + epoll_fd = epoll_create1 (0); |
| + if (epoll_fd < 0) { |
| + nih_fatal ("%s: %s", _("Could not create epoll descriptor"), |
| + strerror (errno)); |
| + exit (1); |
| + } |
| + |
| + NIH_MUST (nih_io_add_watch (NULL, epoll_fd, NIH_IO_READ, |
| + epoll_watcher, NULL)); |
| + |
| + /* Allocate jobs hash table */ |
| + jobs = NIH_MUST (nih_hash_string_new (NULL, 0)); |
| + |
| + /* Initialise the connection to Upstart */ |
| + connection = NIH_SHOULD (nih_dbus_connect (DBUS_ADDRESS_UPSTART, upstart_disconnected)); |
| + if (! connection) { |
| + NihError *err; |
| + |
| + err = nih_error_get (); |
| + nih_fatal ("%s: %s", _("Could not connect to Upstart"), |
| + err->message); |
| + nih_free (err); |
| + |
| + exit (1); |
| + } |
| + |
| + upstart = NIH_SHOULD (nih_dbus_proxy_new (NULL, connection, |
| + NULL, DBUS_PATH_UPSTART, |
| + NULL, NULL)); |
| + if (! upstart) { |
| + NihError *err; |
| + |
| + err = nih_error_get (); |
| + nih_fatal ("%s: %s", _("Could not create Upstart proxy"), |
| + err->message); |
| + nih_free (err); |
| + |
| + exit (1); |
| + } |
| + |
| + /* 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); |
| + |
| + /* Become daemon */ |
| + if (daemonise) { |
| + 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); |
| + } |
| + |
| + /* Send all logging output to syslog */ |
| + openlog (program_name, LOG_PID, LOG_DAEMON); |
| + nih_log_set_logger (nih_logger_syslog); |
| + } |
| + |
| + /* 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 (); |
| + |
| + return ret; |
| +} |
| + |
| + |
| +static void |
| +epoll_watcher (void * data, |
| + NihIoWatch *watch, |
| + NihIoEvents events) |
| +{ |
| + struct epoll_event event[1024]; |
| + int num_events; |
| + |
| + num_events = epoll_wait (epoll_fd, event, 1024, 0); |
| + if (num_events < 0) { |
| + nih_error ("%s: %s", _("Error from epoll"), strerror (errno)); |
| + return; |
| + } else if (num_events == 0) |
| + return; |
| + |
| + for (int i = 0; i < num_events; i++) { |
| + Socket *sock = (Socket *)event[i].data.ptr; |
| + nih_local char **env = NULL; |
| + size_t env_len = 0; |
| + char *var; |
| + DBusPendingCall *pending_call; |
| + |
| + if (event[i].events & EPOLLIN) |
| + nih_debug ("%p EPOLLIN", sock); |
| + if (event[i].events & EPOLLERR) |
| + nih_debug ("%p EPOLLERR", sock); |
| + if (event[i].events & EPOLLHUP) |
| + nih_debug ("%p EPOLLHUP", sock); |
| + |
| + env = NIH_MUST (nih_str_array_new (NULL)); |
| + |
| + switch (sock->addr.sa_family) { |
| + case AF_INET: |
| + NIH_MUST (nih_str_array_add (&env, NULL, &env_len, |
| + "PROTO=inet")); |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "PORT=%d", |
| + ntohs (sock->sin_addr.sin_port))); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, |
| + var)); |
| + nih_discard (var); |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "ADDR=%s", |
| + inet_ntoa (sock->sin_addr.sin_addr))); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, |
| + var)); |
| + nih_discard (var); |
| + break; |
| + case AF_UNIX: |
| + NIH_MUST (nih_str_array_add (&env, NULL, &env_len, |
| + "PROTO=unix")); |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "PATH=%s", |
| + sock->sun_addr.sun_path)); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, |
| + var)); |
| + nih_discard (var); |
| + break; |
| + default: |
| + nih_assert_not_reached (); |
| + } |
| + |
| + pending_call = NIH_SHOULD (upstart_emit_event_with_file ( |
| + upstart, "socket", env, TRUE, |
| + sock->sock, |
| + (UpstartEmitEventWithFileReply)emit_event_reply, |
| + (NihDBusErrorHandler)emit_event_error, |
| + sock, |
| + NIH_DBUS_TIMEOUT_NEVER)); |
| + if (! pending_call) { |
| + NihError *err; |
| + |
| + err = nih_error_get (); |
| + nih_warn ("%s: %s", _("Could not send socket event"), |
| + err->message); |
| + nih_free (err); |
| + } |
| + |
| + dbus_pending_call_unref (pending_call); |
| + |
| + // might be EPOLLIN |
| + // might be EPOLLERR |
| + // might be EPOLLHUP |
| + } |
| +} |
| + |
| + |
| +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_list_init (&job->sockets); |
| + |
| + /* Find out whether this job listens for any socket events */ |
| + for (char ***event = start_on; event && *event && **event; event++) |
| + if (! strcmp (**event, "socket")) |
| + job_add_socket (job, *event); |
| + for (char ***event = stop_on; event && *event && **event; event++) |
| + if (! strcmp (**event, "socket")) |
| + job_add_socket (job, *event); |
| + |
| + /* If we didn't end up with any sockets, free the job and move on */ |
| + if (NIH_LIST_EMPTY (&job->sockets)) { |
| + nih_free (job); |
| + return; |
| + } |
| + |
| + nih_debug ("Job got added %s", job_class_path); |
| + |
| + nih_alloc_set_destructor (job, nih_list_destroy); |
| + 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 |
| +job_add_socket (Job * job, |
| + char **socket_info) |
| +{ |
| + Socket *sock; |
| + nih_local char *error = NULL; |
| + int components = 0; |
| + struct epoll_event event; |
| + |
| + nih_assert (job != NULL); |
| + nih_assert (socket_info != NULL); |
| + nih_assert (! strcmp(socket_info[0], "socket")); |
| + |
| + sock = NIH_MUST (nih_new (job, Socket)); |
| + memset (sock, 0, sizeof (Socket)); |
| + sock->sock = -1; |
| + |
| + nih_list_init (&sock->entry); |
| + |
| + nih_debug ("Found socket"); |
| + for (char **env = socket_info + 1; env && *env; env++) { |
| + char *val; |
| + size_t name_len; |
| + |
| + val = strchr (*env, '='); |
| + if (! val) { |
| + nih_warn ("Ignored socket event without variable name in %s", |
| + job->path); |
| + goto error; |
| + } |
| + |
| + name_len = val - *env; |
| + val++; |
| + |
| + if (! strncmp (*env, "PROTO", name_len)) { |
| + if (! strcmp (val, "inet")) { |
| + sock->addrlen = sizeof sock->sin_addr; |
| + sock->sin_addr.sin_family = AF_INET; |
| + sock->sin_addr.sin_addr.s_addr = INADDR_ANY; |
| + components = 1; |
| + } else if (! strcmp (val, "unix")) { |
| + sock->addrlen = sizeof sock->sun_addr; |
| + sock->sun_addr.sun_family = AF_UNIX; |
| + components = 1; |
| + } else { |
| + nih_warn ("Ignored socket event with unknown PROTO=%s in %s", |
| + val, job->path); |
| + goto error; |
| + } |
| + |
| + } else if (! strncmp (*env, "PORT", name_len) |
| + && (sock->sin_addr.sin_family == AF_INET)) { |
| + sock->sin_addr.sin_port = htons (atoi (val)); |
| + components--; |
| + |
| + } else if (! strncmp (*env, "ADDR", name_len) |
| + && (sock->sin_addr.sin_family == AF_INET)) { |
| + if (inet_aton (val, &(sock->sin_addr.sin_addr)) == 0) { |
| + nih_warn ("Ignored socket event with invalid ADDR=%s in %s", |
| + val, job->path); |
| + goto error; |
| + } |
| + |
| + } else if (! strncmp (*env, "PATH", name_len) |
| + && (sock->sun_addr.sun_family == AF_UNIX)) { |
| + strncpy (sock->sun_addr.sun_path, val, |
| + sizeof sock->sun_addr.sun_path); |
| + |
| + if (sock->sun_addr.sun_path[0] == '@') |
| + sock->sun_addr.sun_path[0] = '\0'; |
| + |
| + components--; |
| + |
| + } else { |
| + nih_warn ("Ignored socket event with unknown variable %.*s in %s", |
| + (int)name_len, *env, job->path); |
| + goto error; |
| + } |
| + } |
| + |
| + /* Missing any required components? */ |
| + if (components) { |
| + nih_warn ("Ignored incomplete socket event in %s", |
| + job->path); |
| + goto error; |
| + } |
| + |
| + /* Let's try and set this baby up */ |
| + sock->sock = socket (sock->addr.sa_family, SOCK_STREAM, 0); |
| + if (sock->sock < 0) { |
| + nih_warn ("Failed to create socket in %s: %s", |
| + job->path, strerror (errno)); |
| + goto error; |
| + } |
| + |
| + int opt = 1; |
| + if (setsockopt (sock->sock, SOL_SOCKET, SO_REUSEADDR, |
| + &opt, sizeof opt) < 0) { |
| + nih_warn ("Failed to set socket reuse in %s: %s", |
| + job->path, strerror (errno)); |
| + goto error; |
| + } |
| + |
| + if (bind (sock->sock, &sock->addr, sock->addrlen) < 0) { |
| + nih_warn ("Failed to bind socket in %s: %s", |
| + job->path, strerror (errno)); |
| + goto error; |
| + } |
| + |
| + if (listen (sock->sock, SOMAXCONN) < 0) { |
| + nih_warn ("Failed to listen on socket in %s: %s", |
| + job->path, strerror (errno)); |
| + goto error; |
| + } |
| + |
| + /* We have a listening socket, now we want to be notified when someone |
| + * connects; but we just want one notification, we don't want to get |
| + * a DDoS of wake-ups while waiting for the service to start. |
| + * |
| + * The solution is to use epoll in edge-triggered mode, this will |
| + * fire only on initial connection until a new one comes in. |
| + */ |
| + event.events = EPOLLIN | EPOLLET; |
| + event.data.ptr = sock; |
| + |
| + if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, sock->sock, &event) < 0) { |
| + nih_warn ("Failed to watch socket in %s: %s", |
| + job->path, strerror (errno)); |
| + goto error; |
| + } |
| + |
| + /* Okay then, add to the job */ |
| + nih_alloc_set_destructor (sock, socket_destroy); |
| + nih_list_add (&job->sockets, &sock->entry); |
| + |
| + return; |
| + |
| +error: |
| + if (sock->sock != -1) |
| + close (sock->sock); |
| + nih_free (sock); |
| +} |
| + |
| +static void |
| +socket_destroy (Socket *sock) |
| +{ |
| + epoll_ctl (epoll_fd, EPOLL_CTL_DEL, sock->sock, NULL); |
| + close (sock->sock); |
| + |
| + nih_list_destroy (&sock->entry); |
| +} |
| + |
| + |
| +static void |
| +upstart_disconnected (DBusConnection *connection) |
| +{ |
| + nih_fatal (_("Disconnected from Upstart")); |
| + nih_main_loop_exit (1); |
| +} |
| + |
| + |
| +static void |
| +emit_event_reply (Socket * sock, |
| + NihDBusMessage *message) |
| +{ |
| + nih_debug ("Event completed"); |
| +} |
| + |
| +static void |
| +emit_event_error (Socket * sock, |
| + NihDBusMessage *message) |
| +{ |
| + NihError *err; |
| + |
| + err = nih_error_get (); |
| + nih_warn ("%s: %s", _("Error emitting socket event"), err->message); |
| + nih_free (err); |
| +} |
| |
| === added file 'extra/upstart-udev-bridge.c' |
| --- extra/upstart-udev-bridge.c 1970-01-01 00:00:00 +0000 |
| +++ extra/upstart-udev-bridge.c 2010-12-10 04:42:34 +0000 |
| @@ -0,0 +1,310 @@ |
| +/* upstart |
| + * |
| + * Copyright © 2009 Canonical Ltd. |
| + * Author: Scott James Remnant <scott@netsplit.com>. |
| + * |
| + * 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 <libudev.h> |
| + |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#include <syslog.h> |
| + |
| +#include <nih/macros.h> |
| +#include <nih/alloc.h> |
| +#include <nih/string.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" |
| + |
| + |
| +/* Prototypes for static functions */ |
| +static void udev_monitor_watcher (struct udev_monitor *udev_monitor, |
| + NihIoWatch *watch, NihIoEvents events); |
| +static void upstart_disconnected (DBusConnection *connection); |
| +static void emit_event_error (void *data, NihDBusMessage *message); |
| + |
| + |
| +/** |
| + * daemonise: |
| + * |
| + * Set to TRUE if we should become a daemon, rather than just running |
| + * in the foreground. |
| + **/ |
| +static int daemonise = FALSE; |
| + |
| +/** |
| + * upstart: |
| + * |
| + * Proxy to Upstart daemon. |
| + **/ |
| +static NihDBusProxy *upstart = 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 * connection; |
| + struct udev * udev; |
| + struct udev_monitor *udev_monitor; |
| + int ret; |
| + |
| + nih_main_init (argv[0]); |
| + |
| + nih_option_set_synopsis (_("Bridge udev events into upstart")); |
| + nih_option_set_help ( |
| + _("By default, upstart-udev-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); |
| + |
| + /* Initialise the connection to Upstart */ |
| + connection = NIH_SHOULD (nih_dbus_connect (DBUS_ADDRESS_UPSTART, upstart_disconnected)); |
| + if (! connection) { |
| + NihError *err; |
| + |
| + err = nih_error_get (); |
| + nih_fatal ("%s: %s", _("Could not connect to Upstart"), |
| + err->message); |
| + nih_free (err); |
| + |
| + exit (1); |
| + } |
| + |
| + upstart = NIH_SHOULD (nih_dbus_proxy_new (NULL, connection, |
| + NULL, DBUS_PATH_UPSTART, |
| + NULL, NULL)); |
| + if (! 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 udev */ |
| + nih_assert (udev = udev_new ()); |
| + nih_assert (udev_monitor = udev_monitor_new_from_netlink (udev, "udev")); |
| + nih_assert (udev_monitor_enable_receiving (udev_monitor) == 0); |
| + udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); |
| + |
| + NIH_MUST (nih_io_add_watch (NULL, udev_monitor_get_fd (udev_monitor), |
| + NIH_IO_READ, |
| + (NihIoWatcher)udev_monitor_watcher, |
| + udev_monitor)); |
| + |
| + /* Become daemon */ |
| + if (daemonise) { |
| + 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); |
| + } |
| + |
| + /* Send all logging output to syslog */ |
| + openlog (program_name, LOG_PID, LOG_DAEMON); |
| + nih_log_set_logger (nih_logger_syslog); |
| + } |
| + |
| + /* 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 (); |
| + |
| + return ret; |
| +} |
| + |
| + |
| +static void |
| +udev_monitor_watcher (struct udev_monitor *udev_monitor, |
| + NihIoWatch * watch, |
| + NihIoEvents events) |
| +{ |
| + struct udev_device * udev_device; |
| + const char * subsystem; |
| + const char * action; |
| + const char * kernel; |
| + const char * devpath; |
| + const char * devname; |
| + nih_local char * name = NULL; |
| + nih_local char ** env = NULL; |
| + size_t env_len = 0; |
| + DBusPendingCall * pending_call; |
| + |
| + udev_device = udev_monitor_receive_device (udev_monitor); |
| + if (! udev_device) |
| + return; |
| + |
| + subsystem = udev_device_get_subsystem (udev_device); |
| + action = udev_device_get_action (udev_device); |
| + kernel = udev_device_get_sysname (udev_device); |
| + devpath = udev_device_get_devpath (udev_device); |
| + devname = udev_device_get_devnode (udev_device); |
| + |
| + if (! strcmp (action, "add")) { |
| + name = NIH_MUST (nih_sprintf (NULL, "%s-device-added", |
| + subsystem)); |
| + } else if (! strcmp (action, "change")) { |
| + name = NIH_MUST (nih_sprintf (NULL, "%s-device-changed", |
| + subsystem)); |
| + } else if (! strcmp (action, "remove")) { |
| + name = NIH_MUST (nih_sprintf (NULL, "%s-device-removed", |
| + subsystem)); |
| + } else { |
| + name = NIH_MUST (nih_sprintf (NULL, "%s-device-%s", |
| + subsystem, action)); |
| + } |
| + |
| + env = NIH_MUST (nih_str_array_new (NULL)); |
| + |
| + if (kernel) { |
| + nih_local char *var = NULL; |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "KERNEL=%s", kernel)); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
| + } |
| + |
| + if (devpath) { |
| + nih_local char *var = NULL; |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "DEVPATH=%s", devpath)); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
| + } |
| + |
| + if (devname) { |
| + nih_local char *var = NULL; |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "DEVNAME=%s", devname)); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
| + } |
| + |
| + if (subsystem) { |
| + nih_local char *var = NULL; |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "SUBSYSTEM=%s", subsystem)); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
| + } |
| + |
| + if (action) { |
| + nih_local char *var = NULL; |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "ACTION=%s", action)); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
| + } |
| + |
| + for (struct udev_list_entry *list_entry = udev_device_get_properties_list_entry (udev_device); |
| + list_entry != NULL; |
| + list_entry = udev_list_entry_get_next (list_entry)) { |
| + const char * key; |
| + nih_local char *var = NULL; |
| + |
| + key = udev_list_entry_get_name (list_entry); |
| + if (! strcmp (key, "DEVPATH")) |
| + continue; |
| + if (! strcmp (key, "DEVNAME")) |
| + continue; |
| + if (! strcmp (key, "SUBSYSTEM")) |
| + continue; |
| + if (! strcmp (key, "ACTION")) |
| + continue; |
| + |
| + var = NIH_MUST (nih_sprintf (NULL, "%s=%s", key, |
| + udev_list_entry_get_value (list_entry))); |
| + NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); |
| + } |
| + |
| + nih_debug ("%s %s", name, devname); |
| + |
| + pending_call = NIH_SHOULD (upstart_emit_event (upstart, |
| + name, 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); |
| + |
| + udev_device_unref (udev_device); |
| +} |
| + |
| + |
| +static void |
| +upstart_disconnected (DBusConnection *connection) |
| +{ |
| + nih_fatal (_("Disconnected from Upstart")); |
| + nih_main_loop_exit (1); |
| +} |
| + |
| +static void |
| +emit_event_error (void * data, |
| + NihDBusMessage *message) |
| +{ |
| + NihError *err; |
| + |
| + err = nih_error_get (); |
| + nih_warn ("%s", err->message); |
| + nih_free (err); |
| +} |
| |
| === modified file 'init/control.c' |
| --- init/control.c 2011-06-03 11:02:18 +0000 |
| +++ init/control.c 2011-06-06 17:05:11 +0000 |
| @@ -25,8 +25,10 @@ |
| |
| #include <dbus/dbus.h> |
| |
| +#include <fcntl.h> |
| #include <stdio.h> |
| #include <string.h> |
| +#include <unistd.h> |
| |
| #include <nih/macros.h> |
| #include <nih/alloc.h> |
| @@ -507,13 +509,24 @@ |
| } |
| |
| |
| +int |
| +control_emit_event (void *data, |
| + NihDBusMessage *message, |
| + const char *name, |
| + char * const *env, |
| + int wait) |
| +{ |
| + return control_emit_event_with_file (data, message, name, env, wait, -1); |
| +} |
| + |
| /** |
| - * control_emit_event: |
| + * control_emit_event_with_file: |
| * @data: not used, |
| * @message: D-Bus connection and message received, |
| * @name: name of event to emit, |
| * @env: environment of environment, |
| - * @wait: whether to wait for event completion before returning. |
| + * @wait: whether to wait for event completion before returning, |
| + * @file: file descriptor. |
| * |
| * Implements the top half of the EmitEvent method of the com.ubuntu.Upstart |
| * interface, the bottom half may be found in event_finished(). |
| @@ -533,11 +546,12 @@ |
| * Returns: zero on success, negative value on raised error. |
| **/ |
| int |
| -control_emit_event (void *data, |
| - NihDBusMessage *message, |
| - const char *name, |
| - char * const *env, |
| - int wait) |
| +control_emit_event_with_file (void *data, |
| + NihDBusMessage *message, |
| + const char *name, |
| + char * const *env, |
| + int wait, |
| + int file) |
| { |
| Event *event; |
| Blocked *blocked; |
| @@ -550,6 +564,7 @@ |
| if (! strlen (name)) { |
| nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS, |
| _("Name may not be empty string")); |
| + close (file); |
| return -1; |
| } |
| |
| @@ -557,13 +572,26 @@ |
| if (! environ_all_valid (env)) { |
| nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS, |
| _("Env must be KEY=VALUE pairs")); |
| + close (file); |
| return -1; |
| } |
| |
| /* Make the event and block the message on it */ |
| event = event_new (NULL, name, (char **)env); |
| - if (! event) |
| - nih_return_system_error (-1); |
| + if (! event) { |
| + nih_error_raise_system (); |
| + close (file); |
| + return -1; |
| + } |
| + |
| + event->fd = file; |
| + if (event->fd >= 0) { |
| + long flags; |
| + |
| + flags = fcntl (event->fd, F_GETFD); |
| + flags &= ~FD_CLOEXEC; |
| + fcntl (event->fd, F_SETFD, flags); |
| + } |
| |
| if (wait) { |
| blocked = blocked_new (event, BLOCKED_EMIT_METHOD, message); |
| @@ -573,6 +601,7 @@ |
| if (! blocked) { |
| nih_error_raise_system (); |
| nih_free (event); |
| + close (file); |
| return -1; |
| } |
| |
| |
| === modified file 'init/control.h' |
| --- init/control.h 2011-05-31 14:22:00 +0000 |
| +++ init/control.h 2011-06-06 17:05:11 +0000 |
| @@ -72,6 +72,10 @@ |
| const char *name, char * const *env, |
| int wait) |
| __attribute__ ((warn_unused_result)); |
| +int control_emit_event_with_file (void *data, NihDBusMessage *message, |
| + const char *name, char * const *env, |
| + int wait, int file) |
| + __attribute__ ((warn_unused_result)); |
| |
| int control_get_version (void *data, NihDBusMessage *message, |
| char **version) |
| |
| === modified file 'init/event.c' |
| --- init/event.c 2011-05-12 12:07:02 +0000 |
| +++ init/event.c 2011-06-06 17:05:11 +0000 |
| @@ -25,6 +25,7 @@ |
| |
| |
| #include <string.h> |
| +#include <unistd.h> |
| |
| #include <nih/macros.h> |
| #include <nih/alloc.h> |
| @@ -124,4 +125,5 @@ |
| nih_list_init (&event->entry); |
| |
| + event->fd = -1; |
| event->progress = EVENT_PENDING; |
| event->failed = FALSE; |
| @@ -402,8 +404,16 @@ |
| job->start_env = env; |
| nih_ref (job->start_env, job); |
| |
| + nih_discard (env); |
| + env = NULL; |
| + |
| job_finished (job, FALSE); |
| |
| + NIH_MUST (event_operator_fds (class->start_on, job, |
| + &job->fds, &job->num_fds, |
| + &job->start_env, &len, |
| + "UPSTART_FDS")); |
| + |
| event_operator_events (job->class->start_on, |
| job, &job->blocking); |
| |
| @@ -468,6 +478,8 @@ |
| nih_free (blocked); |
| } |
| |
| + close (event->fd); |
| + |
| if (event->failed) { |
| char *name; |
| |
| |
| === modified file 'init/event.h' |
| --- init/event.h 2010-12-10 03:02:57 +0000 |
| +++ init/event.h 2011-06-06 17:05:11 +0000 |
| @@ -67,6 +67,7 @@ |
| |
| char *name; |
| char **env; |
| + int fd; |
| |
| EventProgress progress; |
| int failed; |
| |
| === modified file 'init/event_operator.c' |
| --- init/event_operator.c 2010-11-19 14:34:51 +0000 |
| +++ init/event_operator.c 2010-12-10 07:18:34 +0000 |
| @@ -552,6 +552,65 @@ |
| return *env; |
| } |
| |
| +int * |
| +event_operator_fds (EventOperator *root, |
| + const void *parent, |
| + int **fds, |
| + size_t *num_fds, |
| + char ***env, |
| + size_t *len, |
| + const char *key) |
| +{ |
| + nih_local char *evlist = NULL; |
| + |
| + nih_assert (root != NULL); |
| + nih_assert (fds != NULL); |
| + nih_assert (num_fds != NULL); |
| + nih_assert (env != NULL); |
| + nih_assert (len != NULL); |
| + nih_assert (key != NULL); |
| + |
| + /* Initialise the event list variable with the name given. */ |
| + evlist = nih_sprintf (NULL, "%s=", key); |
| + if (! evlist) |
| + return NULL; |
| + |
| + *num_fds = 0; |
| + NIH_TREE_FOREACH_FULL (&root->node, iter, |
| + (NihTreeFilter)event_operator_filter, NULL) { |
| + EventOperator *oper = (EventOperator *)iter; |
| + |
| + if (oper->type != EVENT_MATCH) |
| + continue; |
| + |
| + nih_assert (oper->event != NULL); |
| + |
| + if (oper->event->fd >= 0) { |
| + *fds = nih_realloc (*fds, parent, sizeof (int) * (*num_fds + 1)); |
| + if (! *fds) |
| + return NULL; |
| + |
| + (*fds)[(*num_fds)++] = oper->event->fd; |
| + |
| + if (evlist[strlen (evlist) - 1] != '=') { |
| + if (! nih_strcat_sprintf (&evlist, NULL, " %d", |
| + oper->event->fd)) |
| + return NULL; |
| + } else { |
| + if (! nih_strcat_sprintf (&evlist, NULL, "%d", |
| + oper->event->fd)) |
| + return NULL; |
| + } |
| + } |
| + } |
| + |
| + if (*num_fds) |
| + if (! environ_add (env, parent, len, TRUE, evlist)) |
| + return NULL; |
| + |
| + return (void *)1; |
| +} |
| + |
| /** |
| * event_operator_events: |
| * @root: operator tree to collect from, |
| |
| === modified file 'init/event_operator.h' |
| --- init/event_operator.h 2009-06-23 09:29:35 +0000 |
| +++ init/event_operator.h 2010-12-10 07:18:34 +0000 |
| @@ -95,6 +95,14 @@ |
| char ** event_operator_environment (EventOperator *root, char ***env, |
| const void *parent, size_t *len, |
| const char *key); |
| +int * |
| +event_operator_fds (EventOperator *root, |
| + const void *parent, |
| + int **fds, |
| + size_t *num_fds, |
| + char ***env, |
| + size_t *len, |
| + const char *key); |
| void event_operator_events (EventOperator *root, |
| const void *parent, NihList *list); |
| |
| |
| === modified file 'init/job.c' |
| --- init/job.c 2011-05-15 12:53:17 +0000 |
| +++ init/job.c 2011-06-06 17:05:11 +0000 |
| @@ -125,6 +125,9 @@ |
| goto error; |
| } |
| |
| + job->fds = NULL; |
| + job->num_fds = 0; |
| + |
| job->pid = nih_alloc (job, sizeof (pid_t) * PROCESS_LAST); |
| if (! job->pid) |
| goto error; |
| |
| === modified file 'init/job.h' |
| --- init/job.h 2010-12-10 03:02:57 +0000 |
| +++ init/job.h 2011-06-06 17:05:11 +0000 |
| @@ -134,6 +134,9 @@ |
| char **stop_env; |
| EventOperator *stop_on; |
| |
| + int *fds; |
| + size_t num_fds; |
| + |
| pid_t *pid; |
| Event *blocker; |
| NihList blocking; |
| |
| === modified file 'po/POTFILES.in' |
| --- po/POTFILES.in 2011-05-05 09:06:21 +0000 |
| +++ po/POTFILES.in 2011-05-15 15:08:06 +0000 |
| @@ -20,3 +20,5 @@ |
| util/runlevel.c |
| util/shutdown.c |
| util/telinit.c |
| + |
| +extra/upstart-udev-bridge.c |
| |