diff --git a/dead.package b/dead.package deleted file mode 100644 index 7ceac32..0000000 --- a/dead.package +++ /dev/null @@ -1 +0,0 @@ -This package was retired on 2012-08-06 due to no active maintainers for the package. diff --git a/fix-selinux-on-run.conf b/fix-selinux-on-run.conf new file mode 100644 index 0000000..a09734b --- /dev/null +++ b/fix-selinux-on-run.conf @@ -0,0 +1,8 @@ +# fix selinux context on /run created and mounted by dracut +# original context is tmpfs_t while we need var_run_t and others + +start on starting rcS + +task + +exec /sbin/restorecon -R /run diff --git a/init-system-dbus.conf b/init-system-dbus.conf new file mode 100644 index 0000000..0fc6a8a --- /dev/null +++ b/init-system-dbus.conf @@ -0,0 +1,6 @@ +# init-system-dbus - tell init process to connect to system dbus server + +start on started messagebus + +task +exec /bin/kill -USR1 1 diff --git a/init.conf b/init.conf new file mode 100644 index 0000000..e69de29 diff --git a/sources b/sources new file mode 100644 index 0000000..0f4c518 --- /dev/null +++ b/sources @@ -0,0 +1,2 @@ +c1b1bab5c1c1ed2595081d8178542b37 upstart-1.2.tar.gz +dce904a3284fb76fa136c618b9e8a80c initjobs-20110502.tar.gz diff --git a/tmpfiles.conf b/tmpfiles.conf new file mode 100644 index 0000000..5b26039 --- /dev/null +++ b/tmpfiles.conf @@ -0,0 +1,46 @@ +# tmpfiles - create missing directories in /var/{lock,run} according to +# /etc/tmpfiles.d/ + +start on stopping rcS + +task + +script + [ -d /etc/tmpfiles.d ] || exit 0 + + SELINUX_RESTORE= + + # Check SELinux status + SELINUX_STATE= + if [ -e "/selinux/enforce" ] && [ "$(cat /proc/self/attr/current)" != "kernel" ]; then + if [ -r "/selinux/enforce" ] ; then + SELINUX_STATE=$(cat "/selinux/enforce") + else + # assume enforcing if you can't read it + SELINUX_STATE=1 + fi + fi + + for i in /etc/tmpfiles.d/*; do + while read type path mode uid gid age; do + # only directories + [ -n "$type" ] || continue + [ $type = "d" ] || [ $type = "D" ] || continue + # only /run /var/run and /var/lock + [ $path != ${path#/run/} ] || + [ $path != ${path#/var/run/} ] || + [ $path != ${path#/var/lock/} ] || + continue + if [ ! -d $path ]; then + mkdir -p $path || continue + SELINUX_RESTORE=1 + chmod $mode $path + chown $uid:$gid $path + fi + done < $i; + done + + if [ -n "$SELINUX_STATE" ] && [ -n "$SELINUX_RESTORE" ]; then + /sbin/restorecon -R /run /var/run /var/lock 2> /dev/null + fi +end script diff --git a/upstart-audit-events.patch b/upstart-audit-events.patch new file mode 100644 index 0000000..f0a2892 --- /dev/null +++ b/upstart-audit-events.patch @@ -0,0 +1,271 @@ +diff --git a/aclocal.m4 b/aclocal.m4 +index 3d3673e..6e09291 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -1066,6 +1066,7 @@ m4_include([m4/lcmessage.m4]) + m4_include([m4/lib-ld.m4]) + m4_include([m4/lib-link.m4]) + m4_include([m4/lib-prefix.m4]) ++m4_include([m4/libaudit.m4]) + m4_include([m4/libnih.m4]) + m4_include([m4/libtool.m4]) + m4_include([m4/lock.m4]) +diff --git a/config.h.in b/config.h.in +index 8a7d63f..8d000aa 100644 +--- a/config.h.in ++++ b/config.h.in +@@ -118,6 +118,9 @@ + /* Define if your file defines LC_MESSAGES. */ + #undef HAVE_LC_MESSAGES + ++/* linux audit support */ ++#undef HAVE_LIBAUDIT ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LIMITS_H + +diff --git a/configure.ac b/configure.ac +index 49544e3..1513500 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -40,6 +40,7 @@ AM_PROG_CC_C_O + NIH_C_THREAD + + # Checks for library functions. ++LINUX_AUDIT + + # Other checks + AC_MSG_CHECKING([whether to include sbindir in PATH]) +diff --git a/m4/libaudit.m4 b/m4/libaudit.m4 +new file mode 100644 +index 0000000..6865fb8 +--- /dev/null ++++ b/m4/libaudit.m4 +@@ -0,0 +1,25 @@ ++# libaudit.m4 - Checks for the Linux Audit System support ++# Copyright (c) 2009 Steve Grubb sgrubb@redhat.com ++# ++AC_DEFUN([LINUX_AUDIT], ++[ ++ AC_ARG_WITH(libaudit, ++ [ --with-libaudit=[auto/yes/no] Add Linux audit support [default=auto]],, ++ with_libaudit=auto) ++ ++ # Check for Linux auditing API ++ # ++ # libaudit detection ++ ++ if test x$with_libaudit = xno ; then ++ have_libaudit=no; ++ else ++ # See if we have audit daemon library ++ AC_CHECK_LIB(audit, audit_log_user_message, ++ AUDIT_LDADD=-laudit,) ++ fi ++ AC_SUBST(AUDIT_LDADD) ++ if test x$AUDIT_LDADD != x ; then ++ AC_DEFINE(HAVE_LIBAUDIT,1,[linux audit support]) ++ fi ++]) +diff --git a/util/Makefile.am b/util/Makefile.am +index 0b49c10..9a772d5 100644 +--- a/util/Makefile.am ++++ b/util/Makefile.am +@@ -45,14 +45,17 @@ reboot_SOURCES = \ + utmp.c utmp.h + reboot_LDADD = \ + $(LTLIBINTL) \ +- $(NIH_LIBS) ++ $(NIH_LIBS) \ ++ $(AUDIT_LDADD) ++ + + runlevel_SOURCES = \ + runlevel.c \ + utmp.c utmp.h + runlevel_LDADD = \ + $(LTLIBINTL) \ +- $(NIH_LIBS) ++ $(NIH_LIBS) \ ++ $(AUDIT_LDADD) + + shutdown_SOURCES = \ + shutdown.c \ +@@ -64,7 +67,8 @@ shutdown_LDADD = \ + $(LTLIBINTL) \ + $(NIH_LIBS) \ + $(NIH_DBUS_LIBS) \ +- $(DBUS_LIBS) ++ $(DBUS_LIBS) \ ++ $(AUDIT_LDADD) + + telinit_SOURCES = \ + telinit.c \ +@@ -76,7 +80,8 @@ telinit_LDADD = \ + $(LTLIBINTL) \ + $(NIH_LIBS) \ + $(NIH_DBUS_LIBS) \ +- $(DBUS_LIBS) ++ $(DBUS_LIBS) \ ++ $(AUDIT_LDADD) + + + com_ubuntu_Upstart_OUTPUTS = \ +@@ -201,7 +206,8 @@ test_sysv_LDADD = \ + $(LTLIBINTL) \ + $(NIH_LIBS) \ + $(NIH_DBUS_LIBS) \ +- $(DBUS_LIBS) ++ $(DBUS_LIBS) \ ++ $(AUDIT_LDADD) + + test_telinit_SOURCES = tests/test_telinit.c telinit.c + test_telinit_CFLAGS = $(AM_CFLAGS) -DTEST +@@ -211,7 +217,8 @@ test_telinit_LDADD = \ + com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ + $(NIH_LIBS) \ + $(NIH_DBUS_LIBS) \ +- $(DBUS_LIBS) ++ $(DBUS_LIBS) \ ++ $(AUDIT_LDADD) + + + .PHONY: tests +diff --git a/util/reboot.c b/util/reboot.c +index 7a9c071..15028eb 100644 +--- a/util/reboot.c ++++ b/util/reboot.c +@@ -36,6 +36,10 @@ + #include + #include + ++#ifdef HAVE_LIBAUDIT ++#include ++#endif ++ + #include "utmp.h" + + +@@ -103,7 +107,6 @@ static int poweroff = FALSE; + **/ + static int exit_only = FALSE; + +- + /** + * options: + * +@@ -127,6 +130,7 @@ static NihOption options[] = { + NIH_OPTION_LAST + }; + ++static void send_audit_event(void); + + int + main (int argc, +@@ -227,6 +231,8 @@ main (int argc, + /* Re-enable Control-Alt-Delete in case it breaks */ + reboot (RB_ENABLE_CAD); + ++ send_audit_event (); ++ + /* Do the syscall */ + switch (mode) { + case REBOOT: +@@ -248,3 +254,21 @@ main (int argc, + + return 0; + } ++ ++/** ++ * send_audit_event ++ * ++ * Send system shutdown audit event ++ **/ ++static void ++send_audit_event (void) ++{ ++#ifdef HAVE_LIBAUDIT ++ int fd = audit_open (); ++ if (fd < 0) ++ return; ++ audit_log_user_message (fd, AUDIT_SYSTEM_SHUTDOWN, "init", ++ NULL, NULL, NULL, 1); ++ close (fd); ++#endif ++} +diff --git a/util/sysv.c b/util/sysv.c +index fbca39f..5476f79 100644 +--- a/util/sysv.c ++++ b/util/sysv.c +@@ -36,6 +36,12 @@ + #include + #include + ++#include ++#ifdef HAVE_LIBAUDIT ++#include ++#include ++#endif ++ + #include "dbus/upstart.h" + + #include "utmp.h" +@@ -54,6 +60,7 @@ + + /* Prototypes for static functions */ + static void error_handler (NihError **err, NihDBusMessage *message); ++static void send_audit_event (int old, int level); + + + /** +@@ -161,6 +168,8 @@ sysv_change_runlevel (int runlevel, + runlevel, prevlevel) < 0) + nih_free (nih_error_get ()); + ++ send_audit_event(prevlevel, runlevel); ++ + /* Make the EmitEvent call, we don't wait for the event to finish + * because sysvinit never did. + */ +@@ -199,3 +208,41 @@ error_handler (NihError ** err, + { + *err = nih_error_steal (); + } ++ ++/** ++ * send_audit_event ++ * @old: current run level ++ * @level: new run level ++ * ++ * Send system runlevel change audit event. If level is 0, then ++ * we consider this to be a reboot event. ++ **/ ++static void ++send_audit_event (int old, int level) ++{ ++#ifdef HAVE_LIBAUDIT ++ int fd = audit_open (); ++ ++ if (fd < 0) ++ return; ++ ++ /* first runlevel change after boot */ ++ if (old == 'N') ++ audit_log_user_message (fd, AUDIT_SYSTEM_BOOT, "init", ++ NULL, NULL, NULL, 1); ++ ++ char buf[64]; ++ ++ snprintf (buf, sizeof (buf), ++ "old-level=%c new-level=%c", old, level); ++ audit_log_user_message (fd, AUDIT_SYSTEM_RUNLEVEL, buf, ++ NULL, NULL, NULL, 1); ++ ++ /* shutdown or reboot */ ++ if (level == '0' || level == '6') ++ audit_log_user_message (fd, AUDIT_SYSTEM_SHUTDOWN, "init", ++ NULL, NULL, NULL, 1); ++ ++ close (fd); ++#endif ++} diff --git a/upstart-close-fd.patch b/upstart-close-fd.patch new file mode 100644 index 0000000..04391d1 --- /dev/null +++ b/upstart-close-fd.patch @@ -0,0 +1,23 @@ +diff --git a/init/job_process.c b/init/job_process.c +index 8ce2f72..c2c96cb 100644 +--- a/init/job_process.c ++++ b/init/job_process.c +@@ -298,6 +298,7 @@ job_process_run (Job *job, + + /* Feed the script to the child process */ + if (shell) { ++ nih_local char *cmd = NULL; + NihIo *io; + + /* Clean up and close the reading end (we don't need it) */ +@@ -317,6 +318,10 @@ job_process_run (Job *job, + nih_free (err); + } + ++ /* Close leaking fd first */ ++ cmd = NIH_MUST (nih_sprintf (NULL, "<&%d- ;", fds[0])); ++ NIH_ZERO (nih_io_write (io, cmd, strlen (cmd))); ++ + NIH_ZERO (nih_io_write (io, script, strlen (script))); + nih_io_shutdown (io); + } diff --git a/upstart-lib-upstart.patch b/upstart-lib-upstart.patch new file mode 100644 index 0000000..bcef9d0 --- /dev/null +++ b/upstart-lib-upstart.patch @@ -0,0 +1,13 @@ +diff --git a/init/paths.h b/init/paths.h +index c7ddec1..1402e9d 100644 +--- a/init/paths.h ++++ b/init/paths.h +@@ -26,7 +26,7 @@ + * This is the default PATH set by the init process itself. + **/ + #ifndef PATH +-#define _PATH "/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin" ++#define _PATH "/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/lib/upstart:/sbin:/bin" + #ifdef EXTRA_PATH + #define PATH _PATH ":" EXTRA_PATH + #else diff --git a/upstart-override.patch b/upstart-override.patch new file mode 100644 index 0000000..7989d57 --- /dev/null +++ b/upstart-override.patch @@ -0,0 +1,5145 @@ +diff --git a/init/conf.c b/init/conf.c +index 89d2cd3..a5a9b85 100644 +--- a/init/conf.c ++++ b/init/conf.c +@@ -49,7 +49,7 @@ + #include "parse_conf.h" + #include "conf.h" + #include "errors.h" +- ++#include "paths.h" + + /* Prototypes for static functions */ + static int conf_source_reload_file (ConfSource *source) +@@ -71,9 +71,19 @@ static int conf_file_visitor (ConfSource *source, + struct stat *statbuf) + __attribute__ ((warn_unused_result)); + +-static int conf_reload_path (ConfSource *source, const char *path) ++static int conf_reload_path (ConfSource *source, const char *path, ++ const char *override_path) ++ __attribute__ ((warn_unused_result)); ++ ++static inline int is_conf_file (const char *path) + __attribute__ ((warn_unused_result)); + ++static inline int is_conf_file_std (const char *path) ++ __attribute__ ((warn_unused_result)); ++ ++static inline int ++is_conf_file_override (const char *path) ++ __attribute__ ((warn_unused_result)); + + /** + * conf_sources: +@@ -86,6 +96,115 @@ NihList *conf_sources = NULL; + + + /** ++ * is_conf_file_std: ++ * @path: path to check. ++ * ++ * Determine if specified path contains a legitimate ++ * configuration file name. ++ * ++ * Returns: TRUE if @path contains a valid configuration file name, ++ * else FALSE. ++ * ++ **/ ++static inline int ++is_conf_file_std (const char *path) ++{ ++ char *ptr = strrchr (path, '.'); ++ ++ if (ptr && IS_CONF_EXT_STD (ptr)) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++/** ++ * is_conf_file_override: ++ * @path: path to check. ++ * ++ * Determine if specified path contains a legitimate ++ * override file name. ++ * ++ * Returns: TRUE if @path contains a valid override file name, ++ * else FALSE. ++ * ++ **/ ++static inline int ++is_conf_file_override (const char *path) ++{ ++ char *ptr = strrchr (path, '.'); ++ ++ if (ptr && IS_CONF_EXT_OVERRIDE (ptr)) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++/** ++ * is_conf_file: ++ * @path: path to check. ++ * ++ * Determine if specified path contains a legitimate ++ * configuration file or override file name. ++ * ++ * Returns: TRUE if @path contains a valid configuration ++ * file or override file name, else FALSE. ++ * ++ **/ ++static inline int ++is_conf_file (const char *path) ++{ ++ char *ptr = strrchr (path, '.'); ++ ++ if (ptr && (ptr > path) && (ptr[-1] != '/') && IS_CONF_EXT (ptr)) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++/** ++ * Convert a configuration file name to an override file name and vice ++ * versa. ++ * ++ * For example, if @path is "foo.conf", this function will return ++ * "foo.override", whereas if @path is "foo.override", it will return ++ * "foo.conf". ++ * ++ * Note that this function should be static, but isn't to allow the ++ * tests to access it. ++ * ++ * @parent: parent of returned path, ++ * @path: path to a configuration file. ++ * ++ * Returns: newly allocated toggled path, or NULL on error. ++ **/ ++char * ++toggle_conf_name (const void *parent, ++ const char *path) ++{ ++ char *new_path; ++ char *ext; ++ char *new_ext; ++ size_t len; ++ ++ ext = strrchr (path, '.'); ++ if (!ext) ++ return NULL; ++ ++ new_ext = IS_CONF_EXT_STD (ext) ++ ? CONF_EXT_OVERRIDE ++ : CONF_EXT_STD; ++ ++ len = strlen (new_ext); ++ ++ new_path = NIH_MUST (nih_strndup (parent, path, (ext - path) + len)); ++ ++ memcpy (new_path + (ext - path), new_ext, len); ++ ++ return new_path; ++} ++ ++ ++/** + * conf_init: + * + * Initialise the conf_sources list. +@@ -233,10 +352,9 @@ conf_reload (void) + NihError *err; + + err = nih_error_get (); +- if (err->number != ENOENT) +- nih_error ("%s: %s: %s", source->path, +- _("Unable to load configuration"), +- err->message); ++ nih_error ("%s: %s: %s", source->path, ++ _("Unable to load configuration"), ++ err->message); + nih_free (err); + } + } +@@ -329,10 +447,16 @@ static int + conf_source_reload_file (ConfSource *source) + { + NihError *err = NULL; ++ nih_local char *override_path = NULL; ++ ++ struct stat statbuf; + + nih_assert (source != NULL); + nih_assert (source->type == CONF_FILE); + ++ /* this function should only be called for standard ++ * configuration files. ++ */ + if (! source->watch) { + nih_local char *dpath = NULL; + char *dname; +@@ -361,7 +485,7 @@ conf_source_reload_file (ConfSource *source) + /* Parse the file itself. If this fails, then we can discard the + * inotify error, since this one will be better. + */ +- if (conf_reload_path (source, source->path) < 0) { ++ if (conf_reload_path (source, source->path, NULL) < 0) { + if (err) + nih_free (err); + +@@ -382,6 +506,26 @@ conf_source_reload_file (ConfSource *source) + nih_free (err); + } + ++ if (! is_conf_file_std (source->path)) ++ return 0; ++ ++ override_path = toggle_conf_name (NULL, source->path); ++ ++ if (stat (override_path, &statbuf) != 0) ++ return 0; ++ ++ nih_debug ("Updating configuration for %s from %s", ++ source->path, override_path); ++ if (conf_reload_path (source, source->path, override_path) < 0) { ++ NihError *err; ++ ++ err = nih_error_get (); ++ nih_error ("%s: %s: %s", override_path, ++ _("Error while reloading configuration file"), ++ err->message); ++ nih_free (err); ++ } ++ + return 0; + } + +@@ -501,19 +645,18 @@ conf_file_filter (ConfSource *source, + * @is_dir: TRUE of @path is a directory. + * + * This is the file filter used for the jobs directory, we only care +- * about paths with the ".conf" extension. Directories that +- * match the nih_file_ignore() function are also ignored. ++ * about paths with particular extensions (see IS_CONF_EXT). ++ * ++ * Directories that match the nih_file_ignore() function are also ignored. + * +- * Returns: FALSE if @path ends in ".conf", or is the original source, +- * TRUE otherwise. ++ * Returns: FALSE if @path ends in ".conf" or ".override", ++ * or is the original source, TRUE otherwise. + **/ + static int + conf_dir_filter (ConfSource *source, + const char *path, + int is_dir) + { +- char *ptr; +- + nih_assert (source != NULL); + nih_assert (path != NULL); + +@@ -523,8 +666,7 @@ conf_dir_filter (ConfSource *source, + if (is_dir) + return nih_file_ignore (NULL, path); + +- ptr = strrchr (path, '.'); +- if (ptr && (ptr > path) && (ptr[-1] != '/') && (! strcmp (ptr, ".conf"))) ++ if (is_conf_file (path)) + return FALSE; + + return TRUE; +@@ -546,29 +688,92 @@ conf_dir_filter (ConfSource *source, + * After checking that it was a regular file that was changed, we reload it; + * we expect this to fail sometimes since the file may be only partially + * written. +- **/ ++ **/ + static void + conf_create_modify_handler (ConfSource *source, + NihWatch *watch, + const char *path, + struct stat *statbuf) + { ++ ConfFile *file = NULL; ++ const char *error_path = path; ++ nih_local char *new_path = NULL; ++ int ret; ++ + nih_assert (source != NULL); + nih_assert (watch != NULL); + nih_assert (path != NULL); + nih_assert (statbuf != NULL); + ++ /* note that symbolic links are ignored */ + if (! S_ISREG (statbuf->st_mode)) + return; + +- if (conf_reload_path (source, path) < 0) { ++ new_path = toggle_conf_name (NULL, path); ++ file = (ConfFile *)nih_hash_lookup (source->files, new_path); ++ ++ if (is_conf_file_override (path)) { ++ if (! file) { ++ /* override file has no corresponding conf file */ ++ nih_debug ("Ignoring orphan override file %s", path); ++ return; ++ } ++ ++ /* reload conf file */ ++ nih_debug ("Loading configuration file %s", new_path); ++ ret = conf_reload_path (source, new_path, NULL); ++ if (ret < 0) { ++ error_path = new_path; ++ goto error; ++ } ++ ++ /* overlay override settings */ ++ nih_debug ("Loading override file %s for %s", path, new_path); ++ ret = conf_reload_path (source, new_path, path); ++ if (ret < 0) { ++ error_path = path; ++ goto error; ++ } ++ } else { ++ nih_debug ("Loading configuration and override files for %s", path); ++ ++ /* load conf file */ ++ nih_debug ("Loading configuration file %s", path); ++ ret = conf_reload_path (source, path, NULL); ++ if (ret < 0) { ++ error_path = path; ++ goto error; ++ } ++ ++ /* ensure we ignore directory changes (which won't have overrides. */ ++ if (is_conf_file_std (path)) { ++ struct stat st; ++ if (stat (new_path, &st) == 0) { ++ /* overlay override settings */ ++ nih_debug ("Loading override file %s for %s", new_path, path); ++ ret = conf_reload_path (source, path, new_path); ++ if (ret < 0) { ++ error_path = new_path; ++ goto error; ++ } ++ } ++ ++ } ++ } ++ ++ return; ++ ++error: ++ { + NihError *err; + + err = nih_error_get (); +- nih_error ("%s: %s: %s", path, +- _("Error while loading configuration file"), +- err->message); ++ nih_error ("%s: %s: %s", error_path, ++ _("Error while loading configuration file"), ++ err->message); + nih_free (err); ++ if (file) ++ nih_unref (file, source); + } + } + +@@ -585,13 +790,14 @@ conf_create_modify_handler (ConfSource *source, + * + * We lookup the file in our hash table, and if we can find it, perform + * the usual deletion of it. +- **/ ++ **/ + static void + conf_delete_handler (ConfSource *source, + NihWatch *watch, + const char *path) + { + ConfFile *file; ++ nih_local char *new_path = NULL; + + nih_assert (source != NULL); + nih_assert (watch != NULL); +@@ -603,7 +809,11 @@ conf_delete_handler (ConfSource *source, + * it's probably a directory or something, so just ignore it. + */ + file = (ConfFile *)nih_hash_lookup (source->files, path); +- if (! file) { ++ /* Note we have to be careful to consider deletion of directories too. ++ * This is handled implicitly by the override check which will return ++ * false if passed a directory in this case. ++ */ ++ if (! file && ! is_conf_file_override (path)) { + if (! strcmp (watch->path, path)) { + nih_warn ("%s: %s", source->path, + _("Configuration directory deleted")); +@@ -614,7 +824,30 @@ conf_delete_handler (ConfSource *source, + return; + } + +- nih_unref (file, source); ++ /* non-override files (and directories) are the simple case, so handle ++ * them and leave. ++ */ ++ if (! is_conf_file_override (path)) { ++ nih_unref (file, source); ++ return; ++ } ++ ++ /* if an override file is deleted for which there is a corresponding ++ * conf file, reload the conf file to remove any modifications ++ * introduced by the override file. ++ */ ++ new_path = toggle_conf_name (NULL, path); ++ file = (ConfFile *)nih_hash_lookup (source->files, new_path); ++ ++ if (file) { ++ nih_debug ("Reloading configuration for %s on deletion of overide (%s)", ++ new_path, path); ++ ++ if ( conf_reload_path (source, new_path, NULL) < 0 ) { ++ nih_warn ("%s: %s", new_path, ++ _("Unable to reload configuration after override deletion")); ++ } ++ } + } + + /** +@@ -637,22 +870,61 @@ conf_file_visitor (ConfSource *source, + const char *path, + struct stat *statbuf) + { ++ ConfFile *file = NULL; ++ nih_local char *new_path = NULL; ++ + nih_assert (source != NULL); + nih_assert (dirname != NULL); + nih_assert (path != NULL); + nih_assert (statbuf != NULL); + ++ /* We assume that CONF_EXT_STD files are visited before ++ * CONF_EXT_OVERRIDE files. Happily, this assumption is currently ++ * valid since CONF_EXT_STD comes before CONF_EXT_OVERRIDE if ordered ++ * alphabetically. ++ * ++ * If this were ever to change (for example if we decided to ++ * rename the CONF_EXT_OVERRIDE files to end in ".abc", say), the logic ++ * in this function would be erroneous since it would never be possible when ++ * visiting an override file (before a conf file) to lookup a conf file ++ * in the hash, since the conf file would not yet have been seen and thus would ++ * not exist in the hash (yet). ++ */ ++ nih_assert (CONF_EXT_STD[1] < CONF_EXT_OVERRIDE[1]); ++ + if (! S_ISREG (statbuf->st_mode)) + return 0; + +- if (conf_reload_path (source, path) < 0) { +- NihError *err; ++ if (is_conf_file_std (path)) { ++ if (conf_reload_path (source, path, NULL) < 0) { ++ NihError *err; + +- err = nih_error_get (); +- nih_error ("%s: %s: %s", path, +- _("Error while loading configuration file"), +- err->message); +- nih_free (err); ++ err = nih_error_get (); ++ nih_error ("%s: %s: %s", path, ++ _("Error while loading configuration file"), ++ err->message); ++ nih_free (err); ++ } ++ return 0; ++ } ++ ++ new_path = toggle_conf_name (NULL, path); ++ file = (ConfFile *)nih_hash_lookup (source->files, new_path); ++ ++ if (file) { ++ /* we're visiting an override file with an associated conf file that ++ * has already been loaded, so just overlay the override file. If ++ * there is no corresponding conf file, we ignore the override file. ++ */ ++ if (conf_reload_path (source, new_path, path) < 0) { ++ NihError *err; ++ ++ err = nih_error_get (); ++ nih_error ("%s: %s: %s", new_path, ++ _("Error while reloading configuration file"), ++ err->message); ++ nih_free (err); ++ } + } + + return 0; +@@ -662,12 +934,15 @@ conf_file_visitor (ConfSource *source, + /** + * conf_reload_path: + * @source: configuration source, +- * @path: path of file to be reloaded. ++ * @path: path of conf file to be reloaded. ++ * @override_path: if TRUE and @path refers to a path associated with @source, ++ * overlay the contents of @path into the existing @source entry for ++ * @path. If FALSE, discard any existing knowledge of @path. + * +- * This function is used to parse the file at @path in the context of the +- * given configuration @source. Necessary ConfFile structures are allocated +- * and attached to @source as appropriate. CONF_FILE sources always have +- * a single ConfFile when the file exists. ++ * This function is used to parse the file at @path (or @override_path) in the ++ * context of the given configuration @source. Necessary ConfFile structures ++ * are allocated and attached to @source as appropriate. CONF_FILE sources ++ * always have a single ConfFile when the file exists. + * + * If the file has been parsed before, then the existing item is deleted and + * freed if the file fails to load, or after the new item has been parsed. +@@ -679,36 +954,43 @@ conf_file_visitor (ConfSource *source, + **/ + static int + conf_reload_path (ConfSource *source, +- const char *path) ++ const char *path, ++ const char *override_path) + { +- ConfFile *file; ++ ConfFile *file = NULL; + nih_local char *buf = NULL; + const char *start, *end; + nih_local char *name = NULL; + size_t len, pos, lineno; + NihError *err = NULL; ++ const char *path_to_load; + + nih_assert (source != NULL); + nih_assert (path != NULL); + +- /* Look up the old file in memory, and then free it. In cases +- * of failure, we discard it anyway, so there's no particular reason ++ path_to_load = ( override_path ? override_path : path); ++ ++ /* If there is no corresponding override file, look up the old ++ * conf file in memory, and then free it. In cases of failure, ++ * we discard it anyway, so there's no particular reason + * to keep it around anymore. + */ + file = (ConfFile *)nih_hash_lookup (source->files, path); +- if (file) ++ if (! override_path && file) + nih_unref (file, source); + + /* Read the file into memory for parsing, if this fails we don't + * bother creating a new ConfFile structure for it and bail out + * now. + */ +- buf = nih_file_read (NULL, path, &len); ++ buf = nih_file_read (NULL, path_to_load, &len); + if (! buf) + return -1; + +- /* Parse the file, storing the item in a new ConfFile structure. */ +- file = NIH_MUST (conf_file_new (source, path)); ++ /* Create a new ConfFile structure (if no override_path was specified) */ ++ file = (ConfFile *)nih_hash_lookup (source->files, path); ++ if (! file) ++ file = NIH_MUST (conf_file_new (source, path)); + + pos = 0; + lineno = 1; +@@ -717,7 +999,14 @@ conf_reload_path (ConfSource *source, + case CONF_FILE: + case CONF_DIR: + /* Simple file of options; usually no item attached to it. */ +- nih_debug ("Loading configuration from %s", path); ++ if (override_path) { ++ nih_debug ("Updating configuration for %s from %s", ++ path, override_path); ++ } else { ++ nih_debug ("Loading configuration from %s %s", ++ (source->type == CONF_DIR ? "directory" : "file"), path); ++ } ++ + if (parse_conf (file, buf, len, &pos, &lineno) < 0) + err = nih_error_get (); + +@@ -735,7 +1024,7 @@ conf_reload_path (ConfSource *source, + start++; + + end = strrchr (start, '.'); +- if (end && (! strcmp (end, ".conf"))) { ++ if (end && IS_CONF_EXT (end)) { + name = NIH_MUST (nih_strndup (NULL, start, end - start)); + } else { + name = NIH_MUST (nih_strdup (NULL, start)); +@@ -744,8 +1033,14 @@ conf_reload_path (ConfSource *source, + /* Create a new job item and parse the buffer to produce + * the job definition. + */ +- nih_debug ("Loading %s from %s", name, path); +- file->job = parse_job (NULL, name, buf, len, &pos, &lineno); ++ if (override_path) { ++ nih_debug ("Updating %s (%s) with %s", ++ name, path, override_path); ++ } else { ++ nih_debug ("Loading %s from %s", name, path); ++ } ++ file->job = parse_job (NULL, file->job, ++ name, buf, len, &pos, &lineno); + if (file->job) { + job_class_consider (file->job); + } else { +@@ -779,7 +1074,7 @@ conf_reload_path (ConfSource *source, + case PARSE_EXPECTED_OPERATOR: + case PARSE_EXPECTED_VARIABLE: + case PARSE_MISMATCHED_PARENS: +- nih_error ("%s:%zi: %s", path, lineno, err->message); ++ nih_error ("%s:%zi: %s", path_to_load, lineno, err->message); + nih_free (err); + err = NULL; + break; +@@ -882,3 +1177,149 @@ conf_select_job (const char *name) + + return NULL; + } ++ ++#ifdef DEBUG ++ ++size_t ++debug_count_list_entries (const NihList *list) ++{ ++ size_t i = 0; ++ NIH_LIST_FOREACH (list, iter) { ++ i++; ++ } ++ return i; ++} ++ ++size_t ++debug_count_hash_entries (const NihHash *hash) ++{ ++ size_t i = 0; ++ NIH_HASH_FOREACH_SAFE (hash, iter) { ++ i++; ++ } ++ return i; ++} ++ ++void ++debug_show_job_class (const JobClass *job) ++{ ++ int i; ++ char **env = (char **)job->env; ++ char **export = (char **)job->export; ++ ++ nih_assert (job); ++ ++ nih_debug ("JobClass %p: name='%s', path='%s', task=%d, " ++ "respawn=%d, console=%x, deleted=%d, debug=%d", ++ job, job->name, job->path, job->task, ++ job->respawn, job->console, job->deleted, job->debug); ++ ++ nih_debug ("\tstart_on=%p, stop_on=%p, emits=%p, process=%p", ++ job->start_on, job->stop_on, job->emits, job->process); ++ ++ nih_debug ("\tauthor='%s', description='%s'", ++ job->author, job->description); ++ ++ if (env && *env) { ++ nih_debug ("\tenv:"); ++ i = 0; ++ while ( *env ) { ++ nih_debug ("\t\tenv[%d]='%s' (len=%u+1)", ++ i, *env, strlen (*env)); ++ env++; ++ ++i; ++ } ++ } else { ++ nih_debug ("\tenv: none."); ++ } ++ ++ ++ if (export && *export) { ++ nih_debug ("\texport:"); ++ i = 0; ++ while ( *export ) { ++ nih_debug ("\t\tenv[%d]='%s' (len=%u+1)", ++ i, *export, strlen (*export)); ++ export++; ++ ++i; ++ } ++ } ++ else { ++ nih_debug ("\texport: none"); ++ } ++} ++ ++void ++debug_show_job_classes (void) ++{ ++ nih_debug ("job_classes:"); ++ ++ NIH_HASH_FOREACH_SAFE (job_classes, iter) { ++ JobClass *job = (JobClass *)iter; ++ debug_show_job_class (job); ++ } ++} ++ ++void ++debug_show_event (const Event *event) ++{ ++ nih_assert (event); ++ ++ nih_debug ("Event %p: name='%s', progress=%x, failed=%d, " ++ "blockers=%d, blocking=%p", ++ event, event->name, event->progress, event->failed, ++ event->blockers, (void *)&event->blocking); ++} ++ ++void ++debug_show_conf_file (const ConfFile *file) ++{ ++ nih_assert (file); ++ ++ nih_debug ("ConfFile %p: path='%s', source=%p, flag=%x, job=%p", ++ file, file->path, file->source, file->flag, file->job); ++ ++ /* Some ConfFile objects won't have any JobClass details, for example, ++ * the ConfFile object associated with "/etc/init.conf". ++ */ ++ if (! file->job) { ++ nih_debug ("ConfFile %p: job: no JobClass object.", file); ++ return; ++ } ++ ++ nih_debug ("ConfFile %p: job:", file); ++ debug_show_job_class (file->job); ++} ++ ++void ++debug_show_conf_source (const ConfSource *source) ++{ ++ nih_assert (source); ++ ++ nih_debug ("ConfSource %p: path='%s', type=%x, flag=%x", ++ source, source->path, source->type, source->flag); ++ ++ nih_debug ("ConfSource %p files (%d):", source, ++ debug_count_hash_entries (source->files)); ++ ++ NIH_HASH_FOREACH (source->files, file_iter) { ++ ConfFile *file = (ConfFile *)file_iter; ++ debug_show_conf_file (file); ++ } ++} ++ ++void ++debug_show_conf_sources (void) ++{ ++ nih_assert (conf_sources); ++ ++ nih_debug ("conf_sources:"); ++ ++ NIH_LIST_FOREACH (conf_sources, iter) { ++ ConfSource *source = (ConfSource *)iter; ++ debug_show_conf_source (source); ++ } ++} ++ ++#endif /* DEBUG */ ++ +diff --git a/init/conf.h b/init/conf.h +index d6915f3..bac02ac 100644 +--- a/init/conf.h ++++ b/init/conf.h +@@ -124,6 +124,46 @@ int conf_file_destroy (ConfFile *file); + + JobClass * conf_select_job (const char *name); + ++char *toggle_conf_name (const void *parent, const char *path) ++ __attribute__ ((warn_unused_result, malloc)); ++ ++#ifdef DEBUG ++ ++/* used for debugging only */ ++ ++size_t ++debug_count_hash_entries (const NihHash *hash); ++ ++size_t ++debug_count_list_entries (const NihList *list) ++ __attribute__ ((unused)); ++ ++void ++debug_show_job_class (const JobClass *job) ++ __attribute__ ((unused)); ++ ++void ++debug_show_job_classes (void) ++ __attribute__ ((unused)); ++ ++void ++debug_show_event (const Event *event) ++ __attribute__ ((unused)); ++ ++void ++debug_show_conf_file(const ConfFile *file) ++ __attribute__ ((unused)); ++ ++void ++debug_show_conf_source(const ConfSource *source) ++ __attribute__ ((unused)); ++ ++void ++debug_show_conf_sources(void) ++ __attribute__ ((unused)); ++ ++#endif ++ + NIH_END_EXTERN + + #endif /* INIT_CONF_H */ +diff --git a/init/man/init.5 b/init/man/init.5 +index 73fff91..56a4d0e 100644 +--- a/init/man/init.5 ++++ b/init/man/init.5 +@@ -1,42 +1,79 @@ +-.TH init 5 2010-12-14 "Upstart" ++.TH init 5 2011-01-12 "Upstart" + .\" + .SH NAME + init \- Upstart init daemon job configuration + .\" + .SH SYNOPSIS +-.B /etc/init ++.B /etc/init/ + .\" + .SH DESCRIPTION + On startup, the Upstart + .BR init (8) +-daemon reads its job configuration from the +-.I /etc/init +-directory, and watches for future changes using ++daemon reads its job configuration from files in the ++.I /etc/init/ ++directory, and watches for future changes to these files using + .BR inotify (7). + +-Files in this directory must end in +-.I .conf +-and may also be present in sub-directories. ++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: + +-Each file defines a single service or task, with the name taken from its +-relative path within the directory without the extension. For example a +-job defined in ++.IP \(bu 4 ++Files ending in ++.I .conf ++are called configuration files, or simply "conf files" for short. ++These are the primary vehicle for specifying a job. ++.IP \(bu 4 ++Files ending in ++.I .override ++are called override files. If an override file is present, the stanzas ++it contains take precedence over those equivalently named stanzas in the ++corresponding configuration file contents for a particular job. ++The main use for override files is to modify how a job will run without ++having to modify its configuration file directly. See the section ++\fBOverride File Handling\fP below for further details. ++.P ++A job can thus be defined by either: ++.IP \[bu] 2 ++A single configuration file. ++.IP \[bu] ++A single configuration file \fBand\fP a single override file. ++.P ++Unless explicitly stated otherwise, any reference to a jobs ++configuration can refer both to a configuration file or an override ++file. ++ ++Each configuration file defines the template for a single \fIservice\fP ++(long-running process or daemon) or \fItask\fP (short-lived process). ++ ++Note that a configuration file is not itself a job: it is a description ++of an environmenta job could be run in. A job is the runtime embodiment ++of a configuration file. ++ ++The configuration file name as displayed by Upstart and associated ++tooling is taken from its relative path within the directory without the ++extension. For example a configuration file + .I /etc/init/rc-sysinit.conf + is named + .IR rc-sysinit , +-while a job defined in ++while a configuration file + .I /etc/init/net/apache.conf + is named + .IR net/apache . ++Since override files only modify the way a configuration file is ++interpreted, they are not named. + +-These files are plain text and should not be executable. ++Configuration files are plain text and should not be executable. + .\" +-.SS Format ++.SS Configuration File Format + Each line begins with a configuration stanza and continues until either + the end of the line or a line containing a closing stanza. Line breaks + within a stanza are permitted within single or double quotes, or if + preceeded by a blackslash. + ++If a stanza is duplicated, the last occurence will be used. Unrecognized ++stanzas will generate parse errors, which will stop a job from running. ++ + Stanzas and their arguments are delimited by whitespace, which consists + of one or more space or tab characters which are otherwise ignored unless + placed within single or double quotes. +@@ -144,10 +181,10 @@ event is emitted. It is typically used to clean up the environment, + such as removing temporary directories. + + .PP +-All of these processes, including the main process, are optional. Services +-without a main process will appear to be running until they are stopped, +-this is commonly used to define states such as runlevels. It's quite +-permissable to have no main process, but to have ++All of these processes, including the main process, are optional. ++Services without a main process will appear to be running until they are ++stopped: this is commonly used to define states such as runlevels. It ++is permissable to have no main process, but to have + .B pre-start + and + .B post-stop +@@ -249,7 +286,7 @@ The + .B stop on + stanza defines the set of events that will cause the job to be automatically + stopped. It has the same syntax as +-.B start on ++\fBstart on\fP. + + .I VALUE + may additionally expand the value of any variable that came from the +@@ -306,7 +343,8 @@ utility to default to acting on the job the commands are called from. + .B env \fIKEY\fR[=\fIVALUE\fR] + Defines a default environment variable, the value of which may be overriden + by the event or command that starts the job. +-If no value is given, then the value is taken from the ++If \'KEY=VALUE\' is specified, the variable KEY is given the value VALUE. ++If only \'KEY\' is given, then the value is taken from the + .BR init (8) + daemon's own environment. + .\" +@@ -366,7 +404,7 @@ Respawning is subject to a limit, if the job is respawned more than + times in + .I INTERVAL + seconds, it will be considered to be having deeper problems and will +-be stopped. ++be stopped. Default COUNT is 10. Default INTERVAL is 5 seconds. + + This only applies to automatic respawns and not the + .BR restart (8) +@@ -385,7 +423,8 @@ normal exit 0 1 TERM HUP + .SS Instances + By default, only one instance of any job is permitted to exist at one + time. Attempting to start a job when it's already starting or running +-results in an error. ++results in an error. Note that a job is considered to be running if its ++pre-start process is running. + + Multiple instances may be permitted by defining the names of those + instances. If an instance with the same name is not already starting +@@ -561,6 +600,46 @@ value, both of which are integers. The special value + .B unlimited + may be specified for either. + .\" ++.SS Override File Handling ++Override files allow a jobs environment to be changed without modifying ++the jobs configuration file. Rules governing override files: ++ ++.IP \[bu] 2 ++If a job is embodied with only a configuration file, the contents of ++this file define the job. ++.IP \[bu] ++If an override files exists where there is no existing cofiguration ++file, the override file is ignored. ++.IP \[bu] ++If both a configuration file \fBand\fP an override file exist for a job ++and both files are syntactically correct: ++.RS ++.IP \[bu] 2 ++stanzas in the override file will take precedence over stanzas present ++in the corresponding configuration file. ++.IP \[bu] ++stanzas in the override file which are not present in the corresponding ++configuration file will be honoured when the job runs. ++.RE ++.IP \[bu] ++If both a configuration file and an override file exist for a job and ++subsequently the override file is deleted, the configuration file is ++automatically reloaded with the effect that any changes introduced by ++the override file are undone and the configuration file alone now defines ++the job. ++.IP \[bu] ++If both a configuration file and an override file exist for a job and ++subsequently the configuration file is deleted, a new instance of the ++job can no longer be started (since without a corresponding ++configuration file an override file is ignored). ++.IP \[bu] ++If both a configuration file and an override file exist for a job and ++any of the contents of the override file are invalid, the override file ++is ignored and only the contents of the configuration file are ++considered. ++.P ++ ++.\" + .SS Miscellaneous + .TP + .B kill timeout \fIINTERVAL +@@ -568,7 +647,7 @@ Specifies the interval between sending the job's main process the + .I SIGTERM + and + .I SIGKILL +-signals when stopping the running job. ++signals when stopping the running job. Default is 5 seconds. + .\" + .TP + .B expect stop +@@ -609,6 +688,48 @@ Without this stanza + is unable to supervise forking processes and will believe them to have + stopped as soon as they fork on startup. + .\" ++.SH BUGS ++The ++.B and ++and ++.B or ++operators allowed with ++.B start on ++and ++.B stop on ++do not work intuitively: operands to the right of either operator are ++only evaluated once and state information is then discarded. This can ++lead to jobs with complex \fBstart on\fP or \fPstop on\fP conditions ++not behaving as expected \fIwhen restarted\fP. For example, if a job ++encodes the following condition: ++.ti +4 ++.sp 1 ++.nf ++start on A and (B or C) ++.fi ++ ++When 'A' and 'B' become true, the condition is satisfied so the job will ++be run. However, if the job ends and subsequently 'A' and 'C' become true, ++the job will \fInot\fP be re-run even though the condtion is satisfied. ++Avoid using complex conditions with jobs which need to be restarted. ++.\" ++.SH AUTHOR ++Manual page written by Scott James Remnant ++.RB < scott@netsplit.com > ++and James Hunt ++.RB < james.hunt@canonical.com > . ++.\" ++.SH REPORTING BUGS ++Report bugs at ++.RB < https://launchpad.net/upstart/+bugs > ++.\" ++.SH COPYRIGHT ++Copyright \(co 2010 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. ++ ++.\" + .SH SEE ALSO + .BR init (8) + .BR sh (1) +diff --git a/init/parse_job.c b/init/parse_job.c +index 4e05454..76d3145 100644 +--- a/init/parse_job.c ++++ b/init/parse_job.c +@@ -263,6 +263,7 @@ static NihConfigStanza stanzas[] = { + /** + * parse_job: + * @parent: parent object for new job, ++ * @update: If not NULL, update the existing specified JobClass, + * @name: name of new job, + * @file: file or string to parse, + * @len: length of @file, +@@ -278,10 +279,12 @@ static NihConfigStanza stanzas[] = { + * of the returned job are freed, the returned job will also be + * freed. + * +- * Returns: new JobClass structure on success, NULL on raised error. ++ * Returns: if @update is NULL, returns new JobClass structure on success, NULL on raised error. ++ * If @update is not NULL, returns @update or NULL on error. + **/ + JobClass * + parse_job (const void *parent, ++ JobClass *update, + const char *name, + const char *file, + size_t len, +@@ -294,13 +297,22 @@ parse_job (const void *parent, + nih_assert (file != NULL); + nih_assert (pos != NULL); + +- class = job_class_new (parent, name); ++ if (update) { ++ class = update; ++ nih_debug ("Reusing JobClass %s (%s)", ++ class->name, class->path); ++ } else { ++ nih_debug ("Creating new JobClass %s", ++ name); ++ class = job_class_new (parent, name); ++ } + if (! class) + nih_return_system_error (NULL); + + if (nih_config_parse_file (file, len, pos, lineno, + stanzas, class) < 0) { +- nih_free (class); ++ if (!update) ++ nih_free (class); + return NULL; + } + +diff --git a/init/parse_job.h b/init/parse_job.h +index f98f70c..65a205f 100644 +--- a/init/parse_job.h ++++ b/init/parse_job.h +@@ -27,8 +27,8 @@ + + NIH_BEGIN_EXTERN + +-JobClass *parse_job (const void *parent, const char *name, +- const char *file, size_t len, ++JobClass *parse_job (const void *parent, JobClass *update, ++ const char *name, const char *file, size_t len, + size_t *pos, size_t *lineno) + __attribute__ ((warn_unused_result, malloc)); + +diff --git a/init/paths.h b/init/paths.h +index d7c6fa4..c7ddec1 100644 +--- a/init/paths.h ++++ b/init/paths.h +@@ -103,5 +103,55 @@ + #define TELINIT SBINDIR "/telinit" + #endif + ++/** ++ * File extension for standard configuration files. ++ **/ ++#define CONF_EXT_STD ".conf" ++ ++/** ++ * File extension for override files. ++ * ++ * Note that override files are not stored in the ConfSource 'files' hash: ++ * all JobClass information from override files is added to the JobClass for ++ * the corresponding (CONF_EXT_STD) object. ++ **/ ++#define CONF_EXT_OVERRIDE ".override" ++ ++/** ++ * Determine if specified path extension representes a standard ++ * configuration file. ++ * ++ * @period: pointer to last period in path to check. ++ * ++ * Returns 1 if specified path extension matches that for a ++ * standard configuration file, else return 0. ++ **/ ++#define IS_CONF_EXT_STD(period) \ ++ (!strcmp (period, CONF_EXT_STD)) ++ ++/** ++ * Determine if specified path extension representes an ++ * override file. ++ * ++ * @period: pointer to last period in path to check. ++ * ++ * Returns 1 if specified path extension matches that for ++ * an override file, else return 0. ++ **/ ++#define IS_CONF_EXT_OVERRIDE(period) \ ++ (!strcmp (period, CONF_EXT_OVERRIDE)) ++ ++/** ++ * Determine if specified filename has a valid configuration ++ * file name extension. ++ * ++ * @period: pointer to last period in filename. ++ * ++ * Returns: TRUE if extension beyond @period is one of the ++ * recognized types, else FALSE. ++ **/ ++#define IS_CONF_EXT(period) \ ++ (IS_CONF_EXT_STD(period) || \ ++ IS_CONF_EXT_OVERRIDE(period)) + + #endif /* INIT_PATHS_H */ +diff --git a/init/tests/test_conf.c b/init/tests/test_conf.c +index 35730b7..9a504de 100644 +--- a/init/tests/test_conf.c ++++ b/init/tests/test_conf.c +@@ -47,6 +47,50 @@ + #include "job.h" + #include "conf.h" + ++/* macro to try and ensure the environment is as pristine as possible ++ * (to avoid follow-on errors caused by not freeing objects in a ++ * previous test, say) ++ */ ++#define TEST_ENSURE_CLEAN_ENV() \ ++{ \ ++ setvbuf(stdout, NULL, _IONBF, 0); \ ++ \ ++ if (job_classes) { \ ++ TEST_HASH_EMPTY (job_classes); \ ++ } \ ++ \ ++ if (conf_sources) { \ ++ TEST_LIST_EMPTY (conf_sources); \ ++ } \ ++ \ ++ if (nih_io_watches) { \ ++ TEST_LIST_EMPTY (nih_io_watches); \ ++ } \ ++ \ ++ if (nih_timers) { \ ++ TEST_LIST_EMPTY (nih_timers); \ ++ } \ ++ \ ++ if (events) { \ ++ TEST_LIST_EMPTY (events); \ ++ } \ ++} ++ ++/* Force an inotify watch update */ ++#define TEST_FORCE_WATCH_UPDATE() \ ++{ \ ++ int nfds = 0; \ ++ fd_set readfds, writefds, exceptfds; \ ++ \ ++ FD_ZERO (&readfds); \ ++ FD_ZERO (&writefds); \ ++ FD_ZERO (&exceptfds); \ ++ \ ++ nih_debug("calling nih_io_select_fds"); \ ++ nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); \ ++ nih_debug("calling nih_io_handle_fds"); \ ++ nih_io_handle_fds (&readfds, &writefds, &exceptfds); \ ++} + + void + test_source_new (void) +@@ -2417,40 +2461,70 @@ no_inotify: + } + } + +- + void +-test_source_reload_file (void) ++test_toggle_conf_name (void) + { +- ConfSource *source; +- ConfFile *file, *old_file; +- FILE *f; +- int ret, fd[4096], i = 0, nfds; +- char dirname[PATH_MAX]; +- char tmpname[PATH_MAX], filename[PATH_MAX]; +- fd_set readfds, writefds, exceptfds; +- NihError *err; ++ char override_ext[] = ".override"; ++ char dirname[PATH_MAX]; ++ char filename[PATH_MAX]; ++ JobClass *job; ++ char *f; ++ char *p; + +- TEST_FUNCTION_FEATURE ("conf_source_reload", +- "with configuration file"); +- program_name = "test"; +- nih_log_set_priority (NIH_LOG_FATAL); ++ TEST_FUNCTION_FEATURE ("toggle_conf_name", ++ "changing conf to override"); + + TEST_FILENAME (dirname); +- mkdir (dirname, 0755); +- + strcpy (filename, dirname); + strcat (filename, "/foo.conf"); ++ f = toggle_conf_name (NULL, filename); ++ TEST_NE_P (f, NULL); + +- f = fopen (filename, "w"); +- fprintf (f, "#nothing to test\n"); +- fclose (f); ++ p = strstr (f, ".override"); ++ TEST_NE_P (p, NULL); ++ TEST_EQ_P (p, f+strlen (f) - strlen (override_ext)); ++ nih_free (f); + ++ TEST_FEATURE ("changing override to conf"); + strcpy (filename, dirname); +- strcat (filename, "/bar.conf"); ++ strcat (filename, "/bar.override"); ++ f = toggle_conf_name (NULL, filename); ++ TEST_NE_P (f, NULL); + +- f = fopen (filename, "w"); +- fprintf (f, "#nothing to test\n"); +- fclose (f); ++ p = strstr (f, ".conf"); ++ TEST_NE_P (p, NULL); ++ TEST_EQ_P (p, f+strlen (f) - strlen (".conf")); ++ nih_free (f); ++ ++ /* test parent param */ ++ job = job_class_new (NULL, "foo"); ++ TEST_NE_P (job, NULL); ++ ++ f = toggle_conf_name (job, filename); ++ TEST_NE_P (f, NULL); ++ ++ TEST_EQ (TRUE, nih_alloc_parent (f, job)); ++ ++ nih_free (job); ++} ++ ++void ++test_override (void) ++{ ++ ConfSource *source; ++ ConfFile *file; ++ FILE *f; ++ int ret, fd[4096], i = 0; ++ char dirname[PATH_MAX]; ++ char filename[PATH_MAX], override[PATH_MAX]; ++ JobClass *job; ++ NihError *err; ++ ++ program_name = "test"; ++ nih_log_set_priority (NIH_LOG_FATAL); ++ ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_GROUP ("override files"); + + /* Make sure that we have inotify before performing some tests... */ + if ((fd[0] = inotify_init ()) < 0) { +@@ -2460,207 +2534,1426 @@ test_source_reload_file (void) + close (fd[0]); + + +- /* Check that we can load a file source for the first time. An +- * inotify watch should be established on the parent directory, +- * the descriptor set to be closed-on-exec, but only that single +- * file parsed. ++ /* Explicit test of behaviour prior to introduction of override files. ++ * ++ * conf with no override before watch: ++ * create conf ++ * create watch ++ * ensure conf loaded ++ * update conf ++ * ensure conf updated ++ * delete conf ++ * ensure conf deleted + */ +- TEST_FEATURE ("with new conf file"); ++ TEST_FEATURE ("with pre-override environment (conf with no override before watch)"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create conf */ + strcpy (filename, dirname); + strcat (filename, "/foo.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); ++ fclose (f); + +- source = conf_source_new (NULL, filename, CONF_FILE); ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); + ret = conf_source_reload (source); +- + TEST_EQ (ret, 0); +- TEST_ALLOC_SIZE (source->watch, sizeof (NihWatch)); +- TEST_EQ_STR (source->watch->path, dirname); +- TEST_EQ_P (source->watch->data, source); +- +- TEST_TRUE (fcntl (source->watch->fd, F_GETFD) & FD_CLOEXEC); +- +- TEST_EQ (source->flag, TRUE); + +- strcpy (filename, dirname); +- strcat (filename, "/foo.conf"); ++ /* ensure conf loaded */ + file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_NE_P (job->start_on, NULL); + +- TEST_ALLOC_SIZE (file, sizeof (ConfFile)); +- TEST_ALLOC_PARENT (file, source); +- TEST_EQ (file->flag, source->flag); +- TEST_EQ_P (file->data, NULL); +- +- nih_free (file); ++ /* update conf */ ++ f = fopen (filename, "a"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fclose (f); + ++ TEST_FORCE_WATCH_UPDATE(); + +- strcpy (filename, dirname); +- strcat (filename, "/bar.conf"); ++ /* ensure conf updated */ + file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (job->start_on, NULL); + +- TEST_EQ_P (file, NULL); ++ /* delete conf */ ++ unlink (filename); ++ ++ TEST_FORCE_WATCH_UPDATE(); + ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ TEST_HASH_EMPTY (job_classes); + TEST_HASH_EMPTY (source->files); + +- nih_free (source); ++ TEST_FORCE_WATCH_UPDATE(); + ++ /* ensure conf deleted */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); + +- /* Check that if we create a new file in the directory, alongside +- * the one we are watching, it is ignored. ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ /* Explicit test of behaviour prior to introduction of override files. ++ * ++ * conf with no override after watch: ++ * create watch ++ * create conf ++ * ensure conf loaded ++ * update conf ++ * ensure conf updated ++ * delete conf ++ * ensure conf deleted + */ +- TEST_FEATURE ("with new file alongside conf file"); +- strcpy (filename, dirname); +- strcat (filename, "/foo.conf"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FEATURE ("with pre-override environment (conf with no override after watch)"); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); + +- source = conf_source_new (NULL, filename, CONF_FILE); ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); + ret = conf_source_reload (source); +- + TEST_EQ (ret, 0); + ++ /* create conf */ + strcpy (filename, dirname); +- strcat (filename, "/baz.conf"); +- ++ strcat (filename, "/foo.conf"); + f = fopen (filename, "w"); +- fprintf (f, "#nothing to test\n"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); + fclose (f); + +- nfds = 0; +- FD_ZERO (&readfds); +- FD_ZERO (&writefds); +- FD_ZERO (&exceptfds); +- +- nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); +- nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ TEST_FORCE_WATCH_UPDATE(); + ++ /* ensure conf loaded */ + file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); + +- TEST_EQ_P (file, NULL); +- ++ /* update conf */ ++ f = fopen (filename, "a"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fclose (f); + +- /* Check that if we modify a file in the directory, alongside +- * the one we are watching, it is ignored. +- */ +- TEST_FEATURE ("with modification to file alongside conf file"); +- strcpy (filename, dirname); +- strcat (filename, "/bar.conf"); ++ TEST_FORCE_WATCH_UPDATE(); + +- f = fopen (filename, "w"); +- fprintf (f, "#nothing to test\n"); +- fclose (f); ++ /* ensure conf updated */ ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (job->start_on, NULL); + +- nfds = 0; +- FD_ZERO (&readfds); +- FD_ZERO (&writefds); +- FD_ZERO (&exceptfds); ++ /* delete conf */ ++ unlink (filename); + +- nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); +- nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ TEST_FORCE_WATCH_UPDATE(); + ++ /* ensure conf deleted */ + file = (ConfFile *)nih_hash_lookup (source->files, filename); +- + TEST_EQ_P (file, NULL); ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); + + +- /* Check that the configuration file we're watching can be modified +- * using the direct writing technique. +- */ +- TEST_FEATURE ("with modification (direct write)"); +- strcpy (filename, dirname); +- strcat (filename, "/foo.conf"); +- +- old_file = (ConfFile *)nih_hash_lookup (source->files, filename); +- TEST_FREE_TAG (old_file); ++ TEST_FEATURE ("ensure lone override ignored before watch"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); + ++ /* create override */ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.override"); + f = fopen (filename, "w"); +- fprintf (f, "#still nothing to test\n"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); + fclose (f); + +- nfds = 0; +- FD_ZERO (&readfds); +- FD_ZERO (&writefds); +- FD_ZERO (&exceptfds); +- +- nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); +- nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); + +- TEST_FREE (old_file); ++ TEST_FORCE_WATCH_UPDATE(); + ++ /* ensure no conf object created */ ++ TEST_HASH_EMPTY (source->files); + file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); + +- TEST_ALLOC_SIZE (file, sizeof (ConfFile)); +- TEST_ALLOC_PARENT (file, source); +- TEST_EQ (file->flag, source->flag); +- TEST_EQ_P (file->data, NULL); ++ /* update override */ ++ f = fopen (filename, "a"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "author \"me\"\n"); ++ fclose (f); + +- old_file = file; ++ TEST_FORCE_WATCH_UPDATE(); + ++ /* ensure no conf object created */ ++ TEST_HASH_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); + +- /* Check that the configuration file we're watching can be modified +- * using the write and then rename technique; it should be parsed +- * and the previous file marked for deletion. +- */ +- TEST_FEATURE ("with modification (atomic rename)"); +- strcpy (tmpname, dirname); +- strcat (tmpname, "/.foo.tmp"); ++ /* delete override */ ++ unlink (filename); ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); + +- f = fopen (tmpname, "w"); +- fprintf (f, "#nothing to test\n"); +- fclose (f); + +- strcpy (filename, dirname); +- strcat (filename, "/foo.conf"); ++ TEST_FEATURE ("ensure lone override ignored after watch"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); + +- rename (tmpname, filename); ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); + +- TEST_FREE_TAG (old_file); ++ strcpy (filename, dirname); ++ strcat (filename, "/bar.override"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fclose (f); + +- nfds = 0; +- FD_ZERO (&readfds); +- FD_ZERO (&writefds); +- FD_ZERO (&exceptfds); ++ TEST_FORCE_WATCH_UPDATE(); + +- nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); +- nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ /* ensure no conf object created */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "bar"); ++ TEST_EQ_P (job, NULL); + +- TEST_FREE (old_file); ++ /* delete override */ ++ unlink (filename); ++ ++ TEST_FORCE_WATCH_UPDATE(); + ++ /* ensure override still not present */ + file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "bar"); ++ TEST_EQ_P (job, NULL); + +- TEST_ALLOC_SIZE (file, sizeof (ConfFile)); +- TEST_ALLOC_PARENT (file, source); +- TEST_EQ (file->flag, source->flag); +- TEST_EQ_P (file->data, NULL); ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); + +- old_file = file; + ++ TEST_FEATURE ("create conf, watch, then create/modify/delete override"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); + +- /* Check that we can delete the configuration file that we're +- * watching, the metadata for it should be lost. +- */ +- TEST_FEATURE ("with deletion"); ++ /* create conf */ + strcpy (filename, dirname); + strcat (filename, "/foo.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); ++ fclose (f); + +- unlink (filename); +- +- TEST_FREE_TAG (old_file); +- +- nfds = 0; +- FD_ZERO (&readfds); +- FD_ZERO (&writefds); +- FD_ZERO (&exceptfds); ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); + +- nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); +- nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ /* ensure conf loaded */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_P ((job->emits)[1], NULL); ++ TEST_NE_P (job->start_on, NULL); ++ ++ /* create override */ ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fclose (f); + +- TEST_FREE (old_file); ++ TEST_HASH_NOT_EMPTY (source->files); ++ TEST_FORCE_WATCH_UPDATE(); + ++ /* ensure conf updated */ ++ TEST_HASH_NOT_EMPTY (source->files); + file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (job->start_on, NULL); + ++ /* ensure no override in hash */ ++ file = (ConfFile *)nih_hash_lookup (source->files, override); + TEST_EQ_P (file, NULL); + ++ /* modify override */ ++ f = fopen (override, "a"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "emits world\n"); ++ fclose (f); + +- /* Check that the watch allows us to see if the file we want is +- * created using the direct writing technique, and thus parsed. ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf updated */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_STR ((job->emits)[1], "world"); ++ ++ /* delete override */ ++ unlink (override); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf reverted */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_NE_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_P ((job->emits)[1], NULL); ++ ++ nih_free (source); ++ unlink (filename); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ TEST_FEATURE ("create watch, conf, then create/modify/delete override"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ /* create conf */ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_P ((job->emits)[1], NULL); ++ TEST_NE_P (job->start_on, NULL); ++ ++ /* create override */ ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fclose (f); ++ ++ TEST_HASH_NOT_EMPTY (source->files); ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf updated */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (job->start_on, NULL); ++ ++ /* ensure no override in hash */ ++ file = (ConfFile *)nih_hash_lookup (source->files, override); ++ TEST_EQ_P (file, NULL); ++ ++ /* modify override */ ++ f = fopen (override, "a"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "emits world\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf updated */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_STR ((job->emits)[1], "world"); ++ ++ /* delete override */ ++ unlink (override); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf reverted */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_NE_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_P ((job->emits)[1], NULL); ++ ++ nih_free (source); ++ unlink (filename); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ TEST_FEATURE ("create override, watch, then create/modify/delete conf"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ ++ /* create override */ ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "author \"bar\"\n"); ++ fclose (f); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure no conf object created */ ++ TEST_HASH_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ ++ /* create conf */ ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); ++ fprintf (f, "author \"foo\"\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "bar"); ++ ++ /* modify conf */ ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on wibble\n"); ++ fprintf (f, "emits moo\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf reloaded and updated with override */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "moo"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "bar"); ++ ++ /* delete conf */ ++ unlink (filename); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf object deleted */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ TEST_HASH_EMPTY (source->files); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ file = (ConfFile *)nih_hash_lookup (source->files, override); ++ TEST_EQ_P (file, NULL); ++ ++ unlink (override); ++ ++ /* ensure no conf object still */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ TEST_HASH_EMPTY (source->files); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ file = (ConfFile *)nih_hash_lookup (source->files, override); ++ TEST_EQ_P (file, NULL); ++ ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ TEST_FEATURE ("create watch, override, then create/modify/delete conf"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ ++ /* create override */ ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "author \"bar\"\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure no conf object created */ ++ TEST_HASH_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ ++ /* create conf */ ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); ++ fprintf (f, "author \"foo\"\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "bar"); ++ ++ /* modify conf */ ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on wibble\n"); ++ fprintf (f, "emits moo\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf reloaded and updated with override */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "moo"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "bar"); ++ ++ /* delete conf */ ++ unlink (filename); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf object deleted */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ TEST_HASH_EMPTY (source->files); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ file = (ConfFile *)nih_hash_lookup (source->files, override); ++ TEST_EQ_P (file, NULL); ++ ++ unlink (override); ++ ++ /* ensure no conf object still */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ TEST_HASH_EMPTY (source->files); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ file = (ConfFile *)nih_hash_lookup (source->files, override); ++ TEST_EQ_P (file, NULL); ++ ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ TEST_FEATURE ("create override, watch, conf, then modify/delete override"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ ++ /* create override */ ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "author \"bar\"\n"); ++ fclose (f); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure no conf object created */ ++ TEST_HASH_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ ++ /* create conf */ ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); ++ fprintf (f, "author \"foo\"\n"); ++ fclose (f); ++ ++ /* FIXME: crashes here */ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "bar"); ++ ++ /* modify override */ ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "author \"meh\"\n"); ++ fprintf (f, "env wibble=wobble\n"); ++ fprintf (f, "manual\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf reloaded and updated with override */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "meh"); ++ TEST_EQ_STR ((job->env)[0], "wibble=wobble"); ++ ++ /* delete override */ ++ unlink (override); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf object reverted */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_NE_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "foo"); ++ TEST_EQ_P (job->env, NULL); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ ++ unlink (filename); ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ TEST_FEATURE ("create watch, override, conf, then modify/delete override"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* create override */ ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "author \"bar\"\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* create conf */ ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "emits hello\n"); ++ fprintf (f, "author \"foo\"\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "bar"); ++ ++ /* update override */ ++ f = fopen (override, "a"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "author \"me\"\n"); ++ fprintf (f, "env wibble=wobble\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf reloaded and updated with override */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ ++ /* should pick up override, *NOT* conf */ ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "me"); ++ TEST_EQ_STR ((job->env)[0], "wibble=wobble"); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* delete override */ ++ unlink (override); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR (job->author, "foo"); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_NE_P (job->start_on, NULL); ++ TEST_EQ_P (job->env, NULL); ++ ++ unlink (filename); ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ TEST_FEATURE ("create both conf+override files, watch, then modify/delete conf"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create conf */ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "author \"me\"\n"); ++ fprintf (f, "env foo=bar\n"); ++ fprintf (f, "emits hello\n"); ++ fclose (f); ++ ++ /* create override */ ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "author \"you\"\n"); ++ fclose (f); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_STR (job->author, "you"); ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->env)[0], "foo=bar"); ++ TEST_EQ_P (job->export, NULL); ++ ++ /* modify conf */ ++ f = fopen (filename, "a"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "export foo\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf updated */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_STR ((job->env)[0], "foo=bar"); ++ TEST_NE_P (job->export, NULL); ++ TEST_EQ_STR ((job->export)[0], "foo"); ++ ++ /* delete conf */ ++ unlink (filename); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf object deleted */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_EQ_P (file, NULL); ++ TEST_HASH_EMPTY (source->files); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_EQ_P (job, NULL); ++ file = (ConfFile *)nih_hash_lookup (source->files, override); ++ TEST_EQ_P (file, NULL); ++ ++ unlink (override); ++ nih_free (source); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ TEST_FEATURE ("create both conf+override files, watch, then modify/delete override"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create conf */ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "author \"me\"\n"); ++ fprintf (f, "env foo=bar\n"); ++ fprintf (f, "emits hello\n"); ++ fclose (f); ++ ++ /* create override */ ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "author \"you\"\n"); ++ fclose (f); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_STR (job->author, "you"); ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->env)[0], "foo=bar"); ++ TEST_EQ_P (job->export, NULL); ++ ++ /* modify override */ ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "description \"hello world\"\n"); ++ fprintf (f, "author \"ubuntu\"\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf updated */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR (job->author, "ubuntu"); ++ TEST_NE_P (job->description, NULL); ++ TEST_EQ_STR (job->description, "hello world"); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_NE_P (job->start_on, NULL); ++ ++ /* delete override */ ++ unlink (override); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf updated */ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_NE_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->env)[0], "foo=bar"); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_STR (job->author, "me"); ++ TEST_EQ_P (job->description, NULL); ++ ++ nih_free (source); ++ unlink (filename); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ TEST_FEATURE ("create conf, watch, then create invalid override, delete override"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create conf */ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "author \"wibble\"\n"); ++ fprintf (f, "emits hello\n"); ++ fclose (f); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf loaded */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_NE_P (job->start_on, NULL); ++ ++ /* create (partially) invalid override (which should be ++ * fully ignored) ++ */ ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "bleaugh!\n"); ++ fprintf (f, "wha...?\n"); ++ fprintf (f, "author \"moo\"\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ unlink (override); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf still loaded */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_NE_P (job->start_on, NULL); ++ TEST_EQ_STR (job->author, "wibble"); ++ ++ nih_free (source); ++ unlink (filename); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ TEST_FEATURE ("ensure override ignored for CONF_FILE"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create empty conf */ ++ strcpy (filename, dirname); ++ strcat (filename, "/init.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fclose (f); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_FILE); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ ++ /* We expect conf_source_reload to fail in this situation since ++ * although "init.conf" is a supported config file, it is not ++ * allowed to contain any stanzas, implying that it can only ++ * contain comments. In fact, if the file exists but is zero ++ * size that is currently an error since upstart blindly calls ++ * nih_file_read(), which will fail since there are no bytes to ++ * read. ++ */ ++ TEST_NE (ret, 0); ++ err = nih_error_steal (); ++ TEST_EQ (err->number, EILSEQ); ++ nih_free (err); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf NOT loaded */ ++ TEST_HASH_EMPTY (source->files); ++ ++ /* create override */ ++ strcpy (override, dirname); ++ strcat (override, "/init.override"); ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fclose (f); ++ ++ TEST_FORCE_WATCH_UPDATE(); ++ ++ /* ensure conf still NOT loaded */ ++ TEST_HASH_EMPTY (source->files); ++ ++ nih_free (source); ++ unlink (filename); ++ unlink (override); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ /* Consume all available inotify instances so that the following ++ * tests run without inotify. ++ */ ++ for (i = 0; i < 4096; i++) ++ if ((fd[i] = inotify_init ()) < 0) ++ break; ++ ++no_inotify: ++ /* If you don't have inotify, any override file must exist ++ * before the system boots. ++ */ ++ ++ TEST_FEATURE ("both conf+override files with no inotify support"); ++ TEST_ENSURE_CLEAN_ENV (); ++ TEST_FILENAME (dirname); ++ TEST_EQ (mkdir (dirname, 0755), 0); ++ ++ /* create conf */ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ f = fopen (filename, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "start on started\n"); ++ fprintf (f, "author \"me\"\n"); ++ fprintf (f, "env foo=bar\n"); ++ fprintf (f, "emits hello\n"); ++ fclose (f); ++ ++ /* create override */ ++ strcpy (override, dirname); ++ strcat (override, "/foo.override"); ++ f = fopen (override, "w"); ++ TEST_NE_P (f, NULL); ++ fprintf (f, "manual\n"); ++ fprintf (f, "author \"you\"\n"); ++ fclose (f); ++ ++ /* create watch */ ++ source = conf_source_new (NULL, dirname, CONF_JOB_DIR); ++ TEST_NE_P (source, NULL); ++ ret = conf_source_reload (source); ++ TEST_EQ (ret, 0); ++ ++ /* ensure conf loaded */ ++ TEST_HASH_NOT_EMPTY (source->files); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_NE_P (file, NULL); ++ job = (JobClass *)nih_hash_lookup (job_classes, "foo"); ++ TEST_NE_P (job, NULL); ++ TEST_EQ_P (file->job, job); ++ TEST_EQ_STR ((job->emits)[0], "hello"); ++ TEST_EQ_STR (job->author, "you"); ++ TEST_EQ_P (job->start_on, NULL); ++ TEST_EQ_STR ((job->env)[0], "foo=bar"); ++ TEST_EQ_P (job->export, NULL); ++ ++ nih_free (source); ++ unlink (filename); ++ unlink (override); ++ TEST_EQ (rmdir (dirname), 0); ++ ++ ++ nih_log_set_priority (NIH_LOG_MESSAGE); ++ ++ /* Release consumed instances */ ++ for (i = 0; i < 4096; i++) { ++ if (fd[i] < 0) ++ break; ++ ++ close (fd[i]); ++ } ++} ++ ++void ++test_source_reload_file (void) ++{ ++ ConfSource *source; ++ ConfFile *file, *old_file; ++ FILE *f; ++ int ret, fd[4096], i = 0, nfds; ++ char dirname[PATH_MAX]; ++ char tmpname[PATH_MAX], filename[PATH_MAX]; ++ fd_set readfds, writefds, exceptfds; ++ NihError *err; ++ ++ TEST_FUNCTION_FEATURE ("conf_source_reload", ++ "with configuration file"); ++ program_name = "test"; ++ nih_log_set_priority (NIH_LOG_FATAL); ++ ++ TEST_FILENAME (dirname); ++ mkdir (dirname, 0755); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ ++ f = fopen (filename, "w"); ++ fprintf (f, "#nothing to test\n"); ++ fclose (f); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/bar.conf"); ++ ++ f = fopen (filename, "w"); ++ fprintf (f, "#nothing to test\n"); ++ fclose (f); ++ ++ /* Make sure that we have inotify before performing some tests... */ ++ if ((fd[0] = inotify_init ()) < 0) { ++ printf ("SKIP: inotify not available\n"); ++ goto no_inotify; ++ } ++ close (fd[0]); ++ ++ ++ /* Check that we can load a file source for the first time. An ++ * inotify watch should be established on the parent directory, ++ * the descriptor set to be closed-on-exec, but only that single ++ * file parsed. ++ */ ++ TEST_FEATURE ("with new conf file"); ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ ++ source = conf_source_new (NULL, filename, CONF_FILE); ++ ret = conf_source_reload (source); ++ ++ TEST_EQ (ret, 0); ++ TEST_ALLOC_SIZE (source->watch, sizeof (NihWatch)); ++ TEST_EQ_STR (source->watch->path, dirname); ++ TEST_EQ_P (source->watch->data, source); ++ ++ TEST_TRUE (fcntl (source->watch->fd, F_GETFD) & FD_CLOEXEC); ++ ++ TEST_EQ (source->flag, TRUE); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ ++ TEST_ALLOC_SIZE (file, sizeof (ConfFile)); ++ TEST_ALLOC_PARENT (file, source); ++ TEST_EQ (file->flag, source->flag); ++ TEST_EQ_P (file->data, NULL); ++ ++ nih_free (file); ++ ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/bar.conf"); ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ ++ TEST_EQ_P (file, NULL); ++ ++ TEST_HASH_EMPTY (source->files); ++ ++ nih_free (source); ++ ++ ++ /* Check that if we create a new file in the directory, alongside ++ * the one we are watching, it is ignored. ++ */ ++ TEST_FEATURE ("with new file alongside conf file"); ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ ++ source = conf_source_new (NULL, filename, CONF_FILE); ++ ret = conf_source_reload (source); ++ ++ TEST_EQ (ret, 0); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/baz.conf"); ++ ++ f = fopen (filename, "w"); ++ fprintf (f, "#nothing to test\n"); ++ fclose (f); ++ ++ nfds = 0; ++ FD_ZERO (&readfds); ++ FD_ZERO (&writefds); ++ FD_ZERO (&exceptfds); ++ ++ nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); ++ nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ ++ TEST_EQ_P (file, NULL); ++ ++ ++ /* Check that if we modify a file in the directory, alongside ++ * the one we are watching, it is ignored. ++ */ ++ TEST_FEATURE ("with modification to file alongside conf file"); ++ strcpy (filename, dirname); ++ strcat (filename, "/bar.conf"); ++ ++ f = fopen (filename, "w"); ++ fprintf (f, "#nothing to test\n"); ++ fclose (f); ++ ++ nfds = 0; ++ FD_ZERO (&readfds); ++ FD_ZERO (&writefds); ++ FD_ZERO (&exceptfds); ++ ++ nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); ++ nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ ++ TEST_EQ_P (file, NULL); ++ ++ ++ /* Check that the configuration file we're watching can be modified ++ * using the direct writing technique. ++ */ ++ TEST_FEATURE ("with modification (direct write)"); ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ ++ old_file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ TEST_FREE_TAG (old_file); ++ ++ f = fopen (filename, "w"); ++ fprintf (f, "#still nothing to test\n"); ++ fclose (f); ++ ++ nfds = 0; ++ FD_ZERO (&readfds); ++ FD_ZERO (&writefds); ++ FD_ZERO (&exceptfds); ++ ++ nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); ++ nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ ++ TEST_FREE (old_file); ++ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ ++ TEST_ALLOC_SIZE (file, sizeof (ConfFile)); ++ TEST_ALLOC_PARENT (file, source); ++ TEST_EQ (file->flag, source->flag); ++ TEST_EQ_P (file->data, NULL); ++ ++ old_file = file; ++ ++ ++ /* Check that the configuration file we're watching can be modified ++ * using the write and then rename technique; it should be parsed ++ * and the previous file marked for deletion. ++ */ ++ TEST_FEATURE ("with modification (atomic rename)"); ++ strcpy (tmpname, dirname); ++ strcat (tmpname, "/.foo.tmp"); ++ ++ f = fopen (tmpname, "w"); ++ fprintf (f, "#nothing to test\n"); ++ fclose (f); ++ ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ ++ rename (tmpname, filename); ++ ++ TEST_FREE_TAG (old_file); ++ ++ nfds = 0; ++ FD_ZERO (&readfds); ++ FD_ZERO (&writefds); ++ FD_ZERO (&exceptfds); ++ ++ nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); ++ nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ ++ TEST_FREE (old_file); ++ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ ++ TEST_ALLOC_SIZE (file, sizeof (ConfFile)); ++ TEST_ALLOC_PARENT (file, source); ++ TEST_EQ (file->flag, source->flag); ++ TEST_EQ_P (file->data, NULL); ++ ++ old_file = file; ++ ++ ++ /* Check that we can delete the configuration file that we're ++ * watching, the metadata for it should be lost. ++ */ ++ TEST_FEATURE ("with deletion"); ++ strcpy (filename, dirname); ++ strcat (filename, "/foo.conf"); ++ ++ unlink (filename); ++ ++ TEST_FREE_TAG (old_file); ++ ++ nfds = 0; ++ FD_ZERO (&readfds); ++ FD_ZERO (&writefds); ++ FD_ZERO (&exceptfds); ++ ++ nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); ++ nih_io_handle_fds (&readfds, &writefds, &exceptfds); ++ ++ TEST_FREE (old_file); ++ ++ file = (ConfFile *)nih_hash_lookup (source->files, filename); ++ ++ TEST_EQ_P (file, NULL); ++ ++ ++ /* Check that the watch allows us to see if the file we want is ++ * created using the direct writing technique, and thus parsed. + */ + TEST_FEATURE ("with creation (direct write)"); + strcpy (filename, dirname); +@@ -2969,8 +4262,6 @@ test_source_reload_file (void) + TEST_HASH_EMPTY (source->files); + + nih_free (source); +- +- + /* Consume all available inotify instances so that the following + * tests run without inotify. + */ +@@ -3456,6 +4747,8 @@ main (int argc, + test_source_reload_conf_dir (); + test_source_reload_file (); + test_source_reload (); ++ test_toggle_conf_name (); ++ test_override (); + test_file_destroy (); + test_select_job (); + +diff --git a/init/tests/test_parse_job.c b/init/tests/test_parse_job.c +index 73218f4..07a99c1 100644 +--- a/init/tests/test_parse_job.c ++++ b/init/tests/test_parse_job.c +@@ -64,7 +64,7 @@ test_parse_job (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -110,7 +110,7 @@ test_parse_job (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -131,6 +131,45 @@ test_parse_job (void) + + nih_free (job); + } ++ ++ TEST_FEATURE ("with non-NULL update parameter (override)"); ++ { ++ JobClass *tmp = NULL; ++ ++ strcpy (buf, "start on starting\n"); ++ strcat (buf, "author \"me\"\n"); ++ ++ pos = 0; ++ lineno = 1; ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), ++ &pos, &lineno); ++ ++ TEST_NE_P (job, NULL); ++ TEST_EQ_STR (job->author, "me"); ++ TEST_NE_P (job->start_on, NULL); ++ ++ strcat (buf, "author \"you\"\n"); ++ strcat (buf, "manual\n"); ++ strcat (buf, "description \"my description\"\n"); ++ ++ pos = 0; ++ lineno = 1; ++ tmp = parse_job (NULL, job, "test", buf, strlen (buf), ++ &pos, &lineno); ++ TEST_NE_P (tmp, NULL); ++ ++ /* if passed a job, the same object should be returned. ++ */ ++ TEST_EQ_P (tmp, job); ++ ++ TEST_EQ_STR (tmp->author, "you"); ++ TEST_EQ_P (tmp->start_on, NULL); ++ TEST_NE_P (tmp->description, NULL); ++ ++ TEST_EQ_STR (tmp->description, "my description"); ++ ++ nih_free (job); ++ } + } + + void +@@ -153,7 +192,7 @@ test_stanza_exec (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -190,7 +229,7 @@ test_stanza_exec (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -229,7 +268,7 @@ test_stanza_exec (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -266,7 +305,7 @@ test_stanza_exec (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -299,7 +338,7 @@ test_stanza_script (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -340,7 +379,7 @@ test_stanza_script (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -379,7 +418,7 @@ test_stanza_script (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -416,7 +455,7 @@ test_stanza_script (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -447,7 +486,7 @@ test_stanza_pre_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -484,7 +523,7 @@ test_stanza_pre_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -524,7 +563,7 @@ test_stanza_pre_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -565,7 +604,7 @@ test_stanza_pre_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -604,7 +643,7 @@ test_stanza_pre_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -643,7 +682,7 @@ test_stanza_pre_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -680,7 +719,7 @@ test_stanza_pre_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -699,7 +738,7 @@ test_stanza_pre_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -718,7 +757,7 @@ test_stanza_pre_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -737,7 +776,7 @@ test_stanza_pre_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -768,7 +807,7 @@ test_stanza_post_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -805,7 +844,7 @@ test_stanza_post_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -845,7 +884,7 @@ test_stanza_post_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -886,7 +925,7 @@ test_stanza_post_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -925,7 +964,7 @@ test_stanza_post_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -964,7 +1003,7 @@ test_stanza_post_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1001,7 +1040,7 @@ test_stanza_post_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1020,7 +1059,7 @@ test_stanza_post_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1039,7 +1078,7 @@ test_stanza_post_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1058,7 +1097,7 @@ test_stanza_post_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1089,7 +1128,7 @@ test_stanza_pre_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1126,7 +1165,7 @@ test_stanza_pre_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1166,7 +1205,7 @@ test_stanza_pre_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1207,7 +1246,7 @@ test_stanza_pre_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1246,7 +1285,7 @@ test_stanza_pre_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1285,7 +1324,7 @@ test_stanza_pre_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1322,7 +1361,7 @@ test_stanza_pre_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1341,7 +1380,7 @@ test_stanza_pre_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1360,7 +1399,7 @@ test_stanza_pre_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1379,7 +1418,7 @@ test_stanza_pre_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1410,7 +1449,7 @@ test_stanza_post_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1447,7 +1486,7 @@ test_stanza_post_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1487,7 +1526,7 @@ test_stanza_post_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1528,7 +1567,7 @@ test_stanza_post_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1567,7 +1606,7 @@ test_stanza_post_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1606,7 +1645,7 @@ test_stanza_post_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1643,7 +1682,7 @@ test_stanza_post_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1662,7 +1701,7 @@ test_stanza_post_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1681,7 +1720,7 @@ test_stanza_post_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1700,7 +1739,7 @@ test_stanza_post_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -1731,7 +1770,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1776,7 +1815,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1828,7 +1867,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1879,7 +1918,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -1942,7 +1981,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2012,7 +2051,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2090,7 +2129,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2170,7 +2209,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2249,7 +2288,7 @@ test_stanza_start (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2291,7 +2330,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2310,7 +2349,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2329,7 +2368,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2348,7 +2387,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2367,7 +2406,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2384,7 +2423,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2403,7 +2442,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2422,7 +2461,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2441,7 +2480,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2460,7 +2499,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2479,7 +2518,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2498,7 +2537,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2517,7 +2556,7 @@ test_stanza_start (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -2551,7 +2590,7 @@ test_stanza_manual (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2586,7 +2625,7 @@ test_stanza_manual (void) + TEST_FEATURE ("manual stanza after start on"); + strcpy (buf, "start on wibble\nmanual\n"); + +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + TEST_NE_P (job, NULL); +@@ -2616,7 +2655,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2661,7 +2700,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2713,7 +2752,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2764,7 +2803,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2827,7 +2866,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2897,7 +2936,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -2975,7 +3014,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3055,7 +3094,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3134,7 +3173,7 @@ test_stanza_stop (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3176,7 +3215,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3195,7 +3234,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3214,7 +3253,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3233,7 +3272,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3252,7 +3291,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3269,7 +3308,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3288,7 +3327,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3307,7 +3346,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3326,7 +3365,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3345,7 +3384,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3364,7 +3403,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3383,7 +3422,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3402,7 +3441,7 @@ test_stanza_stop (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3432,7 +3471,7 @@ test_stanza_description (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3465,7 +3504,7 @@ test_stanza_description (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3498,7 +3537,7 @@ test_stanza_description (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3517,7 +3556,7 @@ test_stanza_description (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3547,7 +3586,7 @@ test_stanza_author (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3580,7 +3619,7 @@ test_stanza_author (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3613,7 +3652,7 @@ test_stanza_author (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3632,7 +3671,7 @@ test_stanza_author (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3662,7 +3701,7 @@ test_stanza_version (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3695,7 +3734,7 @@ test_stanza_version (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3728,7 +3767,7 @@ test_stanza_version (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3747,7 +3786,7 @@ test_stanza_version (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3777,7 +3816,7 @@ test_stanza_emits (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3814,7 +3853,7 @@ test_stanza_emits (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3857,7 +3896,7 @@ test_stanza_emits (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3899,7 +3938,7 @@ test_stanza_emits (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -3929,7 +3968,7 @@ test_stanza_expect (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3962,7 +4001,7 @@ test_stanza_expect (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -3995,7 +4034,7 @@ test_stanza_expect (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4028,7 +4067,7 @@ test_stanza_expect (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4061,7 +4100,7 @@ test_stanza_expect (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4093,7 +4132,7 @@ test_stanza_expect (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4112,7 +4151,7 @@ test_stanza_expect (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4131,7 +4170,7 @@ test_stanza_expect (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4159,7 +4198,7 @@ test_stanza_respawn (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4193,7 +4232,7 @@ test_stanza_respawn (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4226,7 +4265,7 @@ test_stanza_respawn (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4260,7 +4299,7 @@ test_stanza_respawn (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4293,7 +4332,7 @@ test_stanza_respawn (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4326,7 +4365,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4345,7 +4384,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4364,7 +4403,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4383,7 +4422,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4402,7 +4441,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4421,7 +4460,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4440,7 +4479,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4459,7 +4498,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4478,7 +4517,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4497,7 +4536,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4516,7 +4555,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4535,7 +4574,7 @@ test_stanza_respawn (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4565,7 +4604,7 @@ test_stanza_task (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4597,7 +4636,7 @@ test_stanza_task (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4629,7 +4668,7 @@ test_stanza_task (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4659,7 +4698,7 @@ test_stanza_instance (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4693,7 +4732,7 @@ test_stanza_instance (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4726,7 +4765,7 @@ test_stanza_instance (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4745,7 +4784,7 @@ test_stanza_instance (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4775,7 +4814,7 @@ test_stanza_kill (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4808,7 +4847,7 @@ test_stanza_kill (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -4840,7 +4879,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4859,7 +4898,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4878,7 +4917,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4897,7 +4936,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4916,7 +4955,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4935,7 +4974,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4954,7 +4993,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -4974,7 +5013,7 @@ test_stanza_kill (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5005,7 +5044,7 @@ test_stanza_normal (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5044,7 +5083,7 @@ test_stanza_normal (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5083,7 +5122,7 @@ test_stanza_normal (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5127,7 +5166,7 @@ test_stanza_normal (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5168,7 +5207,7 @@ test_stanza_normal (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5187,7 +5226,7 @@ test_stanza_normal (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5206,7 +5245,7 @@ test_stanza_normal (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5225,7 +5264,7 @@ test_stanza_normal (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5244,7 +5283,7 @@ test_stanza_normal (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5263,7 +5302,7 @@ test_stanza_normal (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5293,7 +5332,7 @@ test_stanza_console (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5326,7 +5365,7 @@ test_stanza_console (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5359,7 +5398,7 @@ test_stanza_console (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5392,7 +5431,7 @@ test_stanza_console (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5423,7 +5462,7 @@ test_stanza_console (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5442,7 +5481,7 @@ test_stanza_console (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5460,7 +5499,7 @@ test_stanza_console (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5490,7 +5529,7 @@ test_stanza_env (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5528,7 +5567,7 @@ test_stanza_env (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5565,7 +5604,7 @@ test_stanza_env (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5584,7 +5623,7 @@ test_stanza_env (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5614,7 +5653,7 @@ test_stanza_export (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5651,7 +5690,7 @@ test_stanza_export (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5694,7 +5733,7 @@ test_stanza_export (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5736,7 +5775,7 @@ test_stanza_export (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5766,7 +5805,7 @@ test_stanza_umask (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5799,7 +5838,7 @@ test_stanza_umask (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5831,7 +5870,7 @@ test_stanza_umask (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5850,7 +5889,7 @@ test_stanza_umask (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5869,7 +5908,7 @@ test_stanza_umask (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5888,7 +5927,7 @@ test_stanza_umask (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5907,7 +5946,7 @@ test_stanza_umask (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5927,7 +5966,7 @@ test_stanza_umask (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -5957,7 +5996,7 @@ test_stanza_nice (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -5990,7 +6029,7 @@ test_stanza_nice (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6023,7 +6062,7 @@ test_stanza_nice (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6055,7 +6094,7 @@ test_stanza_nice (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6074,7 +6113,7 @@ test_stanza_nice (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6093,7 +6132,7 @@ test_stanza_nice (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6112,7 +6151,7 @@ test_stanza_nice (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6131,7 +6170,7 @@ test_stanza_nice (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6150,7 +6189,7 @@ test_stanza_nice (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6180,7 +6219,7 @@ test_stanza_oom (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6213,7 +6252,7 @@ test_stanza_oom (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6246,7 +6285,7 @@ test_stanza_oom (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6279,7 +6318,7 @@ test_stanza_oom (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6311,7 +6350,7 @@ test_stanza_oom (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6330,7 +6369,7 @@ test_stanza_oom (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6349,7 +6388,7 @@ test_stanza_oom (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6368,7 +6407,7 @@ test_stanza_oom (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6387,7 +6426,7 @@ test_stanza_oom (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6406,7 +6445,7 @@ test_stanza_oom (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -6435,7 +6474,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6469,7 +6508,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6503,7 +6542,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6537,7 +6576,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6571,7 +6610,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6606,7 +6645,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6641,7 +6680,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6675,7 +6714,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6710,7 +6749,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6744,7 +6783,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6778,7 +6817,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6812,7 +6851,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6847,7 +6886,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6881,7 +6920,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6917,7 +6956,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6956,7 +6995,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -6991,7 +7030,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -7026,7 +7065,7 @@ test_stanza_limit (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -7060,7 +7099,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7079,7 +7118,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7098,7 +7137,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7117,7 +7156,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7136,7 +7175,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7155,7 +7194,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7174,7 +7213,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7193,7 +7232,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7212,7 +7251,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7231,7 +7270,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7250,7 +7289,7 @@ test_stanza_limit (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7280,7 +7319,7 @@ test_stanza_chroot (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -7314,7 +7353,7 @@ test_stanza_chroot (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -7347,7 +7386,7 @@ test_stanza_chroot (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7366,7 +7405,7 @@ test_stanza_chroot (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7396,7 +7435,7 @@ test_stanza_chdir (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -7430,7 +7469,7 @@ test_stanza_chdir (void) + TEST_ALLOC_FAIL { + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), + &pos, &lineno); + + if (test_alloc_failed) { +@@ -7463,7 +7502,7 @@ test_stanza_chdir (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + +@@ -7482,7 +7521,7 @@ test_stanza_chdir (void) + + pos = 0; + lineno = 1; +- job = parse_job (NULL, "test", buf, strlen (buf), &pos, &lineno); ++ job = parse_job (NULL, NULL, "test", buf, strlen (buf), &pos, &lineno); + + TEST_EQ_P (job, NULL); + diff --git a/upstart-reexec.patch b/upstart-reexec.patch new file mode 100644 index 0000000..324c3a2 --- /dev/null +++ b/upstart-reexec.patch @@ -0,0 +1,65 @@ +diff -up upstart-0.6.5/init/main.c.foo upstart-0.6.5/init/main.c +--- upstart-0.6.5/init/main.c.foo 2010-04-30 19:38:07.846260462 -0400 ++++ upstart-0.6.5/init/main.c 2010-04-30 21:35:38.690487114 -0400 +@@ -67,6 +67,7 @@ static void kbd_handler (void *data, + static void pwr_handler (void *data, NihSignal *signal); + static void hup_handler (void *data, NihSignal *signal); + static void usr1_handler (void *data, NihSignal *signal); ++static void term_handler (void *data, NihSignal *signal); + #endif /* DEBUG */ + + +@@ -241,6 +242,10 @@ main (int argc, + /* SIGUSR1 instructs us to reconnect to D-Bus */ + nih_signal_set_handler (SIGUSR1, nih_signal_handler); + NIH_MUST (nih_signal_add_handler (NULL, SIGUSR1, usr1_handler, NULL)); ++ ++ /* SIGTERM instructs us to re-exec ourselves */ ++ nih_signal_set_handler (SIGTERM, nih_signal_handler); ++ NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, term_handler, NULL)); + #endif /* DEBUG */ + + +@@ -485,4 +490,42 @@ usr1_handler (void *data, + } + } + } ++ ++/** ++ * term_handler: ++ * @data: unused, ++ * @signal: signal that called this handler ++ * ++ * This is called when we recieve the TERM signal, which instructs us ++ * to reexec ourselves. ++ **/ ++static void ++term_handler (void *data, ++ NihSignal *signal) ++{ ++ NihError *err; ++ sigset_t mask, oldmask; ++ ++ nih_assert (argv0 != NULL); ++ nih_assert (signal != NULL); ++ ++ nih_warn (_("Re-executing %s"), argv0); ++ ++ /* Block signals while we work. We're the last signal handler ++ * installed so this should mean that they're all handled now. ++ * ++ * The child must make sure that it unblocks these again when ++ * it's ready. ++ */ ++ sigfillset (&mask); ++ sigprocmask (SIG_BLOCK, &mask, &oldmask); ++ execl (argv0, argv0, "--restart", NULL); ++ nih_error_raise_system (); ++ ++ err = nih_error_get (); ++ nih_error (_("Failed to re-execute %s: %s"), argv0, err->message); ++ nih_free (err); ++ ++ sigprocmask (SIG_SETMASK, &oldmask, NULL); ++} + #endif /* DEBUG */ diff --git a/upstart-remove-tests.patch b/upstart-remove-tests.patch new file mode 100644 index 0000000..720294a --- /dev/null +++ b/upstart-remove-tests.patch @@ -0,0 +1,213 @@ +diff -up upstart-0.6.3/nih-dbus/tests/test_dbus_connection.c.tests upstart-0.6.3/nih-dbus/tests/test_dbus_connection.c +--- upstart-0.6.3/nih-dbus/tests/test_dbus_connection.c.tests 2009-12-14 15:51:25.323430105 +0100 ++++ upstart-0.6.3/nih-dbus/tests/test_dbus_connection.c 2009-12-14 15:51:25.323430105 +0100 +@@ -1521,7 +1521,6 @@ main (int argc, + nih_error_init (); + + test_connect (); +- test_bus (); + test_setup (); + test_server (); + +diff -up upstart-0.6.3/nih/tests/test_child.c.tests upstart-0.6.3/nih/tests/test_child.c +--- upstart-0.6.3/nih/tests/test_child.c.tests 2009-12-14 15:51:25.910710749 +0100 ++++ upstart-0.6.3/nih/tests/test_child.c 2009-12-14 15:51:25.888145178 +0100 +@@ -328,186 +328,6 @@ test_poll (void) + nih_free (watch); + + +- /* Check that a signal raised from a traced child causes the reaper +- * to be called with a traced event and the event in the status +- * field. It should not be removed from the list since the child +- * hasn't gone away. +- */ +- TEST_FEATURE ("with signal from traced child"); +- +- TEST_CHILD (pid) { +- assert0 (ptrace (PTRACE_TRACEME, 0, NULL, NULL)); +- +- raise (SIGSTOP); +- raise (SIGCHLD); +- pause (); +- exit (0); +- } +- +- waitid (P_PID, pid, &siginfo, WSTOPPED); +- +- assert0 (ptrace (PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESYSGOOD)); +- assert0 (ptrace (PTRACE_CONT, pid, NULL, SIGCONT)); +- +- waitid (P_PID, pid, &siginfo, WSTOPPED | WNOWAIT); +- +- watch = nih_child_add_watch (NULL, pid, NIH_CHILD_TRAPPED, +- my_handler, &watch); +- +- TEST_FREE_TAG (watch); +- +- handler_called = 0; +- last_data = NULL; +- last_pid = 0; +- last_event = -1; +- last_status = 0; +- +- nih_child_poll (); +- +- TEST_TRUE (handler_called); +- TEST_EQ (last_pid, pid); +- TEST_EQ (last_event, NIH_CHILD_TRAPPED); +- TEST_EQ (last_status, SIGCHLD); +- TEST_NOT_FREE (watch); +- +- assert0 (ptrace (PTRACE_DETACH, pid, NULL, 0)); +- +- kill (pid, SIGTERM); +- waitid (P_PID, pid, &siginfo, WEXITED); +- nih_free (watch); +- +- +-#if HAVE_VALGRIND_VALGRIND_H +- /* These tests fail when running under valgrind. +- */ +- if (! RUNNING_ON_VALGRIND) { +-#endif +- /* Check that when a traced child forks it causes the reaper +- * to be called with a ptrace event and the fork event in the +- * status field. It should not be removed from the list since the +- * child hasn't gone away. +- */ +- TEST_FEATURE ("with fork by traced child"); +- +- TEST_CHILD (pid) { +- assert0 (ptrace (PTRACE_TRACEME, 0, NULL, NULL)); +- +- raise (SIGSTOP); +- +- child = fork (); +- assert (child >= 0); +- +- pause (); +- +- exit (0); +- } +- +- waitid (P_PID, pid, &siginfo, WSTOPPED); +- +- assert0 (ptrace (PTRACE_SETOPTIONS, pid, NULL, +- PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK)); +- assert0 (ptrace (PTRACE_CONT, pid, NULL, SIGCONT)); +- +- /* Wait for ptrace to stop the parent (signalling the fork) */ +- waitid (P_PID, pid, &siginfo, WSTOPPED | WNOWAIT); +- +- /* Will be able to get the child pid now, we have to do it here +- * because we want to wait on it to ensure the test is synchronous; +- * otherwise nih_child_poll() could actually eat the child event +- * before it returns -- normally we'd do this inside the handler, +- * so things would probably work (we iter the handlers for each +- * event, so you can add one). +- */ +- data = 0; +- assert0 (ptrace (PTRACE_GETEVENTMSG, pid, NULL, &data)); +- assert (data != 0); +- child = (pid_t)data; +- +- /* Wait for ptrace to stop the child, otherwise it might not be +- * ready for us to actually detach from. +- */ +- waitid (P_PID, child, &siginfo, WSTOPPED | WNOWAIT); +- +- watch = nih_child_add_watch (NULL, pid, NIH_CHILD_PTRACE, +- my_handler, &watch); +- +- TEST_FREE_TAG (watch); +- +- handler_called = 0; +- last_data = NULL; +- last_pid = 0; +- last_event = -1; +- last_status = 0; +- +- nih_child_poll (); +- +- TEST_TRUE (handler_called); +- TEST_EQ (last_pid, pid); +- TEST_EQ (last_event, NIH_CHILD_PTRACE); +- TEST_EQ (last_status, PTRACE_EVENT_FORK); +- TEST_NOT_FREE (watch); +- +- assert0 (ptrace (PTRACE_DETACH, child, NULL, SIGCONT)); +- kill (child, SIGTERM); +- +- assert0 (ptrace (PTRACE_DETACH, pid, NULL, SIGCONT)); +- kill (pid, SIGTERM); +- waitid (P_PID, pid, &siginfo, WEXITED); +- nih_free (watch); +- +- +- /* Check that when a traced child execs it causes the reaper +- * to be called with a ptrace event and the exec event in the +- * status field. It should not be removed from the list since the +- * child hasn't gone away. +- */ +- TEST_FEATURE ("with exec by traced child"); +- +- TEST_CHILD (pid) { +- assert0 (ptrace (PTRACE_TRACEME, 0, NULL, NULL)); +- +- raise (SIGSTOP); +- +- execl ("/bin/true", "true", NULL); +- exit (255); +- } +- +- waitid (P_PID, pid, &siginfo, WSTOPPED); +- +- assert0 (ptrace (PTRACE_SETOPTIONS, pid, NULL, +- PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC)); +- assert0 (ptrace (PTRACE_CONT, pid, NULL, SIGCONT)); +- +- waitid (P_PID, pid, &siginfo, WSTOPPED | WNOWAIT); +- +- watch = nih_child_add_watch (NULL, pid, NIH_CHILD_PTRACE, +- my_handler, &watch); +- +- TEST_FREE_TAG (watch); +- +- handler_called = 0; +- last_data = NULL; +- last_pid = 0; +- last_event = -1; +- last_status = 0; +- +- nih_child_poll (); +- +- TEST_TRUE (handler_called); +- TEST_EQ (last_pid, pid); +- TEST_EQ (last_event, NIH_CHILD_PTRACE); +- TEST_EQ (last_status, PTRACE_EVENT_EXEC); +- TEST_NOT_FREE (watch); +- +- assert0 (ptrace (PTRACE_DETACH, pid, NULL, SIGCONT)); +- +- waitid (P_PID, pid, &siginfo, WEXITED); +- nih_free (watch); +-#if HAVE_VALGRIND_VALGRIND_H +- } +-#endif +- +- + /* Check that we can watch for events from any process, which + * shouldn't be freed when the child dies. + */ +diff -up upstart-0.6.3/nih/tests/test_string.c.tests upstart-0.6.3/nih/tests/test_string.c +--- upstart-0.6.3/nih/tests/test_string.c.tests 2009-12-17 12:34:43.357844975 +0100 ++++ upstart-0.6.3/nih/tests/test_string.c 2009-12-17 12:38:41.911484010 +0100 +@@ -1511,8 +1511,6 @@ main (int argc, + test_array_copy (); + test_array_append (); + test_str_wrap (); +- test_str_screen_width (); +- test_str_screen_wrap (); + + return 0; + } diff --git a/upstart-shutdown-exitcode.patch b/upstart-shutdown-exitcode.patch new file mode 100644 index 0000000..0b70308 --- /dev/null +++ b/upstart-shutdown-exitcode.patch @@ -0,0 +1,28 @@ +diff --git a/util/shutdown.c b/util/shutdown.c +index 0a6d8bf..2ef847a 100644 +--- a/util/shutdown.c ++++ b/util/shutdown.c +@@ -459,6 +459,7 @@ shutdown_now (void) + { + nih_local char **extra_env = NULL; + NihDBusError * dbus_err; ++ int exit_val = 0; + + if (init_halt) { + char *e; +@@ -486,12 +487,14 @@ shutdown_now (void) + * yet rebooted ... so try /dev/initctl + */ + sysvinit_shutdown (); ++ nih_fatal ("Unable to shutdown system"); ++ exit_val = 1; + } + + unlink (ETC_NOLOGIN); + nih_main_unlink_pidfile (); + +- exit (0); ++ exit (exit_val); + } + + /** diff --git a/upstart-telinit.patch b/upstart-telinit.patch new file mode 100644 index 0000000..3cc2b1e --- /dev/null +++ b/upstart-telinit.patch @@ -0,0 +1,13 @@ +diff -up upstart-0.6.3/util/telinit.c.foo upstart-0.6.3/util/telinit.c +--- upstart-0.6.3/util/telinit.c.foo 2009-12-03 14:52:58.000000000 -0500 ++++ upstart-0.6.3/util/telinit.c 2009-12-03 14:53:06.000000000 -0500 +@@ -174,9 +174,6 @@ main (int argc, + break; + case 'U': + case 'u': +- ret = kill (1, SIGTERM); +- if (ret < 0) +- nih_error_raise_system (); + break; + default: + nih_assert_not_reached (); diff --git a/upstart-utmp.patch b/upstart-utmp.patch new file mode 100644 index 0000000..ccda548 --- /dev/null +++ b/upstart-utmp.patch @@ -0,0 +1,80 @@ +diff --git a/init/job_class.c b/init/job_class.c +index eeec83d..00eb13d 100644 +--- a/init/job_class.c ++++ b/init/job_class.c +@@ -219,6 +219,8 @@ job_class_new (const void *parent, + class->chroot = NULL; + class->chdir = NULL; + ++ class->utmp_id = NULL; ++ + class->deleted = FALSE; + class->debug = FALSE; + +diff --git a/init/job_class.h b/init/job_class.h +index d40e750..e5b5190 100644 +--- a/init/job_class.h ++++ b/init/job_class.h +@@ -145,6 +145,7 @@ typedef struct job_class { + struct rlimit *limits[RLIMIT_NLIMITS]; + char *chroot; + char *chdir; ++ char *utmp_id; + + int deleted; + int debug; +diff --git a/init/job_process.c b/init/job_process.c +index 70fa2bf..544f5e3 100644 +--- a/init/job_process.c ++++ b/init/job_process.c +@@ -361,11 +361,13 @@ job_process_spawn (JobClass *class, + char * const *env, + int trace) + { +- sigset_t child_set, orig_set; +- pid_t pid; +- int i, fds[2]; +- char filename[PATH_MAX]; +- FILE *fd; ++ sigset_t child_set, orig_set; ++ pid_t pid; ++ int i, fds[2]; ++ char filename[PATH_MAX]; ++ FILE *fd; ++ struct utmpx utmp; ++ struct timeval tv; + + nih_assert (class != NULL); + +@@ -558,6 +560,20 @@ job_process_spawn (JobClass *class, + } + } + ++ /* Write utmp INIT_PROCESS entry */ ++ if (class->utmp_id) { ++ memset(&utmp, 0, sizeof(struct utmpx)); ++ strncpy(utmp.ut_id, class->utmp_id, sizeof(utmp.ut_id)); ++ utmp.ut_pid = getpid(); ++ utmp.ut_type = INIT_PROCESS; ++ gettimeofday(&tv, NULL); ++ utmp.ut_tv.tv_sec = tv.tv_sec; ++ utmp.ut_tv.tv_usec = tv.tv_usec; ++ setutxent(); ++ pututxline(&utmp); ++ endutxent(); ++ } ++ + /* Execute the process, if we escape from here it failed */ + if (execvp (argv[0], argv) < 0) { + nih_error_raise_system (); +@@ -1176,6 +1192,10 @@ job_process_terminated (Job *job, + memset(utmptr->ut_user, 0, UT_NAMESIZE); + memset(utmptr->ut_host, 0, UT_HOSTSIZE); + utmptr->ut_time = 0; ++ ++ /* Set class utmp_id for next spawn */ ++ job->class->utmp_id = nih_strdup (job->class, utmptr->ut_id); ++ + /* Update existing utmp file. */ + pututxline(utmptr); + diff --git a/upstart.sh b/upstart.sh new file mode 100644 index 0000000..ebd143b --- /dev/null +++ b/upstart.sh @@ -0,0 +1,4 @@ +# check if upstart is running +if /lib/upstart/initctl list > /dev/null 2> /dev/null; then + export PATH=/lib/upstart:$PATH +fi diff --git a/upstart.spec b/upstart.spec new file mode 100644 index 0000000..8f64556 --- /dev/null +++ b/upstart.spec @@ -0,0 +1,346 @@ +%define initscripts 20110502 + +Name: upstart +Version: 1.2 +Release: 4%{?dist} +Summary: An event-driven init system + +Group: System Environment/Base +License: GPLv2 and LGPLv2+ +URL: https://launchpad.net/upstart +Source0: http://launchpad.net/upstart/1.x/%{version}/+download/upstart-%{version}.tar.gz +Source1: init-system-dbus.conf +# These are taken from http://git.fedorahosted.org/git/?p=initscripts.git +Source2: initjobs-%{initscripts}.tar.gz +# create missing files and directories according to /etc/tmpfiles.d/ +Source3: tmpfiles.conf +# fix selinux context on /run created and mounted by dracut +Source4: fix-selinux-on-run.conf +# add /lib/upstart to shell PATH if upstart is running +Source5: upstart.sh +Source6: init.conf +Patch1: upstart-telinit.patch +Patch2: upstart-audit-events.patch +# set DEAD_PROCESS for dead process with pid in utmp table (#572199, #632568) +Patch3: upstart-utmp.patch +Patch4: upstart-reexec.patch +# allow job override files +Patch7: upstart-override.patch +# add /lib/upstart to default PATH set by init process +Patch10: upstart-lib-upstart.patch +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildRequires: autoconf, automake, gettext, audit-libs-devel, expat-devel +BuildRequires: dbus-devel >= 1:1.2.16, libnih-devel >= 1.0.1 +Conflicts: initscripts < 9.12 +Requires: sysvinit-userspace /etc/rc.d/rc.sysinit + +%description +Upstart is an event-based replacement for the /sbin/init daemon which +handles starting of tasks and services during boot, stopping them +during shutdown and supervising them while the system is running. + +%prep +%setup -q -a 2 +%patch1 -p1 -b .u +%patch2 -p1 -b .audit +%patch3 -p1 -b .utmp +%patch4 -p1 -b .reexec +%patch7 -p1 -b .override +%patch10 -p1 -b .lib-upstart + +%build +autoreconf +%configure --sbindir=/lib/upstart --libdir=/%{_lib} +make %{?_smp_mflags} + +%install +rm -rf %{buildroot} +make install DESTDIR=%{buildroot} + +# Rename common SysV binaries and symlink them to allow parallel +# installation with systemd. We move the utility binaries into a +# subdirectory, so that program_name stays the same and Upstart still +# can check argv[0] properly in various places. +mkdir -p %{buildroot}/sbin/ +ln -sf ../lib/upstart/init %{buildroot}/sbin/upstart +ln -sf ../lib/upstart/initctl %{buildroot}/sbin/initctl +ln -sf ../lib/upstart/start %{buildroot}/sbin/start +ln -sf ../lib/upstart/status %{buildroot}/sbin/status +ln -sf ../lib/upstart/stop %{buildroot}/sbin/stop +ln -sf ../lib/upstart/restart %{buildroot}/sbin/restart +ln -sf ../lib/upstart/reload %{buildroot}/sbin/reload +ln -sf reboot %{buildroot}/lib/upstart/poweroff +ln -sf reboot %{buildroot}/lib/upstart/halt +# Rename init.8.gz to upstart.7.gz (#644249) +mv %{buildroot}/%{_mandir}/man8/init.8 %{buildroot}/%{_mandir}/man7/upstart.7 +# Remove these files due the conflicts with systemd +rm -f %{buildroot}/%{_mandir}/man8/{halt,init,poweroff,reboot,runlevel,shutdown,telinit}.* + +# don't ship default jobs +rm -f %{buildroot}/%{_sysconfdir}/init/* +install -m 644 %{SOURCE1} %{buildroot}/%{_sysconfdir}/init/ +install -m 644 %{SOURCE3} %{buildroot}/%{_sysconfdir}/init/ +install -m 644 %{SOURCE4} %{buildroot}/%{_sysconfdir}/init/ +for file in initjobs-%{initscripts}/*.conf ; do + install -m 644 $file %{buildroot}/%{_sysconfdir}/init/ +done + +mkdir -p -m 755 %{buildroot}/%{_sysconfdir}/profile.d/ +install -m 644 %{SOURCE5} %{buildroot}/%{_sysconfdir}/profile.d/ + +install -m 644 %{SOURCE6} %{buildroot}/%{_sysconfdir}/init.conf + +%find_lang %{name} + +%check +#some tests fail in koji while pass in mock and local build +#to run make check use "--with check" +%if %{?_with_check:1}%{!?_with_check:0} + make check +%endif + +%clean +rm -rf %{buildroot} + +%triggerpostun -- upstart < 0.6.0 +[ -f /proc/1/exe -a -d /proc/1/root ] && kill -TERM 1 + +%files -f %{name}.lang +%defattr(-,root,root,-) +%doc AUTHORS +%doc COPYING +%doc NEWS +%doc README +%doc TODO +%doc HACKING +%{_sysconfdir}/init/ +%config(noreplace) %{_sysconfdir}/init.conf +%config(noreplace) %{_sysconfdir}/dbus-1/system.d/Upstart.conf +/sbin/upstart +/sbin/initctl +/sbin/start +/sbin/status +/sbin/stop +/sbin/restart +/sbin/reload +%dir /lib/upstart +/lib/upstart/init +/lib/upstart/initctl +/lib/upstart/poweroff +/lib/upstart/reboot +/lib/upstart/runlevel +/lib/upstart/shutdown +/lib/upstart/halt +/lib/upstart/start +/lib/upstart/status +/lib/upstart/stop +/lib/upstart/restart +/lib/upstart/telinit +/lib/upstart/reload +/etc/profile.d/upstart.sh +%{_mandir}/man5/init.5.gz +%{_mandir}/man5/inittab.5.gz +%{_mandir}/man7/control-alt-delete.7.gz +%{_mandir}/man7/keyboard-request.7.gz +%{_mandir}/man7/power-status-changed.7.gz +%{_mandir}/man7/runlevel.7.gz +%{_mandir}/man7/started.7.gz +%{_mandir}/man7/starting.7.gz +%{_mandir}/man7/startup.7.gz +%{_mandir}/man7/stopped.7.gz +%{_mandir}/man7/stopping.7.gz +%{_mandir}/man7/upstart.7.gz +%{_mandir}/man8/restart.8.gz +%{_mandir}/man8/initctl.8.gz +%{_mandir}/man8/start.8.gz +%{_mandir}/man8/status.8.gz +%{_mandir}/man8/stop.8.gz +%{_mandir}/man8/reload.8.gz + +%changelog +* Wed May 25 2011 Petr Lautrbach 1.2-4 +- drop Obsoletes: upstart-sysvinit (#707507) +- clean upstart-sysvinit stuff from spec file + +* Fri May 13 2011 Petr Lautrbach 1.2-3 +- add /etc/init.conf file + +* Wed May 04 2011 Petr Lautrbach 1.2-2 +- /run - fix selinux context and check for tmpfiles.d +- physically move binaries to /lib/upstart +- add /lib/upstart to default PATH + +* Wed Mar 23 2011 Petr Lautrbach 1.2-1 +- upgrade to 1.2 +- update upstream URL + +* Mon Mar 21 2011 Petr Lautrbach 1.1-1 +- upgrade to 1.1 - https://launchpad.net/upstart/1.x/1.1 +- allow job override files + +* Thu Mar 03 2011 Petr Lautrbach 1.0-1 +- upgrade to 1.0 - https://launchpad.net/upstart/1.x/1.0 + +* Mon Feb 07 2011 Fedora Release Engineering - 0.6.7-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Fri Dec 17 2010 Petr Lautrbach 0.6.7-1 +- upgrade to 0.6.7 + +* Mon Dec 06 2010 Petr Lautrbach 0.6.6-4 +- Stop tty and serial also on runlevel 's' + +* Fri Nov 26 2010 Petr Lautrbach 0.6.6-3 +- create missing files and directories according to /etc/init/tmpfiles.conf + +* Tue Nov 16 2010 Bill Nottingham 0.6.6-2 +- require /etc/rc.d/rc.sysinit to pull in new initscripts-legacy package where needed + +* Wed Oct 27 2010 Petr Lautrbach 0.6.6-1 +- upgrade to 0.6.6 +- boot jobs source renamed + +* Tue Oct 26 2010 Petr Lautrbach 0.6.5-14 +- send close fd command to shell first (#646894) + +* Tue Oct 19 2010 Petr Lautrbach 0.6.5-13 +- Rename init(8) to upstart(7) (#644249) + +* Thu Oct 14 2010 Petr Lautrbach 0.6.5-12 +- Disable -sysvinit to avoid conflicts with systemd (#635323) + +* Wed Sep 29 2010 jkeating - 0.6.5-11 +- Rebuilt for gcc bug 634757 + +* Fri Sep 24 2010 Bill Nottingham 0.6.5-10 +- merge in basic boot jobs here (#637099) + +* Tue Sep 21 2010 Petr Lautrbach 0.6.5-9 +- set DEAD_PROCESS for dead process with pid in utmp table (#572199, #632568) +- exit shutdown with nonzero exitcode when fails shutdown + +* Fri Sep 17 2010 Bill Nottingham - 0.6.5-8 +- ensure F15 version is newer than F-14 + +* Sat Jul 24 2010 Casey Dahlin - 0.6.5-7 +- Make upgrades work correctly now that -sysvinit is split off + +* Sat Jul 24 2010 Lennart Poettering - 0.6.5-6 +- Split off -sysvinit to make Upstart parallel installable with systemd + +* Tue May 4 2010 Bill Nottingham 0.6.5-5 +- re-add SIGTERM handler so restart on shutdown works, avoiding dirty inodes (#576662) + +* Tue Apr 6 2010 Casey Dahlin 0.6.5-4 +- Reload init after install + +* Wed Feb 24 2010 Petr Lautrbach 0.6.5-3 +- run "make check" only with --with check + +* Fri Feb 19 2010 Casey Dahlin 0.6.5-2 +- be more specific about which libnih we need. + +* Wed Feb 17 2010 Petr Lautrbach 0.6.5-1 +- upgrade to 0.6.5 + +* Fri Jan 29 2010 Petr Lautrbach 0.6.3-7 +- add SIGUSR1 handler and init-system-dbus.conf (#559660) + +* Sun Jan 17 2010 Dennis Gilmore - 0.6.3-6 +- add patch from upstream fixing sparc alignment issues + +* Mon Jan 11 2010 Petr Lautrbach 0.6.3-5 +- License changed to GPLv2 and LGPLv2+ + +* Wed Dec 16 2009 Petr Lautrbach 0.6.3-4 +- audit events patch rebased for 0.6 (#470661) + +* Thu Dec 3 2009 Bill Nottingham 0.6.3-3 +- make 'telinit u' a no-op, temporarily + +* Fri Nov 27 2009 Petr Lautrbach 0.6.3-2 +- Removed tests which fail in koji + +* Fri Nov 20 2009 Casey Dahlin - 0.6.3-1 +- Upgrade to 0.6.3 + +* Fri Aug 21 2009 Tomas Mraz - 0.3.11-3 +- rebuilt with new audit + +* Sun Jul 26 2009 Fedora Release Engineering - 0.3.11-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Mon Jun 22 2009 Petr Lautrbach - 0.3.11-1 +- Update to 0.3.11 + +* Mon Apr 27 2009 Bill Nottingham - 0.3.9-24 +- Apply the audit patch correctly (#470661) + +* Fri Apr 3 2009 Casey Dahlin - 0.3.9-23 +- Add audit events patch from Steve Grubb (Bug #470661) + +* Fri Jan 23 2009 Casey Dahlin - 0.3.9-22 +- Re-add 'telinit u' support along with patch to fix it (#450488). Patch due to + + +* Mon Jan 12 2009 Bill Nottingham - 0.3.9-21 +- Remove 'telinit u' support as it is broken (#450488, ) + +* Fri Apr 25 2008 Bill Nottingham - 0.3.9-19 +- with the merge of event-compat-sysv, move the sysvinit obsoletes/provides here + +* Thu Apr 24 2008 Bill Nottingham - 0.3.9-18 +- fix some man page typos (#444008, ) + +* Wed Apr 09 2008 Casey Dahlin - 0.3.9-17 +- Added list of stock events to events(5) + +* Tue Apr 08 2008 Casey Dahlin - 0.3.9-16 +- Add telinit u support + +* Fri Apr 04 2008 Bill Nottingham - 0.3.9-15 +- Add a events(5) manpage that describes event syntax + +* Thu Apr 03 2008 Casey Dahlin - 0.3.9-14 +- Change bug report email address to fedora-devel-list@redhat.com + +* Thu Mar 14 2008 Bill Nottingham - 0.3.9-13 +- Ignore rpm temporary files of the foo; format +- Make ignores of .rpm{new,orig,save} match only at the end of the name + +* Wed Mar 13 2008 Bill Nottingham - 0.3.9-12 +- forgot about rpmorig too (ugh) + +* Wed Mar 13 2008 Casey Dahlin - 0.3.9-11 +- Make logd a noreplace + +* Wed Mar 13 2008 Casey Dahlin - 0.3.9-10 +- Add patch to ignore .rpm{new,save} files + +* Sun Mar 03 2008 Casey Dahlin - 0.3.9-9 +- Remove automake dependency, build Makefile.in changes into patch + +* Sun Mar 03 2008 Casey Dahlin - 0.3.9-8 +- Run automake after patching + +* Sun Mar 03 2008 Casey Dahlin - 0.3.9-7 +- Added BuildRequires: automake + +* Sun Mar 03 2008 Casey Dahlin - 0.3.9-6 +- Added patch to allow runtime tty changes + +* Fri Feb 15 2008 Casey Dahlin - 0.3.9-5 +- Added patch to imply --force on runlevels 0 and 6 + +* Wed Feb 06 2008 Casey Dahlin - 0.3.9-4 +- Patched for GCC 4.3 + +* Thu Jan 31 2008 Casey Dahlin - 0.3.9-3 +- Added AUTHORS, COPYING, etc. +- Made config --libdir option relative + +* Mon Jan 21 2008 Casey Dahlin - 0.3.9-2 +- Remove libnih and libupstart + +* Sun Jan 13 2008 Casey Dahlin - 0.3.9-1 +- Initial packaging