James Hunt has proposed merging lp:~jamesodhunt/upstart/initctl-list-sessions 
into lp:upstart.

Requested reviews:
  Upstart Reviewers (upstart-reviewers)

For more details, see:
https://code.launchpad.net/~jamesodhunt/upstart/initctl-list-sessions/+merge/144958

Implementation of the 'initctl list-sessions' command.
-- 
https://code.launchpad.net/~jamesodhunt/upstart/initctl-list-sessions/+merge/144958
Your team Upstart Reviewers is requested to review the proposed merge of 
lp:~jamesodhunt/upstart/initctl-list-sessions into lp:upstart.
=== modified file 'ChangeLog'
--- ChangeLog	2013-01-24 08:37:53 +0000
+++ ChangeLog	2013-01-25 15:38:24 +0000
@@ -1,3 +1,49 @@
+2013-01-25  James Hunt  <[email protected]>
+
+	* init/control.c: control_session_file_create(): Simplified.
+	* init/xdg.c: Added check for INITCTL_BUILD to hide certain symbols when
+	  building with initctl.
+	* util/Makefile.am:
+	  - Define INITCTL_BUILD.
+	  - Make use of xdg.[ch] in build of initctl and its test.
+	* util/initctl.c:
+	  - list_session_action(): Implementation of 'list-sessions' command.
+	* util/man/initctl.8: Updated for 'list-sessions' command.
+	* util/tests/test_initctl.c:
+	  - _start_upstart(): Replacement for _START_UPSTART() macro.
+	  - start_upstart_common(): Start an instance with common options.
+	  - start_upstart(): Simplest way to start an instance.
+	  - START_UPSTART(): Now calls start_upstart_common().
+	  - test_list_sessions(): Test 'list-sessions' command.
+
+2013-01-25  James Hunt  <[email protected]>
+
+	* init/tests/test_xdg.c: Added test_get_session_dir().
+	* init/xdg.c: get_home_subdir(): Handle unset 'HOME' immediately.
+	* init/control.c: Make use of SESSION_EXT.
+	* init/man/init.5: Added session files.
+	* init/paths.h:
+	  - Comments.
+	  - Added SESSION_EXT.
+
+2013-01-24  James Hunt  <[email protected]>
+
+	* init/control.c:
+	  - control_init(): Create session file in user mode.
+	  - control_cleanup(): New function for cleanup activities.
+	  - control_session_file_create(): Create session file containing
+	    UPSTART_SESSION details.
+	  - control_session_file_remove(): Delete the session file.
+	* init/main.c: Call control_cleanup() to remove session file.
+	* init/paths.h: Added INIT_XDG_SESSION_SUBDIR and SESSION_ENV.
+	* init/xdg.c:
+	  - get_subdir(): Refactor of get_home_subdir().
+	  - get_home_subdir(): Now calls get_subdir().
+	  - Replaced mkdir mode values with INIT_XDG_PATH_MODE.
+	  - xdg_get_runtime_dir(): Obtain XDG_RUNTIME_DIR value.
+	  - get_session_dir(): Obtain path to session directory.
+	* init/xdg.h: Added INIT_XDG_PATH_MODE.
+
 2013-01-21  Dmitrijs Ledkovs  <[email protected]>
 
 	* init/xdg.[ch]: add xdg_get_cache_home and get_user_log_dir

=== modified file 'init/control.c'
--- init/control.c	2013-01-23 12:56:00 +0000
+++ init/control.c	2013-01-25 15:38:24 +0000
@@ -56,15 +56,19 @@
 #include "errors.h"
 #include "state.h"
 #include "event.h"
+#include "paths.h"
+#include "xdg.h"
 
 #include "com.ubuntu.Upstart.h"
 
 /* Prototypes for static functions */
-static int   control_server_connect (DBusServer *server, DBusConnection *conn);
-static void  control_disconnected   (DBusConnection *conn);
-static void  control_register_all   (DBusConnection *conn);
+static int   control_server_connect      (DBusServer *server, DBusConnection *conn);
+static void  control_disconnected        (DBusConnection *conn);
+static void  control_register_all        (DBusConnection *conn);
 
-static void  control_bus_flush      (void);
+static void  control_bus_flush           (void);
+static void  control_session_file_create (void);
+static void  control_session_file_remove (void);
 
 /**
  * use_session_bus:
@@ -108,6 +112,8 @@
 /* External definitions */
 extern int user_mode;
 
+extern char *session_file;
+
 /**
  * control_init:
  *
@@ -120,14 +126,27 @@
 		control_conns = NIH_MUST (nih_list_new (NULL));
 
 	if (! control_server_address) {
-		if (user_mode)
+		if (user_mode) {
 			NIH_MUST (nih_strcat_sprintf (&control_server_address, NULL,
 					    "%s-session/%d/%d", DBUS_ADDRESS_UPSTART, getuid (), getpid ()));
-		else
-			control_server_address = nih_strdup (NULL, DBUS_ADDRESS_UPSTART);
+
+			control_session_file_create ();
+		} else {
+			control_server_address = NIH_MUST (nih_strdup (NULL, DBUS_ADDRESS_UPSTART));
+		}
 	}
 }
 
+/**
+ * control_cleanup:
+ *
+ * Perform cleanup operations.
+ **/
+void
+control_cleanup (void)
+{
+	control_session_file_remove ();
+}
 
 /**
  * control_server_open:
@@ -1117,3 +1136,55 @@
 		NIH_ZERO (control_emit_restarted (conn, DBUS_PATH_UPSTART));
 	}
 }
+
+/**
+ * control_session_file_create:
+ *
+ * Create session file if possible.
+ *
+ * Errors are not fatal - the file is just not created.
+ **/
+static void
+control_session_file_create (void)
+{
+	nih_local char *session_dir = NULL;
+	FILE           *f;
+	int             ret;
+
+	nih_assert (control_server_address);
+
+	session_dir = get_session_dir ();
+
+	if (! session_dir)
+		return;
+
+	NIH_MUST (nih_strcat_sprintf (&session_file, NULL, "%s/%d%s",
+				session_dir, (int)getpid (), SESSION_EXT));
+
+	f = fopen (session_file, "w");
+	if (! f) {
+		nih_error ("%s: %s", _("unable to create session file"), session_file);
+		return;
+	}
+
+	ret = fprintf (f, SESSION_ENV "=%s\n", control_server_address);
+
+	if (ret < 0)
+		nih_error ("%s: %s", _("unable to write session file"), session_file);
+
+	fclose (f);
+}
+
+/**
+ * control_session_file_remove:
+ *
+ * Delete session file.
+ *
+ * Errors are not fatal.
+ **/
+static void
+control_session_file_remove (void)
+{
+	if (session_file)
+		(void)unlink (session_file);
+}

=== modified file 'init/control.h'
--- init/control.h	2012-12-14 23:43:15 +0000
+++ init/control.h	2013-01-25 15:38:24 +0000
@@ -54,6 +54,7 @@
 
 
 void control_init                 (void);
+void control_cleanup              (void);
 
 int  control_server_open          (void)
 	__attribute__ ((warn_unused_result));

=== modified file 'init/main.c'
--- init/main.c	2013-01-23 12:40:36 +0000
+++ init/main.c	2013-01-25 15:38:24 +0000
@@ -651,6 +651,8 @@
 	nih_main_loop_interrupt ();
 	ret = nih_main_loop ();
 
+	control_cleanup ();
+
 	return ret;
 }
 

=== modified file 'init/man/init.5'
--- init/man/init.5	2013-01-23 12:40:36 +0000
+++ init/man/init.5	2013-01-25 15:38:24 +0000
@@ -1050,6 +1050,9 @@
 .TP
 .I $XDG_CACHE_HOME/upstart/*.log
 Default location of user session job output logs.
+.TP
+.I $XDG_RUNTIME_DIR/upstart/sessions/*.session
+Location of session files created when running in User Session mode.
 .RE
 .\"
 .SH AUTHOR

=== modified file 'init/paths.h'
--- init/paths.h	2012-12-17 15:46:35 +0000
+++ init/paths.h	2013-01-25 15:38:24 +0000
@@ -116,6 +116,16 @@
 #endif
 
 /**
+ * INIT_XDG_SESSION_SUBDIR:
+ *
+ * Directory below XDG_RUNTIME_DIR/INIT_XDG_SUBDIR used to
+ * store session details.
+ **/
+#ifndef INIT_XDG_SESSION_SUBDIR
+#define INIT_XDG_SESSION_SUBDIR "sessions"
+#endif
+
+/**
  * SHELL:
  *
  * This is the shell binary used whenever we need special processing for
@@ -166,13 +176,25 @@
 #define LOGDIR_ENV "UPSTART_LOGDIR" 
 #endif
 
+/**
+ * SESSION_ENV:
+ *
+ * Environment variable that is set when running as a Session Init.
+ **/
+#ifndef SESSION_ENV
+#define SESSION_ENV "UPSTART_SESSION"
+#endif
 
 /**
+ * CONF_EXT_STD:
+ *
  * File extension for standard configuration files.
  **/
 #define CONF_EXT_STD ".conf"
 
 /**
+ * CONF_EXT_OVERRIDE:
+ *
  * File extension for override files.
  *
  * Note that override files are not stored in the ConfSource 'files' hash:
@@ -182,6 +204,15 @@
 #define CONF_EXT_OVERRIDE ".override"
 
 /**
+ * SESSION_EXT:
+ *
+ * File extension for session files.
+ **/
+#ifndef SESSION_EXT
+#define SESSION_EXT ".session"
+#endif
+
+/**
  * Determine if specified path extension representes a standard
  * configuration file.
  *

=== modified file 'init/tests/test_xdg.c'
--- init/tests/test_xdg.c	2013-01-22 12:01:56 +0000
+++ init/tests/test_xdg.c	2013-01-25 15:38:24 +0000
@@ -435,6 +435,49 @@
 	rmdir (dirname);
 }
 
+void
+test_get_session_dir (void)
+{
+	char dirname[PATH_MAX];
+	char         *expected;
+	char             *path;
+
+	TEST_FUNCTION ("get_session_dir");
+
+	TEST_FEATURE ("with XDG_RUNTIME_DIR set");
+
+	TEST_FILENAME (dirname);
+	assert0 (setenv ("XDG_RUNTIME_DIR", dirname, 1));
+	TEST_EQ (mkdir (dirname, 0755), 0);
+
+	expected = nih_sprintf (NULL, "%s/upstart/sessions", dirname);
+
+	TEST_ALLOC_FAIL {
+		path = get_session_dir ();
+		if (test_alloc_failed) {
+			TEST_EQ_P (path, NULL);
+		} else {
+			TEST_EQ_STR (path, expected);
+			_test_dir_created (expected);
+			nih_free (path);
+		}
+	}
+
+	TEST_FEATURE ("with XDG_RUNTIME_DIR unset");
+	assert0 (unsetenv ("XDG_RUNTIME_DIR"));
+
+	/* no fallback */
+	path = get_session_dir ();
+	TEST_EQ_P (path, NULL);
+
+	rmdir (expected);
+	nih_free (expected);
+	path = nih_sprintf (NULL, "%s/upstart", dirname);
+	rmdir (path);
+	nih_free (path);
+	rmdir (dirname);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -445,6 +488,7 @@
 	test_get_user_upstart_dirs ();
 	test_get_cache_home ();
 	test_get_user_log_dir ();
+	test_get_session_dir ();
 
 	return 0;
 }

=== modified file 'init/xdg.c'
--- init/xdg.c	2013-01-22 12:01:56 +0000
+++ init/xdg.c	2013-01-25 15:38:24 +0000
@@ -34,6 +34,8 @@
 #include "paths.h"
 #include "xdg.h"
 
+#ifndef INITCTL_BUILD
+
 /**
  * user_mode:
  *
@@ -42,37 +44,76 @@
 int user_mode = FALSE;
 
 /**
- * get_home_subdir:
- * @suffix: sub-directory name
- * @create: flag to create sub-directory
- * 
- * Construct path to @suffix directory in user's HOME dir. If @create
- * flag is TRUE, also attempt to create that directory. Errors upon
- * directory creation are ignored.
- * 
- * Returns: newly-allocated path, or NULL on error.
+ * session_file:
+ *
+ * Full path to file containing UPSTART_SESSION details (only set when
+ * user_mode in operation).
+ *
+ * File is created on startup and removed on clean shutdown.
+ **/
+const char *session_file = NULL;
+
+#endif /* INITCTL_BUILD */
+
+/**
+ * get_subdir:
+ * @dir: initial directory,
+ * @suffix: sub-directory of @dir,
+ * @create: flag to create sub-directory.
+ * 
+ * Construct path by appending @suffix to @dir. If @create
+ * flag is TRUE, also attempt to create that directory.
+ *
+ * Errors upon directory creation are ignored.
+ * 
+ * Returns: Newly-allocated path, or NULL on error.
  **/
 char *
-get_home_subdir (const char * suffix, int create)
+get_subdir (const char *dir, const char *suffix, int create)
 {
-	char *dir;
+	char *newdir;
+	nih_assert (dir != NULL);
 	nih_assert (suffix != NULL);
 	nih_assert (suffix[0]);
 	
-	dir = getenv ("HOME");
 	if (dir && dir[0] == '/') {
-		dir = nih_sprintf (NULL, "%s/%s", dir, suffix);
-		if (! dir)
+		newdir = nih_sprintf (NULL, "%s/%s", dir, suffix);
+		if (! newdir)
 			return NULL;
 		if (create)
-			mkdir (dir, 0700);
-		return dir;
+			mkdir (newdir, INIT_XDG_PATH_MODE);
+		return newdir;
 	}
 
 	return NULL;
 }
 
 /**
+ * get_home_subdir:
+ *
+ * @suffix: sub-directory name,
+ * @create: flag to create sub-directory.
+ *
+ * Construct path to @suffix directory in user's HOME directory.
+ * If @create is TRUE, also attempt to create that directory.
+ *
+ * Errors upon directory creation are ignored.
+ * 
+ * Returns: Newly-allocated path, or NULL on error.
+ **/
+char *
+get_home_subdir (const char *suffix, int create)
+{
+	char *env;
+
+	env = getenv ("HOME");
+	if (! env)
+		return NULL;
+
+	return get_subdir (env, suffix, create);
+}
+
+/**
  * xdg_get_cache_home:
  *
  * Determine an XDG compliant XDG_CACHE_HOME
@@ -82,13 +123,12 @@
 char *
 xdg_get_cache_home (void)
 {
-	nih_local char  **env = NULL;
 	char             *dir;
 
 	dir = getenv ("XDG_CACHE_HOME");
 	
 	if (dir && dir[0] == '/') {
-		mkdir (dir, 0700);
+		mkdir (dir, INIT_XDG_PATH_MODE);
 		dir = nih_strdup (NULL, dir);
 		return dir;
 	}
@@ -115,13 +155,12 @@
 char *
 xdg_get_config_home (void)
 {
-	nih_local char  **env = NULL;
-	char             *dir;
+	char  *dir;
 
 	dir = getenv ("XDG_CONFIG_HOME");
 	
 	if (dir && dir[0] == '/') {
-		mkdir (dir, 0700);
+		mkdir (dir, INIT_XDG_PATH_MODE);
 		dir = nih_strdup (NULL, dir);
 		return dir;
 	}
@@ -136,6 +175,60 @@
 }
 
 /**
+ * xdg_get_runtime_dir:
+ *
+ * Determine an XDG compliant XDG_RUNTIME_DIR.
+ *
+ * Returns: newly-allocated path, or NULL on error.
+ **/
+char *
+xdg_get_runtime_dir (void)
+{
+	char *dir;
+
+	dir = getenv ("XDG_RUNTIME_DIR");
+
+	if (dir && dir[0] == '/') {
+		mkdir (dir, INIT_XDG_PATH_MODE);
+		dir = nih_strdup (NULL, dir);
+		return dir;
+	}
+
+	return dir;
+}
+
+/**
+ * get_session_dir:
+ *
+ * Determine full path to XDG-compliant session directory used to store
+ * session files.
+ *
+ * Returns: Newly-allocated path, or NULL on error.
+ **/
+char *
+get_session_dir (void)
+{
+	nih_local char  *runtime_dir = NULL;
+	nih_local char  *dir = NULL;
+	char            *session_dir;
+
+	runtime_dir = xdg_get_runtime_dir ();
+
+	if (runtime_dir && runtime_dir[0] == '/') {
+		dir = get_subdir (runtime_dir, INIT_XDG_SUBDIR, TRUE);
+		if (! dir)
+			return NULL;
+
+		session_dir = get_subdir (dir, INIT_XDG_SESSION_SUBDIR,
+				TRUE);
+
+		return session_dir;
+	}
+
+	return NULL;
+}
+
+/**
  * xdg_get_config_dirs:
  *
  * Determine a list of XDG compliant XDG_CONFIG_DIRS
@@ -189,7 +282,7 @@
 	if (path && path[0]) {
 	        if (! nih_strcat_sprintf (&path, NULL, "/%s", INIT_XDG_SUBDIR))
 			goto error;
-		mkdir (path, 0700);
+		mkdir (path, INIT_XDG_PATH_MODE);
 		if (! nih_str_array_add (&all_dirs, NULL, NULL, path))
 			goto error;
 		nih_free (path);
@@ -261,7 +354,7 @@
 		dir = nih_sprintf (NULL, "%s/%s", path, INIT_XDG_SUBDIR);
 		if (! dir)
 			return NULL;
-		mkdir (dir, 0700);
+		mkdir (dir, INIT_XDG_PATH_MODE);
 		return dir;
 	}
 	return NULL;

=== modified file 'init/xdg.h'
--- init/xdg.h	2013-01-23 12:56:00 +0000
+++ init/xdg.h	2013-01-25 15:38:24 +0000
@@ -23,8 +23,19 @@
 #include "paths.h"
 #include <nih/macros.h>
 
+/**
+ * INIT_XDG_PATH_MODE:
+ *
+ * Absolute mode to create XDG-compliant directory elements with.
+ **/
+#define INIT_XDG_PATH_MODE 0700
+
 NIH_BEGIN_EXTERN
 
+char *    get_env_subdir (const char *envvar, const char *suffix,
+			  int create)
+	__attribute__ ((malloc, warn_unused_result));
+
 char *    get_home_subdir        (const char * suffix, int create)
 	__attribute__ ((malloc, warn_unused_result));
 
@@ -34,6 +45,9 @@
 char *    xdg_get_cache_home     (void)
 	__attribute__ ((malloc, warn_unused_result));
 
+char *    xdg_get_runtime_dir (void)
+	__attribute__ ((malloc, warn_unused_result));
+
 char **   xdg_get_config_dirs    (void)
 	__attribute__ ((malloc, warn_unused_result));
 
@@ -43,6 +57,9 @@
 char *    get_user_log_dir       (void)
 	__attribute__ ((malloc, warn_unused_result));
 
+char *    get_session_dir        (void)
+	__attribute__ ((malloc, warn_unused_result));
+
 NIH_END_EXTERN
 
 #endif /* INIT_XDG_H */

=== modified file 'util/Makefile.am'
--- util/Makefile.am	2012-12-12 14:13:08 +0000
+++ util/Makefile.am	2013-01-25 15:38:24 +0000
@@ -7,6 +7,7 @@
 
 AM_CPPFLAGS = \
 	-DLOCALEDIR="\"$(localedir)\"" \
+	-DINITCTL_BUILD \
 	-DSBINDIR="\"$(sbindir)\"" \
 	-I$(top_builddir) -I$(top_srcdir) -iquote$(builddir) -iquote$(srcdir) \
 	-I$(top_srcdir)/intl
@@ -29,7 +30,8 @@
 	telinit
 
 initctl_SOURCES = \
-	initctl.c initctl.h
+	initctl.c initctl.h \
+	$(top_srcdir)/init/xdg.c $(top_srcdir)/init/xdg.h
 nodist_initctl_SOURCES = \
 	$(com_ubuntu_Upstart_OUTPUTS) \
 	$(com_ubuntu_Upstart_Job_OUTPUTS) \
@@ -192,7 +194,10 @@
 
 check_PROGRAMS = $(TESTS)
 
-test_initctl_SOURCES = tests/test_initctl.c initctl.c
+test_initctl_SOURCES = \
+	tests/test_initctl.c \
+	initctl.c \
+	$(top_srcdir)/init/xdg.c $(top_srcdir)/init/xdg.h
 test_initctl_CFLAGS = $(AM_CFLAGS) -DTEST
 test_initctl_LDADD = \
 	com.ubuntu.Upstart.o \

=== modified file 'util/initctl.c'
--- util/initctl.c	2013-01-22 16:50:19 +0000
+++ util/initctl.c	2013-01-25 15:38:24 +0000
@@ -30,6 +30,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <fnmatch.h>
+#include <pwd.h>
+#include <dirent.h>
+#include <ctype.h>
 
 #include <nih/macros.h>
 #include <nih/alloc.h>
@@ -41,6 +44,7 @@
 #include <nih/error.h>
 #include <nih/hash.h>
 #include <nih/tree.h>
+#include <nih/file.h>
 
 #include <nih-dbus/dbus_error.h>
 #include <nih-dbus/dbus_proxy.h>
@@ -53,7 +57,8 @@
 #include "com.ubuntu.Upstart.Job.h"
 #include "com.ubuntu.Upstart.Instance.h"
 
-#include "../init/events.h"
+#include "init/events.h"
+#include "init/xdg.h"
 #include "initctl.h"
 
 
@@ -126,6 +131,7 @@
 int check_config_action           (NihCommand *command, char * const *args);
 int usage_action                  (NihCommand *command, char * const *args);
 int notify_disk_writeable_action  (NihCommand *command, char * const *args);
+int list_sessions_action          (NihCommand *command, char * const *args);
 
 /**
  * use_dbus:
@@ -1706,6 +1712,123 @@
 	return 1;
 }
 
+
+/**
+ * list_sessions_action:
+ * @command: NihCommand invoked,
+ * @args: command-line arguments.
+ *
+ * This function is called for the "list-sessions" command.
+ *
+ * Unlike other commands, this does not attempt to connect to Upstart.
+ *
+ * Returns: command exit status.
+ **/
+int
+list_sessions_action (NihCommand *command, char * const *args)
+{
+	nih_local const char *session_dir = NULL;
+	DIR                  *dir;
+	struct dirent        *ent;
+
+	nih_assert (command);
+	nih_assert (args);
+	
+	session_dir = get_session_dir ();
+
+	if (! session_dir) {
+		nih_error (_("Unable to query session directory"));
+		return 1;
+	}
+
+	dir = opendir (session_dir);
+	if (! dir)
+		goto error;
+
+	while ((ent = readdir (dir))) {
+		nih_local char  *contents = NULL;
+		size_t           len;
+		nih_local char  *path = NULL;
+		pid_t            pid;
+		nih_local char  *name = NULL;
+		char            *session;
+		char            *p;
+		char            *ext;
+		char            *file;
+		int              all_digits = TRUE;
+
+		file = ent->d_name;
+
+		if (! strcmp (file, ".") || ! strcmp (file, ".."))
+			continue;
+
+		ext = p = strchr (file, '.');
+
+		/* No extension */
+		if (! ext)
+			continue;
+
+		/* Invalid extension */
+		if (strcmp (ext, ".session"))
+			continue;
+
+		NIH_MUST (nih_strncat (&name, NULL, file, (p - file)));
+
+		for (p = name; p && *p; p++) {
+			if (! isdigit (*p)) {
+				all_digits = FALSE;
+				break;
+			}
+		}
+
+		/* Invalid name */
+		if (! all_digits)
+			continue;
+
+		pid = (pid_t) atol (name);
+
+		NIH_MUST (nih_strcat_sprintf (&path, NULL, "%s/%s", session_dir, file));
+
+		contents = nih_file_read (NULL, path, &len);
+
+		if (! contents)
+			continue;
+
+		if (contents[len-1] == '\n')
+			contents[len-1] = '\0';
+
+		p = strchr (contents, '=');
+		if (! p)
+			continue;
+
+		/* Invalid contents */
+		if (strncmp (contents, "UPSTART_SESSION", (p - contents)))
+			continue;
+
+		session = p + 1;
+
+		if (! session || ! *session)
+			continue;
+
+		if (kill (pid, 0)) {
+			nih_info ("%s: %s", _("Ignoring stale session file"), path);
+			continue;
+		}
+
+		nih_message ("%d %s", (int)pid, session);
+	}
+
+	closedir (dir);
+
+	return 0;
+
+error:
+	nih_error ("unable to determine sessions");
+	return 1;
+
+}
+
+
 static void
 start_reply_handler (char **         job_path,
 		     NihDBusMessage *message,
@@ -2660,6 +2783,11 @@
 			  "disk is writeable are flushed to disk"),
 	  NULL, NULL, notify_disk_writeable_action },
 
+	{ "list-sessions", NULL,
+	  N_("List all sessions."),
+	  N_("Displays list of running Session Init sessions"),
+	  NULL, NULL, list_sessions_action },
+
 	NIH_COMMAND_LAST
 };
 

=== modified file 'util/man/initctl.8'
--- util/man/initctl.8	2013-01-22 17:14:40 +0000
+++ util/man/initctl.8	2013-01-25 15:38:24 +0000
@@ -555,6 +555,16 @@
 .RE
 .\"
 .TP
+.B list\-sessions
+List the pid of the Session Init process followed by the value of
+.B UPSTART_SESSION
+in use for that session separted by a space character. Session files
+relating to non-longer running Session Init processes are considered
+\(aqstale\(aq and are not listed (although when run using
+.BR \-\-verbose ","
+the full path of the stale session file is displayed).
+.\"
+.TP
 .B usage
 .I JOB
 .RI [ KEY=VALUE ]...

=== modified file 'util/tests/test_initctl.c'
--- util/tests/test_initctl.c	2013-01-22 18:17:04 +0000
+++ util/tests/test_initctl.c	2013-01-25 15:38:24 +0000
@@ -44,6 +44,7 @@
 #include <nih/main.h>
 #include <nih/command.h>
 #include <nih/error.h>
+#include <nih/file.h>
 #include <nih/string.h>
 
 #include "dbus/upstart.h"
@@ -104,61 +105,6 @@
 }
 
 /**
- * _START_UPSTART:
- *
- * @pid: pid_t that will contain pid of running instance on success,
- * @confdir: full path to configuration directory, or NULL to use
- *           the default,
- * @logdir: full path to log directory, or NULL to use the default.
- *
- * Start an instance of Upstart. Fork errors are fatal. Waits for a
- * reasonable amount of time for Upstart to appear on D-Bus.
- **/
-#define _START_UPSTART(pid, confdir, logdir)                         \
-{                                                                    \
-	nih_local char  **args = NULL;                               \
-	nih_local char   *conf_opts = NULL;                          \
-	nih_local char   *log_opts = NULL;                           \
-	                                                             \
-	TEST_TRUE (getenv ("DBUS_SESSION_BUS_ADDRESS"));             \
-	                                                             \
-	args = NIH_MUST (nih_str_array_new (NULL));                  \
-	                                                             \
-	NIH_MUST (nih_str_array_add (&args, NULL, NULL,              \
-				UPSTART_BINARY));                    \
-	                                                             \
-	NIH_MUST (nih_str_array_add (&args, NULL, NULL,              \
-				"--session"));                       \
-	                                                             \
-	NIH_MUST (nih_str_array_add (&args, NULL, NULL,              \
-				"--no-startup-event"));              \
-	                                                             \
-	NIH_MUST (nih_str_array_add (&args, NULL, NULL,              \
-				"--no-sessions"));                   \
-	                                                             \
-	if (confdir != NULL) {                                       \
-		NIH_MUST (nih_str_array_add (&args, NULL, NULL,      \
-				"--confdir"));                       \
-		NIH_MUST (nih_str_array_add (&args, NULL, NULL,      \
-				confdir));                           \
-	}                                                            \
-	                                                             \
-	if (logdir != NULL) {                                        \
-		NIH_MUST (nih_str_array_add (&args, NULL, NULL,      \
-				"--logdir"));                        \
-		NIH_MUST (nih_str_array_add (&args, NULL, NULL,      \
-				logdir));                            \
-	}                                                            \
-	                                                             \
-	TEST_NE (pid = fork (), -1);                                 \
-	                                                             \
-	if (pid == 0)                                                \
-		execv (args[0], args);                               \
-	                                                             \
-	WAIT_FOR_UPSTART ();                                         \
-}
-
-/**
  * START_UPSTART:
  *
  * @pid: pid_t that will contain pid of running instance on success.
@@ -166,7 +112,7 @@
  * Start an instance of Upstart and return PID in @pid.
  **/
 #define START_UPSTART(pid)                                           \
-	_START_UPSTART (pid, NULL, NULL)
+	start_upstart_common (&(pid), NULL, NULL, NULL)
 
 /**
  * KILL_UPSTART:
@@ -310,6 +256,104 @@
 }
 
 /**
+ * _start_upstart:
+ *
+ * @pid: PID of running instance,
+ * @args: optional list of arguments to specify.
+ *
+ * Start an instance of Upstart.
+ *
+ * If the instance fails to start, abort(3) is called.
+ **/
+void
+_start_upstart (pid_t *pid, char * const *args)
+{
+	nih_local char  **argv = NULL;
+
+	assert (pid);
+
+	TEST_TRUE (getenv ("DBUS_SESSION_BUS_ADDRESS"));
+
+	argv = NIH_MUST (nih_str_array_new (NULL));
+
+	NIH_MUST (nih_str_array_add (&argv, NULL, NULL,
+				UPSTART_BINARY));
+
+	if (args)
+		NIH_MUST (nih_str_array_append (&argv, NULL, NULL, args));
+
+	TEST_NE (*pid = fork (), -1);
+
+	if (*pid == 0)
+		execv (argv[0], argv);
+
+	WAIT_FOR_UPSTART ();
+}
+
+/**
+ * start_upstart_common:
+ *
+ * @pid: PID of running instance,
+ * @confdir: full path to configuration directory,
+ * @logdir: full path to log directory,
+ * @extra: optional extra arguments.
+ *
+ * Wrapper round _start_upstart() which specifies common options.
+ **/
+void
+start_upstart_common (pid_t *pid, const char *confdir,
+		      const char *logdir, char * const *extra)
+{
+	nih_local char  **args = NULL;
+
+	assert (pid);
+
+	args = NIH_MUST (nih_str_array_new (NULL));
+
+	NIH_MUST (nih_str_array_add (&args, NULL, NULL,
+				"--session"));
+
+	NIH_MUST (nih_str_array_add (&args, NULL, NULL,
+				"--no-startup-event"));
+
+	NIH_MUST (nih_str_array_add (&args, NULL, NULL,
+				"--no-sessions"));
+
+	if (confdir) {
+		NIH_MUST (nih_str_array_add (&args, NULL, NULL,
+					"--confdir"));
+		NIH_MUST (nih_str_array_add (&args, NULL, NULL,
+					confdir));
+	}
+
+	if (logdir) {
+		NIH_MUST (nih_str_array_add (&args, NULL, NULL,
+					"--logdir"));
+		NIH_MUST (nih_str_array_add (&args, NULL, NULL,
+					logdir));
+	}
+
+	if (extra)
+		NIH_MUST (nih_str_array_append (&args, NULL, NULL, extra));
+
+	_start_upstart (pid, args);
+}
+
+/**
+ * start_upstart:
+ *
+ * @pid: PID of running instance.
+ *
+ * Wrapper round _start_upstart() which just runs an instance with no
+ * options.
+ **/
+void
+start_upstart (pid_t *pid)
+{
+	start_upstart_common (pid, NULL, NULL, NULL);
+}
+
+/**
  * job_to_pid:
  *
  * @job: job name.
@@ -11324,7 +11368,7 @@
 	/*******************************************************************/
 	TEST_FEATURE ("single job producing output across a re-exec");
 
-	_START_UPSTART (upstart_pid, confdir, logdir);
+	start_upstart_common (&upstart_pid, confdir, logdir, NULL);
 
 	contents = nih_sprintf (NULL, 
 			"pre-start exec echo pre-start\n"
@@ -11481,6 +11525,109 @@
 }
 
 void
+test_list_sessions (void)
+{
+	char             dirname[PATH_MAX];
+	char             confdir[PATH_MAX];
+	nih_local char  *cmd = NULL;
+	nih_local char  **args = NULL;
+	pid_t            upstart_pid = 0;
+	pid_t            dbus_pid    = 0;
+	char           **output;
+	size_t           lines;
+	struct stat      statbuf;
+	nih_local char  *contents = NULL;
+	nih_local char  *session_file = NULL;
+	nih_local char  *path = NULL;
+	nih_local char  *expected = NULL;
+	size_t           len;
+	char            *value;
+
+	TEST_GROUP ("list-sessions");
+
+	/*******************************************************************/
+	TEST_FEATURE ("with no instances");
+
+	cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
+	TEST_NE_P (cmd, NULL);
+	RUN_COMMAND (NULL, cmd, &output, &lines);
+	TEST_EQ (lines, 0);
+
+	/*******************************************************************/
+	TEST_FEATURE ("with 1 running instance");
+
+        TEST_FILENAME (dirname);
+        TEST_EQ (mkdir (dirname, 0755), 0);
+
+        TEST_FILENAME (confdir);
+        TEST_EQ (mkdir (confdir, 0755), 0);
+
+	/* Use the "secret" interface */
+	TEST_EQ (setenv ("UPSTART_CONFDIR", confdir, 1), 0);
+	TEST_EQ (setenv ("XDG_RUNTIME_DIR", dirname, 1), 0);
+
+	args = NIH_MUST (nih_str_array_new (NULL));
+	NIH_MUST (nih_str_array_add (&args, NULL, NULL, "--user"));
+
+	/* Start to create session file */
+	TEST_DBUS (dbus_pid);
+	start_upstart_common (&upstart_pid, NULL, NULL, args);
+
+	session_file = nih_sprintf (NULL, "%s/upstart/sessions/%d.session",
+			dirname, (int)upstart_pid);
+
+	/* session file should now have been created by Upstart */
+	TEST_EQ (stat (session_file, &statbuf), 0);
+
+	contents = nih_file_read (NULL, session_file, &len);
+	TEST_NE_P (contents, NULL);
+	TEST_TRUE (len);
+
+	/* overwrite '\n' */
+	contents[len-1] = '\0';
+
+	TEST_EQ_P (strstr (contents, "UPSTART_SESSION="), contents);
+	value  = strchr (contents, '=');
+	TEST_NE_P (value, NULL);
+
+	/* jump over '=' */
+	value++;
+	TEST_NE_P (value, NULL);
+
+	expected = nih_sprintf (NULL, "%d %s", (int)upstart_pid, value);
+
+	cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
+	TEST_NE_P (cmd, NULL);
+	RUN_COMMAND (NULL, cmd, &output, &lines);
+	TEST_EQ (lines, 1);
+	TEST_EQ_STR (output[0], expected);
+	nih_free (output);
+
+	STOP_UPSTART (upstart_pid);
+	TEST_DBUS_END (dbus_pid);
+
+	/* Upstart cannot yet be instructed to shutdown cleanly, so for
+	 * now we have to remove the session file manually.
+	 */
+	TEST_EQ (unlink (session_file), 0);
+
+	/* Remove the directory tree the Session Init created */
+	path = NIH_MUST (nih_sprintf (NULL, "%s/upstart/sessions", dirname));
+        TEST_EQ (rmdir (path), 0);
+	path = NIH_MUST (nih_sprintf (NULL, "%s/upstart", dirname));
+        TEST_EQ (rmdir (path), 0);
+
+	/*******************************************************************/
+
+	TEST_EQ (unsetenv ("UPSTART_CONFDIR"), 0);
+
+        TEST_EQ (rmdir (dirname), 0);
+        TEST_EQ (rmdir (confdir), 0);
+
+	/*******************************************************************/
+}
+
+void
 test_show_config (void)
 {
 	char             dirname[PATH_MAX];
@@ -15239,6 +15386,7 @@
 	test_log_priority_action ();
 	test_usage ();
 	test_reexec ();
+	test_list_sessions ();
 
 	if (in_chroot () && !dbus_configured ()) {
 		fprintf(stderr, "\n\n"

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

Reply via email to