James Hunt has proposed merging lp:~jamesodhunt/upstart/event-prefixes into 
lp:upstart.

Requested reviews:
  Upstart Reviewers (upstart-reviewers)

For more details, see:
https://code.launchpad.net/~jamesodhunt/upstart/event-prefixes/+merge/146822

This branch reworks the event-prefixing such that an unqualified system event 
will _not_ now cause a user job specifying an unqualified event to start. This 
should avoid confusion but at the cost of requiring user jobs to specify an 
explicit ':sys:' prefix to react to system-level events.

-- 
https://code.launchpad.net/~jamesodhunt/upstart/event-prefixes/+merge/146822
Your team Upstart Reviewers is requested to review the proposed merge of 
lp:~jamesodhunt/upstart/event-prefixes into lp:upstart.
=== modified file 'ChangeLog'
--- ChangeLog	2013-01-31 17:23:55 +0000
+++ ChangeLog	2013-02-06 11:05:27 +0000
@@ -1,3 +1,26 @@
+2013-02-06  James Hunt  <[email protected]>
+
+	* extra/upstart-event-bridge.c:
+	  - upstart_forward_event(): Expand '::' events rather than ignoring
+	    to reflect new meaning.
+	* init/event.h:
+	  - EventType: Added EVENT_TYPE_ANY.
+	  - Changed EVENT_PREFIX_DEFAULT to EVENT_PREFIX_ANY.
+	* init/event.c:
+	  - event_type_from_name(): Changed EVENT_PREFIX_DEFAULT to
+	    EVENT_PREFIX_ANY and its meaning.
+	  - event_canonical_name(): Changed EVENT_PREFIX_DEFAULT to
+	    EVENT_PREFIX_ANY.
+	* init/event_operator.c: event_operator_match(): Additional logic for
+	  Session Inits.
+	* init/man/init.5: Explanation of event prefixes.
+	* init/tests/test_event.c: test_event_names(): Updated for EVENT_TYPE_ANY.
+	* init/tests/test_event_operator.c: test_operator_match(): Replaced
+	  long-hand tests with table-driven testing invoked from
+	  test_operator_match_prefixes() that also fakes being PID 1 to check
+	  all possible match behaviour scenarios.
+	* util/man/initctl.8: Updated event-prefixing explanation.
+
 2013-01-31  James Hunt  <[email protected]>
 
 	* init/control.c:
@@ -36,6 +59,26 @@
 
 2013-01-29  James Hunt  <[email protected]>
 
+	* extra/upstart-event-bridge.c: Ignore events explicitly
+	  prefixed with '::' and ':sys:' as they are system-level private.
+	* init/control.c: control_emit_event_with_file(): Call
+	  event_check_emit_policy() to disallow invalid events.
+	* init/event.c:
+	  - event_type_from_name(): New function to categorise an event based
+	    on its name.
+	  - event_check_emit_policy(): New function.
+	  - event_canonical_name(): Determine short name of event.
+	* init/event.h: Defines to support new functions and EventType enum.
+	* init/event_operator.c: event_operator_match(): Perform checks on
+	  @event and @oper to determine new match behaviour for PID 1
+	  and Session Init processes.
+	* init/tests/test_event.c: test_event_names(): New test.
+	* init/tests/test_event_operator.c:
+	  - test_operator_match(): Updates for new syntax.
+	* util/man/initctl.8: Explanation of new event prefix syntax.
+
+2013-01-29  James Hunt  <[email protected]>
+
 	* init/control.c: More careful uid checking.
 
 2013-01-28  James Hunt  <[email protected]>

=== modified file 'extra/upstart-event-bridge.c'
--- extra/upstart-event-bridge.c	2013-01-23 19:18:43 +0000
+++ extra/upstart-event-bridge.c	2013-02-06 11:05:27 +0000
@@ -280,7 +280,7 @@
 	dbus_error_init (&error);
 
 	/* Extract information from the original event */
-	if (!dbus_message_get_args (message->message, &error,
+	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)) {
@@ -291,8 +291,33 @@
 
 	nih_assert (event_name != NULL);
 
+	/* Ignore event names that are already system-prefixed since,
+	 * by specifying the prefix, the emitter has denoted such events
+	 * are not for the consumption of Session Init processes and
+	 * should be considered private to the system level.
+	 */
+	if (strstr (event_name, ":sys:") == event_name)
+		return;
+
 	/* Build the new event name */
-	NIH_MUST (nih_strcat_sprintf (&new_event_name, NULL, ":sys:%s", event_name));
+	if (strstr (event_name, "::") == event_name) {
+		char  *suffix;
+
+		/* Event is prefixed to allow propagation to any
+		 * namespace so remove existing prefix and fully
+		 * qualify.
+		 */
+
+		suffix = event_name + strlen ("::");
+
+		/* Ignore invalid event comprising only the prefix */
+		if (! suffix || ! *suffix)
+			return;
+
+		NIH_MUST (nih_strcat_sprintf (&new_event_name, NULL, ":sys:%s", suffix));
+	} else {
+		NIH_MUST (nih_strcat_sprintf (&new_event_name, NULL, ":sys:%s", event_name));
+	}
 
 	/* Re-transmit the event */
 	pending_call = upstart_emit_event (user_upstart,

=== modified file 'init/control.c'
--- init/control.c	2013-01-31 17:23:55 +0000
+++ init/control.c	2013-02-06 11:05:27 +0000
@@ -637,6 +637,13 @@
 		return -1;
 	}
 
+	if (! event_check_emit_policy (name)) {
+		nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
+				_("Invalid prefix or no name following prefix"));
+		close (file);
+		return -1;
+	}
+
 	/* Make the event and block the message on it */
 	event = event_new (NULL, name, (char **)env);
 	if (! event) {

=== modified file 'init/event.c'
--- init/event.c	2012-12-14 23:47:51 +0000
+++ init/event.c	2013-02-06 11:05:27 +0000
@@ -835,3 +835,128 @@
 
 	return NULL;
 }
+
+/**
+ * event_type_from_name:
+ *
+ * @name: event name.
+ *
+ * Determine type of event with name @name.
+ *
+ * Returns: EventType.
+ **/
+EventType
+event_type_from_name (const char *name)
+{
+	pid_t  pid;
+	size_t len;
+
+	nih_assert (name);
+
+	len = strlen (name);
+
+	if (! len)
+		return EVENT_TYPE_INVALID;
+
+	pid = getpid ();
+
+	/* handle common case first for speed */
+	if (name[0] != ':') {
+		return pid == 1 ? EVENT_TYPE_SYSTEM : EVENT_TYPE_USER;
+	}
+
+	if (strstr (name, EVENT_PREFIX_ANY) == name) {
+		if (len > strlen (EVENT_PREFIX_ANY))
+			return EVENT_TYPE_ANY;
+	} else if (strstr (name, EVENT_PREFIX_SYSTEM) == name) {
+		if (len > strlen (EVENT_PREFIX_SYSTEM))
+			return EVENT_TYPE_SYSTEM;
+	} else if (strstr (name, EVENT_PREFIX_USER) == name) {
+		if (len > strlen (EVENT_PREFIX_USER))
+			return EVENT_TYPE_USER;
+	}
+
+	/* We cannot expand the name since it falls into one of the
+	 * following categories, all of which are invalid:
+	 *
+	 * - exactly matches EVENT_PREFIX_ANY.
+	 * - exactly matches EVENT_PREFIX_SYSTEM.
+	 * - exactly matches EVENT_PREFIX_USER.
+	 * - has an unrecognised prefix.
+	 */
+	return EVENT_TYPE_INVALID;
+}
+
+/**
+ * event_check_emit_policy:
+ *
+ * @name: event name.
+ *
+ * Determine whether @name represents a legitimate event name for
+ * the running instance.
+ *
+ * Returns: TRUE if @name is valid, else FALSE.
+ **/
+int
+event_check_emit_policy (const char *name)
+{
+	pid_t      pid;
+	EventType  type;
+
+	nih_assert (name);
+
+	pid = getpid ();
+
+	type = event_type_from_name (name);
+
+	if (type == EVENT_TYPE_INVALID)
+		return FALSE;
+
+	/* Note that events of all types must be permitted for Session
+	 * Inits to allow the event bridge to inject system events.
+	 */
+	if (pid == 1 && type == EVENT_TYPE_USER)
+		return FALSE;
+
+	return TRUE;
+}
+
+/**
+ * event_canonical_name:
+ *
+ * @name: name of event.
+ *
+ * Determine the canonical event name based on @name.
+ *
+ * Returns: Pointer into @name representing canonical name
+ * or NULL if @name is invalid.
+ **/
+const char *
+event_canonical_name (const char *name)
+{
+	const char  *canonical = NULL;
+	size_t       len;
+
+	nih_assert (name);
+
+	len = strlen (name);
+
+	if (! len)
+		return NULL;
+
+	/* handle common case first for speed */
+	if (name[0] != ':')
+		return name;
+
+	if (strstr (name, EVENT_PREFIX_ANY) == name)
+		canonical = name + strlen (EVENT_PREFIX_ANY);
+	else if (strstr (name, EVENT_PREFIX_SYSTEM) == name)
+		canonical = name + strlen (EVENT_PREFIX_SYSTEM);
+	else if (strstr (name, EVENT_PREFIX_USER) == name)
+		canonical = name + strlen (EVENT_PREFIX_USER);
+
+	/* Catch event that exactly matches one of the recognised
+	 * prefixes (which is invalid)
+	 */
+	return canonical && *canonical ? canonical : NULL;
+}

=== modified file 'init/event.h'
--- init/event.h	2012-09-20 13:07:53 +0000
+++ init/event.h	2013-02-06 11:05:27 +0000
@@ -42,6 +42,45 @@
 } EventProgress;
 
 /**
+ * EventType;
+ *
+ * Type of event determined from the format of its name.
+ *
+ * Used purely for determining event namespacing so not stored
+ * within the Event structure.
+ **/
+typedef enum event_type {
+	EVENT_TYPE_INVALID,
+	EVENT_TYPE_SYSTEM,
+	EVENT_TYPE_USER,
+	EVENT_TYPE_ANY
+} EventType;
+
+/**
+ * EVENT_PREFIX_SYSTEM:
+ *
+ * Prefix applied internally to all events that have been emitted by
+ * Upstart running as PID 1.
+ **/
+#define EVENT_PREFIX_SYSTEM ":sys:"
+
+/**
+ * EVENT_PREFIX_USER:
+ *
+ * Prefix applied internally to all events that have been emitted by
+ * Upstart running as a Session Init where PID != 1.
+ **/
+#define EVENT_PREFIX_USER ":user:"
+
+/**
+ * EVENT_PREFIX_ANY:
+ *
+ * Prefix that jobs and events can specify to make event name apply to
+ * any namespace.
+ **/
+#define EVENT_PREFIX_ANY "::"
+
+/**
  * Event:
  * @entry: list header,
  * @session: session the event is attached to,
@@ -115,6 +154,15 @@
 Event * event_from_index (int event_index)
 	__attribute__ ((warn_unused_result));
 
+EventType event_type_from_name (const char *name)
+	__attribute__ ((warn_unused_result));
+
+int    event_check_emit_policy (const char *name)
+	__attribute__ ((warn_unused_result));
+
+const char * event_canonical_name (const char *name)
+	__attribute__ ((warn_unused_result));
+
 NIH_END_EXTERN
 
 #endif /* INIT_EVENT_H */

=== modified file 'init/event_operator.c'
--- init/event_operator.c	2012-09-20 13:07:53 +0000
+++ init/event_operator.c	2013-02-06 11:05:27 +0000
@@ -29,6 +29,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <fnmatch.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <nih/macros.h>
 #include <nih/alloc.h>
@@ -275,8 +277,12 @@
 		      Event         *event,
 		      char * const  *env)
 {
-	char * const *oenv;
-	char * const *eenv;
+	char * const    *oenv;
+	char * const    *eenv;
+	EventType        oper_name_type;
+	EventType        event_name_type;
+	const char      *canonical_name_oper;
+	const char      *canonical_name_event;
 
 	nih_assert (oper != NULL);
 	nih_assert (oper->type == EVENT_MATCH);
@@ -284,10 +290,41 @@
 	nih_assert (oper->node.right == NULL);
 	nih_assert (event != NULL);
 
+	canonical_name_oper = event_canonical_name (oper->name);
+	canonical_name_event = event_canonical_name (event->name);
+
+	/* If either type is not recognised, no match is possible */
+	if (! canonical_name_oper || ! canonical_name_event)
+		return FALSE;
+
 	/* Names must match */
-	if (strcmp (oper->name, event->name))
+	if (strcmp (canonical_name_oper, canonical_name_event))
 		return FALSE;
 
+	/* Now, apply policy */
+	event_name_type = event_type_from_name (event->name);
+	oper_name_type = event_type_from_name (oper->name);
+
+	if (getpid () == 1) {
+		/* Don't react to user events */
+		if (event_name_type == EVENT_TYPE_USER || oper_name_type == EVENT_TYPE_USER)
+			return FALSE;
+	} else {
+		/* If user and system prefixes are specified and are not
+		 * the same, no match is possible.
+		 *
+		 * Note that the 'any' type implicitly matches for oper
+		 * but is not allowed for the event (since Session Init
+		 * may not attempt to impersonate PID 1 by emitting an
+		 * any-type event).
+		 */
+		if (oper_name_type == EVENT_TYPE_SYSTEM && (event_name_type == EVENT_TYPE_USER
+					|| event_name_type == EVENT_TYPE_ANY))
+			return FALSE;
+		if (oper_name_type == EVENT_TYPE_USER  && event_name_type == EVENT_TYPE_SYSTEM)
+			return FALSE;
+	}
+
 	/* Match operator environment variables against those from the event,
 	 * starting both from the beginning.
 	 */

=== modified file 'init/man/init.5'
--- init/man/init.5	2013-01-31 16:51:49 +0000
+++ init/man/init.5	2013-02-06 11:05:27 +0000
@@ -345,6 +345,61 @@
 and
 .IR VALUE .
 
+Event names can take one of three forms: fully-qualified,
+implicitly-qualified, and unqualified which allow the event to match in
+different combinations of namespace.
+
+.RS
+.B Fully-Qualified Events
+
+.RS
+An event that begins with either
+.I :sys:
+to match an event emitted at the system (pid 1) level, or 
+.I :user:
+to match an event emitted at the user session level.
+
+Note that the
+.I :user:
+prefix is only honoured for jobs running in \fBUser Session Mode\fR.
+
+.RE
+
+.B Implicitly-Qualified events
+
+.RS
+An event that begins with
+.I ::
+to match any namespace.
+
+Note that this prefix is only honoured for jobs running in
+.B User Session Mode
+and allows a job to start on
+.I either
+a system-level event
+.I or
+a user-session-level event.
+
+.RE
+
+.B Unqualified events
+
+.RS
+Any event that does not begin with a colon (\fI:\fR).
+
+Unqualified events are assumed to be implicitly qualified to the
+appropriate namespace (\fI:sys:\fR for system-level events and
+\fI:user:\fR for for user session-level events) such that they only
+match at that level.
+.RE
+.RE
+.RS
+
+Event names with a leading colon should be considered \fIreserved\fR:
+attempting to match on such an event that does not fall into one of the
+existing categories outlined above will fail.  
+.B This is a change in behaviour from older versions of Upstart.
+
 .nf
 start on started gdm or started kdm
 
@@ -352,6 +407,8 @@
 
 start on net\-device\-added INTERFACE!=lo
 .fi
+.RE
+
 .TP
 .B stop on \fIEVENT \fR[[\fIKEY=\fR]\fIVALUE\fR]... [\fBand\fR|\fBor\fR...]
 The

=== modified file 'init/tests/test_event.c'
--- init/tests/test_event.c	2013-01-31 17:23:55 +0000
+++ init/tests/test_event.c	2013-02-06 11:05:27 +0000
@@ -2039,6 +2039,105 @@
 	}
 }
 
+void
+test_event_names (void)
+{
+	const char *name;
+	EventType   type;
+
+	TEST_GROUP ("event names");
+
+	/************************************************************/
+	TEST_FEATURE ("unqualified");
+
+	name = event_canonical_name ("");
+	/* event name must not be 'empty' */
+	TEST_EQ_P (name, NULL);
+	type = event_type_from_name ("");
+	TEST_EQ (type, EVENT_TYPE_INVALID);
+
+	name = event_canonical_name ("foo");
+	TEST_EQ_STR (name, "foo");
+	type = event_type_from_name ("foo");
+	TEST_EQ (type, EVENT_TYPE_USER);
+
+	/************************************************************/
+	TEST_FEATURE ("implicitly-qualified");
+
+	name = event_canonical_name ("::");
+	/* event name must not be 'empty' */
+	TEST_EQ_P (name, NULL);
+	type = event_type_from_name ("::");
+	TEST_EQ (type, EVENT_TYPE_INVALID);
+
+	name = event_canonical_name ("::foo");
+	TEST_EQ_STR (name, "foo");
+	type = event_type_from_name ("::foo");
+	TEST_EQ (type, EVENT_TYPE_ANY);
+
+	name = event_canonical_name (":::");
+	TEST_EQ_STR (name, ":");
+	type = event_type_from_name (":::");
+	TEST_EQ (type, EVENT_TYPE_ANY);
+
+	name = event_canonical_name ("::::");
+	TEST_EQ_STR (name, "::");
+	type = event_type_from_name ("::::");
+	TEST_EQ (type, EVENT_TYPE_ANY);
+
+	name = event_canonical_name (":::user:");
+	TEST_EQ_STR (name, ":user:");
+	type = event_type_from_name (":::user:");
+	TEST_EQ (type, EVENT_TYPE_ANY);
+
+	/************************************************************/
+	TEST_FEATURE ("fully-qualified");
+
+	name = event_canonical_name (":user:");
+	/* event name must not be 'empty' */
+	TEST_EQ_P (name, NULL);
+	type = event_type_from_name (":user:");
+	TEST_EQ (type, EVENT_TYPE_INVALID);
+
+	name = event_canonical_name (":sys:");
+	/* event name must not be 'empty' */
+	TEST_EQ_P (name, NULL);
+	type = event_type_from_name (":sys:");
+	TEST_EQ (type, EVENT_TYPE_INVALID);
+
+	name = event_canonical_name (":user:::");
+	TEST_EQ_STR (name, "::");
+	type = event_type_from_name (":user:::");
+	TEST_EQ (type, EVENT_TYPE_USER);
+
+	name = event_canonical_name (":sys:::");
+	TEST_EQ_STR (name, "::");
+	type = event_type_from_name (":sys:::");
+	TEST_EQ (type, EVENT_TYPE_SYSTEM);
+
+	name = event_canonical_name (":user: ");
+	TEST_EQ_STR (name, " ");
+	type = event_type_from_name (":user: ");
+	TEST_EQ (type, EVENT_TYPE_USER);
+
+	name = event_canonical_name (":sys: ");
+	TEST_EQ_STR (name, " ");
+	type = event_type_from_name (":sys: ");
+	TEST_EQ (type, EVENT_TYPE_SYSTEM);
+
+	name = event_canonical_name (" :user:");
+	TEST_EQ_STR (name, " :user:");
+	type = event_type_from_name (" :user:");
+	TEST_EQ (type, EVENT_TYPE_USER);
+
+	name = event_canonical_name (" :sys:");
+	TEST_EQ_STR (name, " :sys:");
+	type = event_type_from_name (" :sys:");
+	TEST_EQ (type, EVENT_TYPE_USER);
+
+	/************************************************************/
+}
+
 
 int
 main (int   argc,
@@ -2057,6 +2156,7 @@
 	test_pending ();
 	test_pending_handle_jobs ();
 	test_finished ();
+	test_event_names ();
 
 	return 0;
 }

=== modified file 'init/tests/test_event_operator.c'
--- init/tests/test_event_operator.c	2012-12-17 11:45:28 +0000
+++ init/tests/test_event_operator.c	2013-02-06 11:05:27 +0000
@@ -32,6 +32,136 @@
 #include "conf.h"
 #include "test_util.h"
 
+/**
+ * EventMatchPrefixTest:
+ *
+ * @feature: test feature description,
+ * @event_name: name of event,
+ * @oper_name: name of EVENT_MATCH EventOperator,
+ * @pid1_match: TRUE if @event_name should match @oper_name when running as PID 1,
+ * @session_match: TRUE if @event_name should match @oper_name when
+ *  running with PID != 1.
+ **/
+typedef struct event_match_prefix_test {
+	char  *feature;
+	char  *event_name;
+	char  *oper_name;
+	int    session_match;
+	int    pid1_match;
+} EventMatchPrefixTest;
+
+EventMatchPrefixTest prefix_tests[] = {
+
+	{ "unqualified event with unqualified event operator",
+	  "foo", "foo",
+	  TRUE, TRUE
+	},
+
+	{ "any-qualified event with unqualified event operator",
+	  "::foo", "foo",
+	  TRUE, TRUE
+	},
+
+	{ "fully-qualified user event with unqualified event operator",
+	  ":user:foo", "foo",
+	  TRUE, FALSE
+	},
+
+	{ "fully-qualified system event with unqualified event operator",
+	  ":sys:foo", "foo",
+	  FALSE, TRUE
+	},
+
+	/*******************************/
+
+	{ "unqualified event with any-qualified event operator",
+	  "foo", "::foo",
+	  TRUE, TRUE
+	},
+
+	{ "any-qualified event with any-qualified event operator",
+	  "::foo", "::foo",
+	  TRUE, TRUE
+	},
+
+	{ "fully-qualified user event with any-qualified event operator",
+	  ":user:foo", "::foo",
+	  TRUE, FALSE
+	},
+
+	{ "fully-qualified system event with any-qualified event operator",
+	  ":sys:foo", "::foo",
+	  TRUE, TRUE
+	},
+
+	/*******************************/
+
+	{ "unqualified event with fully-qualified event operator",
+	  "foo", ":user:foo",
+	  TRUE, FALSE
+	},
+
+	{ "any-qualified event with fully-qualified user event operator",
+	  "::foo", ":user:foo",
+	  TRUE, FALSE
+	},
+
+	{ "fully-qualified user event with fully-qualified user event operator",
+	  ":user:foo", ":user:foo",
+	  TRUE, FALSE
+	},
+
+	{ "fully-qualified system event with fully-qualified user event operator",
+	  ":sys:foo", ":user:foo",
+	  FALSE, FALSE
+	},
+
+	/*******************************/
+
+	{ "unqualified event with fully-qualified system operator",
+	  "foo", ":sys:foo",
+	  FALSE, TRUE
+	},
+
+	{ "any-qualified event with fully-qualified system event operator",
+	  "::foo", ":sys:foo",
+	  FALSE, TRUE 
+	},
+
+	{ "fully-qualified user event with fully-qualified system event operator",
+	  ":user:foo", ":sys:foo",
+	  FALSE, FALSE
+	},
+
+	{ "fully-qualified system event with fully-qualified system event operator",
+	  ":sys:foo", ":sys:foo",
+	  TRUE, TRUE
+	},
+
+	/*******************************/
+
+	{ NULL, NULL, NULL, FALSE, FALSE }
+};
+
+/**
+ * getpid_pid:
+ *
+ * PID that our version of getpid () will impersonate.
+ **/
+static int getpid_pid = 0;
+
+void
+setpid (pid_t pid)
+{
+    getpid_pid = pid;
+}
+
+pid_t
+getpid (void)
+{
+    return (pid_t) getpid_pid;
+}
+
 void
 test_operator_new (void)
 {
@@ -525,15 +655,85 @@
 }
 
 void
+test_match_prefix (const EventMatchPrefixTest *test)
+{
+	nih_local Event         *event = NULL;
+	nih_local EventOperator *oper = NULL;
+	int                      ret;
+	nih_local char          *feature = NULL;
+
+	assert (test);
+
+	NIH_MUST (nih_strcat_sprintf (&feature, NULL, "%s (pid %c= 1)",
+				test->feature,
+				getpid () == 1 ? '=' : '!'));
+
+	TEST_FEATURE (feature);
+
+	event = event_new (NULL, test->event_name, NULL);
+	TEST_NE_P (event, NULL);
+
+	oper = event_operator_new (NULL, EVENT_MATCH, test->oper_name, NULL);
+	TEST_NE_P (oper, NULL);
+
+	ret = event_operator_match (oper, event, NULL);
+
+	if (getpid () == 1) {
+		TEST_EQ (ret, test->pid1_match);
+	} else {
+		TEST_EQ (ret, test->session_match);
+	}
+}
+
+void
+test_operator_match_prefixes (void)
+{
+	EventMatchPrefixTest  *test;
+
+	/* the pid we'll pretend to be */
+	pid_t fake_pid = 1234;
+
+	TEST_GROUP ("EventOperator prefix matching");
+
+	for (test = prefix_tests; test && test->feature; test++) {
+
+		setpid (fake_pid);
+		assert (getpid () == fake_pid);
+
+		test_match_prefix (test);
+
+		/* pretend to be PID 1 */
+		setpid ((pid_t)1);
+		assert (getpid () == 1);
+
+		test_match_prefix (test);
+	}
+
+	setpid (fake_pid);
+}
+
+void
 test_operator_match (void)
 {
 	EventOperator *oper;
 	Event         *event;
+	Event         *event_any;
+	Event         *event_fqu;
+	Event         *event_fqs;
 	char          *env1[5], *env2[5], *env[4];
 
 	TEST_FUNCTION ("event_operator_match");
 	event = event_new (NULL, "foo", NULL);
 
+	event_any = event_new (NULL, "::foo", NULL);
+	TEST_NE_P (event_any, NULL);
+
+	event_fqu = event_new (NULL, ":user:foo", NULL);
+	TEST_NE_P (event_fqu, NULL);
+
+	event_fqs = event_new (NULL, ":sys:foo", NULL);
+	TEST_NE_P (event_fqs, NULL);
+
 	/* Check that two events with different names do not match. */
 	TEST_FEATURE ("with different name events");
 	oper = event_operator_new (NULL, EVENT_MATCH, "bar", NULL);
@@ -548,10 +748,14 @@
 	oper = event_operator_new (NULL, EVENT_MATCH, "foo", NULL);
 
 	TEST_TRUE (event_operator_match (oper, event, NULL));
-
-
+	nih_free (oper);
+
+	/************************************************************/
 	/* Check that two events with the same environment lists match. */
 	TEST_FEATURE ("with same environment lists");
+
+	oper = event_operator_new (NULL, EVENT_MATCH, "foo", NULL);
+
 	event->env = env1;
 	event->env[0] = "FRODO=foo";
 	event->env[1] = "BILBO=bar";
@@ -832,9 +1036,11 @@
 
 	nih_free (oper);
 	nih_free (event);
+	nih_free (event_any);
+	nih_free (event_fqu);
+	nih_free (event_fqs);
 }
 
-
 void
 test_operator_handle (void)
 {
@@ -1423,6 +1629,7 @@
 	test_operator_destroy ();
 	test_operator_update ();
 	test_operator_match ();
+	test_operator_match_prefixes ();
 	test_operator_handle ();
 	test_operator_environment ();
 	test_operator_events ();

=== modified file 'util/man/initctl.8'
--- util/man/initctl.8	2013-01-31 16:51:49 +0000
+++ util/man/initctl.8	2013-02-06 11:05:27 +0000
@@ -331,9 +331,37 @@
 .B start
 for a discussion on instances.
 
-There is no limitation on the event names that may be emitted with this
-command, you are free to invent new events and use them in your job
-configurations.
+Event names can take three forms: fully-qualified, implicitly-qualified,
+and unqualified. See
+.BR init (5)
+for further details.
+
+Note that attempting to emit an event with a leading colon (\fI:\fR)
+that does not match one of the existing recognised forms (\fI:sys:\fR,
+\fI:user:\fR and \fI::\fR) will result in an error.
+.B This is a change in behaviour from older versions of Upstart.
+
+Unqualified events are assumed to be implicitly qualified to the
+appropriate namespace (\fI:sys:\fR for system-level events and
+\fI:user:\fR for for user session-level events).
+
+System-level events are made accessible to Session Inits by default via the 
+.BR upstart\-event\-bridge (8) "" "."
+
+To stop system-level events from being visible to Session Inits, specify
+the 
+.I :sys:
+prefix.
+
+Events may be prefixed by
+.I ::
+for explicit propagation to all subsidary namespaces (that is
+system-events with such a prefix are passed to Session Inits).
+
+Note that events emitted with a
+.I :sys:
+prefix at the session level have no impact on jobs running at the system
+level but may impact jobs running at the session level.
 
 The most well\-known event used by the default Upstart configuration is
 the
@@ -784,3 +812,4 @@
 .BR init (8)
 .BR telinit (8)
 .BR shutdown (8)
+.BR upstart\-event\-bridge (8)

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

Reply via email to