Merge authors: Dmitrijs Ledkovs (xnox) James Hunt (jamesodhunt) Steve Langasek (vorlon) Stéphane Graber (stgraber) Related merge proposals: https://code.launchpad.net/~stgraber/upstart/upstart-tests-pty/+merge/140388 proposed by: Stéphane Graber (stgraber) review: Approve - James Hunt (jamesodhunt) https://code.launchpad.net/~xnox/upstart/upstart-user-mode/+merge/140258 proposed by: Dmitrijs Ledkovs (xnox) review: Approve - James Hunt (jamesodhunt) https://code.launchpad.net/~stgraber/upstart/upstart-shell-exec/+merge/140233 proposed by: Stéphane Graber (stgraber) review: Approve - James Hunt (jamesodhunt) https://code.launchpad.net/~stgraber/upstart/upstart-shell-paths/+merge/140175 proposed by: Stéphane Graber (stgraber) review: Approve - James Hunt (jamesodhunt) https://code.launchpad.net/~vorlon/upstart/default-priority-not-always-zero/+merge/140025 proposed by: Steve Langasek (vorlon) review: Approve - James Hunt (jamesodhunt) https://code.launchpad.net/~stgraber/upstart/upstart-dbus-events/+merge/139726 proposed by: Stéphane Graber (stgraber) review: Needs Fixing - James Hunt (jamesodhunt) https://code.launchpad.net/~xnox/upstart/fix-extra-dist/+merge/139472 proposed by: Dmitrijs Ledkovs (xnox) https://code.launchpad.net/~stgraber/upstart/upstart-prctl/+merge/136759 proposed by: Stéphane Graber (stgraber) review: Approve - James Hunt (jamesodhunt) ------------------------------------------------------------ revno: 1407 [merge] committer: James Hunt <[email protected]> branch nick: upstart-setenv+getenv timestamp: Wed 2012-12-19 09:08:57 +0000 message: * Resync with lp:upstart. added: init/tests/test_xdg.c init/xdg.c init/xdg.h modified: ChangeLog configure.ac dbus/com.ubuntu.Upstart.xml init/Makefile.am init/control.c init/control.h init/event.c init/job_class.c init/job_class.h init/job_process.c init/log.c init/main.c init/man/init.5 init/man/init.8 init/paths.h init/tests/test_event.c init/tests/test_job_class.c init/tests/test_job_process.c init/tests/test_log.c init/tests/test_state.c util/Makefile.am
-- lp:upstart https://code.launchpad.net/~upstart-devel/upstart/trunk Your team Upstart Reviewers is subscribed to branch lp:upstart. To unsubscribe from this branch go to https://code.launchpad.net/~upstart-devel/upstart/trunk/+edit-subscription
=== modified file 'ChangeLog' --- ChangeLog 2012-12-11 16:45:55 +0000 +++ ChangeLog 2012-12-19 09:08:57 +0000 @@ -1,3 +1,17 @@ +2012-12-17 James Hunt <[email protected]> + + * init/man/init.5: Document that User Jobs are not supported + within a chroot environment. + +2012-12-14 Steve Langasek <[email protected]> + * init/job_class.[ch]: instead of assuming a fixed value (0) as the + default nice value for job processes, use whatever the nice value + of the current process is. This will be important later for user + sessions where an entire session may be started with a higher nice + value; and it fixes running the test suite as part of a nice'd + build. + * init/tests/test_job_class.c: update test suite to match. + 2012-12-11 James Hunt <[email protected]> * init/Makefile.am: Add explicit -lrt for tests (LP: #1088863) === modified file 'configure.ac' --- configure.ac 2012-12-07 20:33:03 +0000 +++ configure.ac 2012-12-07 21:39:17 +0000 @@ -53,7 +53,7 @@ AM_CONDITIONAL([HAVE_UDEV], [test "$have_udev" = yes]) # Checks for header files. -AC_CHECK_HEADERS([valgrind/valgrind.h]) +AC_CHECK_HEADERS([valgrind/valgrind.h, sys/prctl.h]) # Checks for typedefs, structures, and compiler characteristics. AC_PROG_CC_C99 === modified file 'dbus/com.ubuntu.Upstart.xml' --- dbus/com.ubuntu.Upstart.xml 2012-12-11 16:45:55 +0000 +++ dbus/com.ubuntu.Upstart.xml 2012-12-19 09:08:57 +0000 @@ -60,6 +60,15 @@ <arg name="job" type="o" /> </signal> + <!-- Signal for events being emitted --> + <signal name="EventEmitted"> + <arg name="name" type="s" /> + <arg name="env" type="as" /> + </signal> + + <!-- Signal emitted after upstart restarted and reconnected to DBUS --> + <signal name="Restarted" /> + <!-- Event emission --> <method name="EmitEvent"> <annotation name="com.netsplit.Nih.Method.Async" value="true" /> === modified file 'init/Makefile.am' --- init/Makefile.am 2012-12-11 13:59:01 +0000 +++ init/Makefile.am 2012-12-14 15:54:35 +0000 @@ -56,6 +56,7 @@ parse_conf.c parse_conf.h \ conf.c conf.h \ control.c control.h \ + xdg.c xdg.h \ errors.h nodist_init_SOURCES = \ $(com_ubuntu_Upstart_OUTPUTS) \ @@ -134,7 +135,6 @@ $(TEST_DATA_DIR)/upstart-1.6.json EXTRA_DIST = init.supp $(TEST_DATA_FILES) - test_util_SOURCES = \ tests/test_util.c tests/test_util.h @@ -154,6 +154,7 @@ test_parse_job \ test_parse_conf \ test_conf \ + test_xdg \ test_control check_PROGRAMS = $(TESTS) @@ -338,6 +339,13 @@ $(JSON_LIBS) \ -lrt +test_xdg_SOURCES = tests/test_xdg.c +test_xdg_LDADD = \ + xdg.o \ + environ.o \ + $(NIH_LIBS) \ + -lrt + test_control_SOURCES = tests/test_control.c test_control_LDADD = \ system.o environ.o process.o \ === modified file 'init/control.c' --- init/control.c 2012-12-11 16:45:55 +0000 +++ init/control.c 2012-12-19 09:08:57 +0000 @@ -55,6 +55,7 @@ #include "control.h" #include "errors.h" #include "state.h" +#include "event.h" #include "com.ubuntu.Upstart.h" @@ -1069,6 +1070,45 @@ } /** + * control_notify_event_emitted + * + * Re-emits an event over DBUS using the EventEmitted signal + **/ +void +control_notify_event_emitted (Event *event) +{ + nih_assert (event != NULL); + + control_init (); + + NIH_LIST_FOREACH (control_conns, iter) { + NihListEntry *entry = (NihListEntry *)iter; + DBusConnection *conn = (DBusConnection *)entry->data; + + NIH_ZERO (control_emit_event_emitted (conn, DBUS_PATH_UPSTART, + event->name, event->env)); + } +} + +/** + * control_notify_restarted + * + * DBUS signal sent when upstart has re-executed itself. + **/ +void +control_notify_restarted (void) +{ + control_init (); + + NIH_LIST_FOREACH (control_conns, iter) { + NihListEntry *entry = (NihListEntry *)iter; + DBusConnection *conn = (DBusConnection *)entry->data; + + NIH_ZERO (control_emit_restarted (conn, DBUS_PATH_UPSTART)); + } +} + +/** * control_set_env: * * @data: not used, === modified file 'init/control.h' --- init/control.h 2012-12-07 18:26:43 +0000 +++ init/control.h 2012-12-14 23:43:15 +0000 @@ -30,6 +30,8 @@ #include <json.h> +#include "event.h" + /** * USE_SESSION_BUS_ENV: * @@ -113,6 +115,9 @@ int control_restart (void *data, NihDBusMessage *message) __attribute__ ((warn_unused_result)); +void control_notify_event_emitted (Event *event); +void control_notify_restarted (void); + NIH_END_EXTERN #endif /* INIT_CONTROL_H */ === modified file 'init/event.c' --- init/event.c 2012-10-10 13:24:51 +0000 +++ init/event.c 2012-12-14 23:47:51 +0000 @@ -42,6 +42,7 @@ #include "event.h" #include "job.h" #include "blocked.h" +#include "control.h" #include "errors.h" #include "com.ubuntu.Upstart.h" @@ -507,6 +508,8 @@ } } + control_notify_event_emitted (event); + nih_free (event); } === modified file 'init/job_class.c' --- init/job_class.c 2012-12-11 16:45:55 +0000 +++ init/job_class.c 2012-12-19 09:08:57 +0000 @@ -27,6 +27,8 @@ #include <errno.h> #include <string.h> #include <signal.h> +#include <sys/time.h> +#include <sys/resource.h> #include <nih/macros.h> #include <nih/alloc.h> @@ -243,7 +245,7 @@ class->console = default_console >= 0 ? default_console : CONSOLE_LOG; class->umask = JOB_DEFAULT_UMASK; - class->nice = JOB_DEFAULT_NICE; + class->nice = JOB_NICE_INVALID; class->oom_score_adj = JOB_DEFAULT_OOM_SCORE_ADJ; for (i = 0; i < RLIMIT_NLIMITS; i++) === modified file 'init/job_class.h' --- init/job_class.h 2012-12-11 16:45:55 +0000 +++ init/job_class.h 2012-12-19 09:08:57 +0000 @@ -102,11 +102,11 @@ #define JOB_DEFAULT_UMASK 022 /** - * JOB_DEFAULT_NICE: + * JOB_NICE_INVALID: * - * The default nice level for processes. + * The nice level for processes when no nice level is set. **/ -#define JOB_DEFAULT_NICE 0 +#define JOB_NICE_INVALID -21 /** * JOB_DEFAULT_OOM_SCORE_ADJ: === modified file 'init/job_process.c' --- init/job_process.c 2012-12-03 20:27:09 +0000 +++ init/job_process.c 2012-12-18 09:02:24 +0000 @@ -239,6 +239,12 @@ NIH_MUST (nih_str_array_addp (&argv, NULL, &argc, cmd)); } + + /* At the end, always set proc->script to TRUE, even if the user didn't + * explicitly set it (when using shell variables). That way tests + * can reliably check for shell-specific behaviour. + */ + proc->script = TRUE; } else { /* Split the command on whitespace to produce a list of * arguments that we can exec directly. @@ -780,7 +786,8 @@ /* Adjust the process priority ("nice level"). */ - if (setpriority (PRIO_PROCESS, 0, class->nice) < 0) { + if (class->nice != JOB_NICE_INVALID && + setpriority (PRIO_PROCESS, 0, class->nice) < 0) { nih_error_raise_system (); job_process_error_abort (fds[1], JOB_PROCESS_ERROR_PRIORITY, 0); === modified file 'init/log.c' --- init/log.c 2012-11-23 11:36:47 +0000 +++ init/log.c 2012-12-07 21:38:17 +0000 @@ -31,7 +31,6 @@ #include "session.h" #include "conf.h" #include "paths.h" -#include <sys/prctl.h> static int log_file_open (Log *log); static int log_file_write (Log *log, const char *buf, size_t len); === modified file 'init/main.c' --- init/main.c 2012-12-11 16:45:55 +0000 +++ init/main.c 2012-12-19 09:08:57 +0000 @@ -30,6 +30,13 @@ #include <sys/resource.h> #include <sys/mount.h> +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#ifndef PR_SET_CHILD_SUBREAPER +#define PR_SET_CHILD_SUBREAPER 35 +#endif +#endif + #include <errno.h> #include <stdio.h> #include <limits.h> @@ -61,6 +68,7 @@ #include "conf.h" #include "control.h" #include "state.h" +#include "xdg.h" /* Prototypes for static functions */ @@ -79,6 +87,7 @@ static void handle_confdir (void); static void handle_logdir (void); +static void handle_usermode (void); static int console_type_setter (NihOption *option, const char *arg); @@ -112,6 +121,13 @@ **/ static int disable_startup_event = FALSE; +/** + * user_mode: + * + * If TRUE, upstart runs in user session mode. + **/ +static int user_mode = FALSE; + extern int disable_sessions; extern int disable_job_logging; extern int use_session_bus; @@ -157,6 +173,9 @@ { 0, "startup-event", N_("specify an alternative initial event (for testing)"), NULL, "NAME", &initial_event, NULL }, + { 0, "user", N_("start in user mode (as used for user sessions)"), + NULL, NULL, &user_mode, NULL }, + /* Ignore invalid options */ { '-', "--", NULL, NULL, NULL, NULL, NULL }, @@ -169,6 +188,7 @@ char *argv[]) { char **args = NULL; + char **dirs = NULL; int ret; args_copy = NIH_MUST (nih_str_array_copy (NULL, NULL, argv)); @@ -187,6 +207,7 @@ handle_confdir (); handle_logdir (); + handle_usermode (); if (disable_job_logging) nih_debug ("Job logging disabled"); @@ -512,8 +533,18 @@ } /* Read configuration */ - NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE)); - NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR)); + if (! user_mode) + NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE)); + + if (conf_dir) + NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR)); + + if (user_mode) { + dirs = NIH_MUST (get_user_upstart_dirs ()); + for (char **d = dirs; d && *d; d++) + NIH_MUST (conf_source_new (NULL, *d, CONF_JOB_DIR)); + nih_free (dirs); + } conf_reload (); @@ -598,12 +629,29 @@ * disabled by the term_handler */ sigemptyset (&mask); sigprocmask (SIG_SETMASK, &mask, NULL); + + /* Emit the Restarted signal so that any listing Instance Init + * knows that it needs to restart too. + */ + control_notify_restarted(); } if (disable_sessions) nih_debug ("Sessions disabled"); job_class_environment_init (); + /* Set us as the child subreaper. + * This ensures that even when init doesn't run as PID 1, it'll always be + * the ultimate parent of everything it spawns. */ + +#ifdef HAVE_SYS_PRCTL_H + if (getpid () > 1 && prctl (PR_SET_CHILD_SUBREAPER, 1) < 0) { + nih_warn ("%s: %s", _("Unable to register as subreaper"), + strerror (errno)); + + NIH_MUST (event_new (NULL, "child-subreaper-failed", NULL)); + } +#endif /* Run through the loop at least once to deal with signals that were * delivered to the previous process while the mask was set or to @@ -880,6 +928,9 @@ if (conf_dir) goto out; + if (user_mode) + return; + conf_dir = CONFDIR; dir = getenv (CONFDIR_ENV); @@ -920,6 +971,18 @@ log_dir); } +/** + * handle_usermode: + * + * Setup user session mode. + **/ +static void +handle_usermode (void) +{ + if (user_mode) + use_session_bus = TRUE; +} + /** * NihOption setter function to handle selection of default console * type. === modified file 'init/man/init.5' --- init/man/init.5 2012-11-19 09:48:17 +0000 +++ init/man/init.5 2012-12-18 17:40:49 +0000 @@ -1,4 +1,4 @@ -.TH init 5 2011-05-12 "Upstart" +.TH init 5 2012-12-18 "Upstart" .\" .SH NAME init \- Upstart init daemon job configuration @@ -12,6 +12,10 @@ .B $HOME/.init/ Default location of user job configuration files. .\" +.TP +.B $XDG_CONFIG_HOME/upstart/, $XDG_CONFIG_DIRS/upstart/ +Default locations of user session job configuration files. +.\" .SH DESCRIPTION On startup, the Upstart .BR init (8) @@ -25,6 +29,11 @@ .B User Jobs for further details. +If Upstart was invoked as a user process with \-\-user option, it will +run in User Session mode. See +.B User Session Mode +for further details. + To be considered by Upstart, files in this directory must have a recognized suffix and may also be present in sub\-directories. There are two recognized suffixes: @@ -115,7 +124,47 @@ .B Process environment below). -Note too that User Jobs can be created within a chroot environment. +Note that User Jobs +.B cannot +be used within a chroot environment. + +.\" +.SS User Session Mode + +Upstart can manage complete User Sessions. In this mode it runs with a +process id greater than 1 and will read job configuration files from the +following list of directories in the order shown: + +.IP \(bu 4 +.I $XDG_CONFIG_HOME/upstart/ +.IP \(bu 4 +.I $HOME/.init/ +.IP \(bu 4 +.I $XDG_CONFIG_DIRS/upstart/ +.IP \(bu 4 +.I /usr/share/upstart/sessions/ +.P + +Note that the first directory to contain a job is considered the owner +of that job name: any subsequently searched directory that contains a +job of the same name will be ignored. The same applies for override +files: only the first override file found in the search order will be +applied. Note that an override file does not have to be in the same +directory as the job it overrides, but if not in the directory of the +job it overrides, it must be in an +.I earlier +directory to that which contains the job. + +Jobs in these locations are expected to launch the user's session. +Upstart will try to parent all spawned process with the aid of +.BR prctl (2) "" . +If successful this will ensure that even double-forking daemons will be +reparented to the User Session process, and not to the +.BR init (8) +daemon running with process id 1. + +When running in User Session mode, Upstart will kill all job processes +on session logout or shutdown. .\" .SS Configuration File Format @@ -1000,5 +1049,6 @@ .SH SEE ALSO .BR init (8) .BR initctl (8) +.BR prctl (2) +.BR pty (7) .BR sh (1) -.BR pty (7) === modified file 'init/man/init.8' --- init/man/init.8 2011-12-09 14:07:11 +0000 +++ init/man/init.8 2012-12-18 17:40:49 +0000 @@ -1,4 +1,4 @@ -.TH init 8 2011-04-06 "Upstart" +.TH init 8 2012-12-18 "Upstart" .\" .SH NAME init \- Upstart process management daemon @@ -113,24 +113,66 @@ .BR startup (7) . .\" .TP -.B \-\-verbose +.B \-\-user +Starts in user mode, as used for user sessions. Upstart will be run as +an unprivileged user, reading configuration files from configuration +locations as per roughly XDG Base Directory Specification. See +.BR init (5) +for further details. +.\" +.TP +.B \-q, \-\-quiet +Reduces output messages to errors only. +.\" +.TP +.B \-v, \-\-verbose Outputs verbose messages about job state changes and event emissions to the system console or log, useful for debugging boot. .\" +.TP +.B \-\-version +Outputs version information and exits. +.\" .SH NOTES .B init is not normally executed by a user process, and expects to have a process id of 1. If this is not the case, it will actually execute .BR telinit (8) -and pass all arguments to that. See that manual page for further details. +and pass all arguments to that. See that manual page for further +details. However, if the +.B \-\-user +option is specified, it will read alternative configuration files and +manage the individual user session in a similar fashion. +.\" +.SH ENVIRONMENT VARIABLES + +When run as a user process, the following variables may be used to find +job configuration files: + +.IP \(bu 4 +.I $XDG_CONFIG_HOME +.IP \(bu 4 +.I $XDG_CONFIG_DIRS +.P + +See +.B User Session Mode +in +.BR init (5) +for further details. + .\" .SH FILES .\" .I /etc/init.conf -.I /etc/init/*.conf +.I /etc/init/ .I $HOME/.init/ + +.I $XDG_CONFIG_DIRS/upstart/ + +.I $XDG_CONFIG_HOME/upstart/ .\" .SH AUTHOR Written by Scott James Remnant @@ -141,7 +183,7 @@ .RB < https://launchpad.net/upstart/+bugs > .\" .SH COPYRIGHT -Copyright \(co 2009\-2011 Canonical Ltd. +Copyright \(co 2009\-2012 Canonical Ltd. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. === modified file 'init/paths.h' --- init/paths.h 2012-09-10 07:50:32 +0000 +++ init/paths.h 2012-12-17 15:46:35 +0000 @@ -96,6 +96,24 @@ #define CONFDIR_ENV "UPSTART_CONFDIR" #endif +/** + * INIT_XDG_SUBDIR: + * + * This is the name of the sub folder we will use when constructing + * config source dirs with XDG compliant folders. + **/ +#ifndef INIT_XDG_SUBDIR +#define INIT_XDG_SUBDIR "upstart" +#endif + +/** + * SYSTEM_USERCONFDIR: + * + * This is the path to system-wide user session jobs. + **/ +#ifndef SYSTEM_USERCONFDIR +#define SYSTEM_USERCONFDIR "/usr/share/upstart/sessions" +#endif /** * SHELL: === modified file 'init/tests/test_event.c' --- init/tests/test_event.c 2011-12-09 14:07:11 +0000 +++ init/tests/test_event.c 2012-11-27 23:49:12 +0000 @@ -20,6 +20,7 @@ */ #include <nih/test.h> +#include <nih-dbus/test_dbus.h> #include <sys/types.h> #include <sys/wait.h> @@ -32,6 +33,8 @@ #include <nih/main.h> #include <nih/error.h> +#include "dbus/upstart.h" + #include "control.h" #include "job.h" #include "event.h" @@ -135,11 +138,39 @@ test_poll (void) { Event *event = NULL; + pid_t dbus_pid; + DBusError dbus_error; + DBusConnection *conn, *client_conn; + DBusMessage *message; + NihListEntry *entry; TEST_FUNCTION ("event_poll"); + nih_error_init (); + nih_timer_init (); + nih_main_loop_init (); + control_init (); job_class_init (); + + /* Check that when a D-Bus connection is open, the new instance + * is registered on that connection as an object and the InstanceAdded + * signal is emitted. + */ + TEST_FEATURE ("with D-Bus connection"); + dbus_error_init (&dbus_error); + + TEST_DBUS (dbus_pid); + TEST_DBUS_OPEN (conn); + TEST_DBUS_OPEN (client_conn); + + dbus_bus_add_match (client_conn, "type='signal'", &dbus_error); + assert (! dbus_error_is_set (&dbus_error)); + control_init (); + entry = nih_list_entry_new (NULL); + entry->data = conn; + nih_list_add (control_conns, &entry->entry); + /* Check that a pending event which does not get blocked goes * straight though and gets freed. @@ -154,6 +185,12 @@ event_poll (); + TEST_DBUS_MESSAGE (client_conn, message); + TEST_TRUE (dbus_message_is_signal (message, DBUS_INTERFACE_UPSTART, + "EventEmitted")); + + dbus_message_unref (message); + TEST_FREE (event); } @@ -213,6 +250,14 @@ TEST_FREE (event); } + + nih_free (entry); + + TEST_DBUS_CLOSE (conn); + TEST_DBUS_CLOSE (client_conn); + TEST_DBUS_END (dbus_pid); + + dbus_shutdown (); } === modified file 'init/tests/test_job_class.c' --- init/tests/test_job_class.c 2011-12-09 15:27:57 +0000 +++ init/tests/test_job_class.c 2012-12-14 20:48:51 +0000 @@ -27,6 +27,8 @@ #include <sys/wait.h> #include <sys/ptrace.h> #include <sys/select.h> +#include <sys/time.h> +#include <sys/resource.h> #include <time.h> #include <stdio.h> @@ -131,7 +133,7 @@ TEST_EQ (class->console, CONSOLE_LOG); TEST_EQ (class->umask, 022); - TEST_EQ (class->nice, 0); + TEST_EQ (class->nice, JOB_NICE_INVALID); TEST_EQ (class->oom_score_adj, 0); for (i = 0; i < RLIMIT_NLIMITS; i++) === modified file 'init/tests/test_job_process.c' --- init/tests/test_job_process.c 2012-12-07 21:52:38 +0000 +++ init/tests/test_job_process.c 2012-12-18 11:02:00 +0000 @@ -680,6 +680,8 @@ output = fopen (filename, "r"); TEST_FILE_EQ (output, "BAR=BAZ\n"); TEST_FILE_EQ (output, "FOO=BAR\n"); + if (job->class->process[PROCESS_MAIN]->script) + TEST_FILE_EQ (output, "PWD=/\n"); TEST_FILE_EQ (output, "UPSTART_INSTANCE=\n"); TEST_FILE_EQ (output, "UPSTART_JOB=test\n"); TEST_FILE_EQ (output, "UPSTART_NO_SESSIONS=1\n"); @@ -734,6 +736,8 @@ output = fopen (filename, "r"); TEST_FILE_EQ (output, "BAR=BAZ\n"); TEST_FILE_EQ (output, "FOO=BAR\n"); + if (job->class->process[PROCESS_MAIN]->script) + TEST_FILE_EQ (output, "PWD=/\n"); TEST_FILE_EQ (output, "UPSTART_INSTANCE=foo\n"); TEST_FILE_EQ (output, "UPSTART_JOB=test\n"); TEST_FILE_EQ (output, "UPSTART_NO_SESSIONS=1\n"); @@ -790,6 +794,8 @@ TEST_FILE_EQ (output, "BAR=BAZ\n"); TEST_FILE_EQ (output, "CRACKLE=FIZZ\n"); TEST_FILE_EQ (output, "FOO=SMACK\n"); + if (job->class->process[PROCESS_PRE_STOP]->script) + TEST_FILE_EQ (output, "PWD=/\n"); TEST_FILE_EQ (output, "UPSTART_INSTANCE=\n"); TEST_FILE_EQ (output, "UPSTART_JOB=test\n"); TEST_FILE_EQ (output, "UPSTART_NO_SESSIONS=1\n"); @@ -846,6 +852,8 @@ TEST_FILE_EQ (output, "BAR=BAZ\n"); TEST_FILE_EQ (output, "CRACKLE=FIZZ\n"); TEST_FILE_EQ (output, "FOO=SMACK\n"); + if (job->class->process[PROCESS_POST_STOP]->script) + TEST_FILE_EQ (output, "PWD=/\n"); TEST_FILE_EQ (output, "UPSTART_INSTANCE=\n"); TEST_FILE_EQ (output, "UPSTART_JOB=test\n"); TEST_FILE_EQ (output, "UPSTART_NO_SESSIONS=1\n"); @@ -9190,6 +9198,8 @@ io_error_handler, NULL); TEST_NE_P (io, NULL); + ret = nih_main_loop (); + /* wait for child to finish */ TEST_EQ (waitpid (pid, &status, 0), pid); @@ -9199,7 +9209,6 @@ WIFSTOPPED (status) ? WSTOPSIG (status) : EXIT_FAILURE; - ret = nih_main_loop (); exit (exit_status ? exit_status : ret); } === modified file 'init/tests/test_log.c' --- init/tests/test_log.c 2012-09-20 08:12:05 +0000 +++ init/tests/test_log.c 2012-12-07 21:38:17 +0000 @@ -26,7 +26,6 @@ #include <libgen.h> #include <sys/types.h> #include <sys/ioctl.h> -#include <sys/prctl.h> #include <nih/test.h> #include <nih/timer.h> #include <nih/child.h> === modified file 'init/tests/test_state.c' --- init/tests/test_state.c 2012-12-04 16:09:18 +0000 +++ init/tests/test_state.c 2012-12-07 21:38:17 +0000 @@ -28,7 +28,6 @@ #include <libgen.h> #include <sys/types.h> #include <sys/ioctl.h> -#include <sys/prctl.h> #include <nih/test.h> #include <nih/timer.h> #include <nih/child.h> === added file 'init/tests/test_xdg.c' --- init/tests/test_xdg.c 1970-01-01 00:00:00 +0000 +++ init/tests/test_xdg.c 2012-12-18 14:16:10 +0000 @@ -0,0 +1,306 @@ +/* upstart + * + * test_xdg.c - test suite for init/xdg.c + * + * Copyright © 2012 Canonical Ltd. + * Author: Dmitrijs Ledkovs <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <nih/string.h> +#include <nih/test.h> + +#include <stdlib.h> +#include <limits.h> + +#include "xdg.h" + +void +test_get_home_subdir (void) +{ + char dirname[PATH_MAX]; + char *dir; + char * expected=NULL; + + TEST_FUNCTION ("get_home_subdir"); + + TEST_FEATURE ("with HOME not set"); + TEST_EQ (unsetenv ("HOME"), 0); + + TEST_ALLOC_FAIL { + dir = get_home_subdir ("test"); + TEST_EQ_P (dir, NULL); + } + + TEST_FEATURE ("with HOME set"); + TEST_FILENAME (dirname); + TEST_EQ (setenv ("HOME", dirname, 1), 0); + + TEST_ALLOC_FAIL { + TEST_ALLOC_SAFE { + dir = NULL; + expected = NIH_MUST (nih_sprintf (NULL, "%s/test", dirname)); + } + + dir = get_home_subdir ("test"); + + if (test_alloc_failed) { + TEST_EQ_P (dir, NULL); + } else { + TEST_EQ_STR (dir, expected); + nih_free (dir); + } + + if (expected) + nih_free (expected); + } +} + +void +test_get_config_home (void) +{ + char dirname[PATH_MAX]; + char * outname; + char * expected; + + TEST_FUNCTION ("xdg_get_config_home"); + + TEST_FEATURE ("with HOME set and without environment override"); + TEST_FILENAME (dirname); + TEST_EQ (setenv ("HOME", dirname, 1), 0); + TEST_EQ (unsetenv ("XDG_CONFIG_HOME"), 0); + TEST_ALLOC_FAIL { + TEST_ALLOC_SAFE { + expected = NIH_MUST (nih_sprintf (NULL, "%s/.config", dirname)); + } + + outname = NULL; + outname = xdg_get_config_home (); + + if (! test_alloc_failed) { + TEST_EQ_STR (outname, expected); + } else { + TEST_EQ_P (outname, NULL); + } + + if (outname) + nih_free (outname); + + nih_free(expected); + } + + TEST_FEATURE ("with HOME set and with empty environment override"); + TEST_EQ (setenv ("XDG_CONFIG_HOME", "", 1), 0); + + + TEST_ALLOC_FAIL { + TEST_ALLOC_SAFE { + expected = NIH_MUST (nih_sprintf (NULL, "%s/.config", dirname)); + } + outname = NULL; + outname = xdg_get_config_home(); + + if (test_alloc_failed) { + TEST_EQ_P (outname, NULL); + } else { + TEST_EQ_STR (outname, expected); + } + if (outname) + nih_free (outname); + nih_free(expected); + } + + TEST_FEATURE ("with HOME set and with environment override"); + expected = NIH_MUST (nih_strdup (NULL, "/home/me/.config-test")); + TEST_EQ (setenv ("XDG_CONFIG_HOME", expected, 1), 0); + + TEST_ALLOC_FAIL { + outname = NULL; + outname = xdg_get_config_home(); + + if (test_alloc_failed) { + TEST_EQ_P (outname, NULL); + } else { + TEST_EQ_STR (outname, expected); + } + if (outname) + nih_free (outname); + } + + TEST_FEATURE ("without HOME set and with environment override"); + TEST_EQ (unsetenv ("HOME"), 0); + + TEST_ALLOC_FAIL { + outname = NULL; + outname = xdg_get_config_home(); + + if (test_alloc_failed) { + TEST_EQ_P (outname, NULL); + } else { + TEST_EQ_STR (outname, expected); + } + if (outname) + nih_free (outname); + } + nih_free(expected); + + TEST_FEATURE ("without HOME set and with empty environment override"); + TEST_EQ (setenv ("XDG_CONFIG_HOME", "", 1), 0); + + TEST_ALLOC_FAIL { + outname = NULL; + outname = xdg_get_config_home(); + TEST_EQ_P (outname, NULL); + } + + TEST_FEATURE ("without HOME set and without environment override"); + TEST_EQ (unsetenv ("XDG_CONFIG_HOME"), 0); + TEST_ALLOC_FAIL { + outname = NULL; + outname = xdg_get_config_home(); + TEST_EQ_P (outname, NULL); + } +} + +void +test_get_config_dirs (void) +{ + char **dirs = NULL; + + TEST_FUNCTION ("xdg_get_config_dirs"); + TEST_FEATURE ("without environment override set"); + TEST_EQ (unsetenv ("XDG_CONFIG_DIRS"), 0); + + TEST_ALLOC_FAIL { + dirs = NULL; + dirs = xdg_get_config_dirs(); + + if (test_alloc_failed) { + TEST_EQ_P (dirs, NULL); + } else { + TEST_EQ_STR (dirs[0], "/etc/xdg"); + TEST_EQ (dirs[1], NULL); + nih_free (dirs); + } + } + + TEST_FEATURE ("with empty environment override"); + TEST_EQ (setenv ("XDG_CONFIG_DIRS", "", 1), 0); + TEST_ALLOC_FAIL { + dirs = NULL; + dirs = xdg_get_config_dirs(); + + if (test_alloc_failed) { + TEST_EQ_P (dirs, NULL); + } else { + TEST_EQ_STR (dirs[0], "/etc/xdg"); + TEST_EQ (dirs[1], NULL); + nih_free (dirs); + } + } + + TEST_FEATURE ("with environment override set to single path"); + TEST_EQ (setenv ("XDG_CONFIG_DIRS", "/etc/xdg/xdg-test", 1), 0); + TEST_ALLOC_FAIL { + dirs = NULL; + dirs = xdg_get_config_dirs(); + + if (test_alloc_failed) { + TEST_EQ_P (dirs, NULL); + } else { + TEST_EQ_STR (dirs[0], "/etc/xdg/xdg-test"); + TEST_EQ (dirs[1], NULL); + nih_free (dirs); + } + } + + TEST_FEATURE ("with environment override set to multiple paths"); + TEST_FEATURE ("with environment override set to single path"); + TEST_EQ (setenv ("XDG_CONFIG_DIRS", "/etc/xdg/xdg-test:/etc/xdg/xdg-other", 1), 0); + TEST_ALLOC_FAIL { + dirs = NULL; + dirs = xdg_get_config_dirs(); + + if (test_alloc_failed) { + TEST_EQ_P (dirs, NULL); + } else { + TEST_EQ_STR (dirs[0], "/etc/xdg/xdg-test"); + TEST_EQ_STR (dirs[1], "/etc/xdg/xdg-other"); + TEST_EQ (dirs[2], NULL); + nih_free (dirs); + } + } +} + +void +test_get_user_upstart_dirs (void) +{ + char dirname[PATH_MAX]; + char ** dirs = NULL; + char * path = NULL; + char ** expected = NULL; + + /* Currently only one test for "typical" output. + * Not sure what else to test here. + */ + TEST_FUNCTION ("get_user_upstart_dirs"); + + TEST_FEATURE ("with HOME set"); + TEST_FILENAME (dirname); + TEST_EQ (setenv ("HOME", dirname, 1), 0); + TEST_EQ (unsetenv ("XDG_CONFIG_HOME"), 0); + TEST_EQ (unsetenv ("XDG_CONFIG_DIRS"), 0); + + TEST_ALLOC_FAIL { + TEST_ALLOC_SAFE { + dirs = NULL; + expected = nih_str_array_new (NULL); + path = NIH_MUST (nih_sprintf (NULL, "%s/.config/upstart", dirname)); + assert (nih_str_array_add (&expected, NULL, NULL, path)); + nih_free(path); + path = NIH_MUST (nih_sprintf (NULL, "%s/.init", dirname)); + assert (nih_str_array_add (&expected, NULL, NULL, path)); + nih_free(path); + } + + dirs = NULL; + dirs = get_user_upstart_dirs (); + + if (test_alloc_failed) { + TEST_EQ_P (dirs, NULL); + } else { + TEST_EQ_STR (dirs[0], expected[0]); + TEST_EQ_STR (dirs[1], expected[1]); + TEST_EQ_STR (dirs[2], "/etc/xdg/upstart"); + TEST_EQ_STR (dirs[3], SYSTEM_USERCONFDIR); + TEST_EQ (dirs[4], NULL); + nih_free (dirs); + } + nih_free(expected); + } + +} + +int +main (int argc, + char *argv[]) +{ + test_get_home_subdir (); + test_get_config_home (); + test_get_config_dirs (); + test_get_user_upstart_dirs (); + + return 0; +} === added file 'init/xdg.c' --- init/xdg.c 1970-01-01 00:00:00 +0000 +++ init/xdg.c 2012-12-18 16:31:55 +0000 @@ -0,0 +1,187 @@ +/* upstart + * + * xdg.c - XDG compliant path constructor + * + * Copyright © 2012 Canonical Ltd. + * Author: Dmitrijs Ledkovs <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> + +#include <nih/alloc.h> +#include <nih/logging.h> +#include <nih/string.h> + +#include "paths.h" +#include "xdg.h" + +/** + * get_home_subdir: + * + * Construct path to directory in user's HOME dir. + * + * Returns: newly-allocated path, or NULL on error. + */ + +char * +get_home_subdir (const char * suffix) +{ + char *dir; + nih_assert (suffix && suffix[0]); + + dir = getenv ("HOME"); + if (dir && dir[0]) { + dir = nih_sprintf (NULL, "%s/%s", dir, suffix); + return dir; + } + + return NULL; +} + +/** + * xdg_get_config_home: + * + * Determine an XDG compliant XDG_CONFIG_HOME + * + * Returns: newly-allocated path, or NULL on error. + **/ +char * +xdg_get_config_home (void) +{ + nih_local char **env = NULL; + char *dir; + + dir = getenv ("XDG_CONFIG_HOME"); + + if (dir && dir[0]) { + dir = nih_strdup (NULL, dir); + return dir; + } + + dir = get_home_subdir (".config"); + + return dir; +} + +/** + * xdg_get_config_dirs: + * + * Determine a list of XDG compliant XDG_CONFIG_DIRS + * + * Returns: newly-allocated array of paths, or NULL on error. + **/ +char ** +xdg_get_config_dirs (void) +{ + char *env_path; + char **dirs = NULL; + + env_path = getenv ("XDG_CONFIG_DIRS"); + if (! env_path || ! env_path[0]) + env_path = "/etc/xdg"; + + dirs = nih_str_split (NULL, env_path, ":", TRUE); + + return dirs; +} + +/** + * get_user_upstart_dirs: + * + * Construct an array of user session config source paths to config + * dirs for a particular user. This array is sorted in highest + * priority order and therefore can be iterated to add each of these + * directories as config source dirs, when e.g. upstart is running as + * user session init. + * + * Returns: newly-allocated array of paths, or NULL or error. + **/ +char ** +get_user_upstart_dirs (void) +{ + char *path = NULL; + char **dirs = NULL; + char **all_dirs = NULL; + + all_dirs = nih_str_array_new (NULL); + if (! all_dirs) + goto error; + + /* The current order is inline with Enhanced User Sessions Spec */ + + /* User's: ~/.config/upstart or XDG_CONFIG_HOME/upstart */ + path = xdg_get_config_home (); + if (! path) + goto error; + + if (path && path[0]) { + if (! nih_strcat_sprintf (&path, NULL, "/%s", INIT_XDG_SUBDIR)) + goto error; + if (! nih_str_array_add (&all_dirs, NULL, NULL, path)) + goto error; + nih_free (path); + path = NULL; + } + + /* Legacy User's: ~/.init */ + path = get_home_subdir (USERCONFDIR); + if (! path) + goto error; + + if (path && path[0]) { + if (! nih_str_array_add (&all_dirs, NULL, NULL, path)) + goto error; + nih_free (path); + path = NULL; + } + + /* Systems': XDG_CONFIG_DIRS/upstart */ + dirs = xdg_get_config_dirs (); + if (! dirs) + goto error; + + for (char **p = dirs; p && *p; p++) { + if (! nih_strcat_sprintf (p, NULL, "/%s", INIT_XDG_SUBDIR)) + goto error; + if (! nih_str_array_add (&all_dirs, NULL, NULL, *p)) + goto error; + } + nih_free (dirs); + dirs = NULL; + + /* System's read-only location */ + if (! nih_str_array_add (&all_dirs, NULL, NULL, SYSTEM_USERCONFDIR)) + goto error; + + return all_dirs; + +error: + if (path) + nih_free (path); + + if (dirs) + nih_free (dirs); + + if (all_dirs) + nih_free (all_dirs); + + return NULL; +} + === added file 'init/xdg.h' --- init/xdg.h 1970-01-01 00:00:00 +0000 +++ init/xdg.h 2012-12-18 13:58:23 +0000 @@ -0,0 +1,42 @@ +/* upstart + * + * Copyright © 2012 Canonical Ltd. + * Author: Dmitrijs Ledkovs <[email protected]>. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INIT_XDG_H +#define INIT_XDG_H + +#include "paths.h" +#include <nih/macros.h> + +NIH_BEGIN_EXTERN + +char * get_home_subdir (const char * suffix) + __attribute__ ((malloc, warn_unused_result)); + +char * xdg_get_config_home (void) + __attribute__ ((malloc, warn_unused_result)); + +char ** xdg_get_config_dirs (void) + __attribute__ ((malloc, warn_unused_result)); + +char ** get_user_upstart_dirs (void) + __attribute__ ((malloc, warn_unused_result)); + +NIH_END_EXTERN + +#endif /* INIT_XDG_H */ === modified file 'util/Makefile.am' --- util/Makefile.am 2012-12-08 00:00:28 +0000 +++ util/Makefile.am 2012-12-12 14:13:08 +0000 @@ -188,7 +188,7 @@ test_sysv \ test_telinit -EXTRA_DIST = tests/test_user_sessions.sh +EXTRA_DIST += tests/test_user_sessions.sh check_PROGRAMS = $(TESTS)
-- upstart-devel mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/upstart-devel
