--
James Hunt
____________________________________
http://upstart.ubuntu.com/cookbook
http://upstart.ubuntu.com/cookbook/upstart_cookbook.pdf
=== modified file 'ChangeLog'
--- ChangeLog 2012-03-16 09:03:02 +0000
+++ ChangeLog 2012-03-16 21:02:13 +0000
@@ -12,6 +12,73 @@
- test_spawn():
- umask reset.
- New test "ensure multi processes output logged".
+ * dbus/com.ubuntu.Upstart.xml:
+ - added 'NotifyDiskWriteable' method.
+ * init/control.c:
+ - control_notify_disk_writeable(): New function to flush early job log.
+ * init/job_process.c:
+ - job_process_terminated(): Call log_handle_unflushed() to potentially
+ add log object to unflushed list (the early job log) in certain
+ scenarios.
+ * init/log.c:
+ - log_flushed: bool indicating successful flush of early job log.
+ - log_unflushed_files: The "early job log" list.
+ - log_new(): Call log_unflushed_init() and initialize new log members.
+ - log_flush(): Only call log_read_watch() conditionally now.
+ - log_io_reader(): More careful consideration of errno by
+ using saved value from log member.
+ - log_io_error_handler(): Set remote_closed for the benefit of
+ log_flushed() (to avoid flushing multiple times).
+ - log_file_open: Now saves errno value from open(2).
+ - log_read_watch(): Removed log->unflushed->len assert since it was
+ erroneous: even if unflushed data exists, it will be written in
+ order when log_io_reader() calls log_file_write().
+ - log_unflushed_init(): New function to initialise the
+ log_unflushed_files list.
+ - log_handle_unflushed(): New function that potentially adds log
+ object to the log_unflushed_files list to allow the data to be
+ flushed _after_ the parent object has been destroyed.
+ - log_clear_unflushed(): New function to clear the
+ log_unflushed_files list by attempting to flush the data to disk.
+ * init/log.h:
+ - Added new Log members: detached, remote_closed and open_errno.
+ - Updated documentation.
+ - extern for log_unflushed_files.
+ - Added prototypes for new functions: log_handle_unflushed(),
+ log_clear_unflushed() and log_unflushed_init().
+ * init/tests/test_job_process.c:
+ - test_run():
+ - Call log_unflushed_init().
+ - Corrected grammar in error messages for "ensure sane fds" tests.
+ - "with single line command writing fast and exiting": Call
+ nih_child_add_watch().
+ - added waitid() calls to ensure log data not added to
+ unflushed list.
+ - test_spawn():
+ - Call log_unflushed_init().
+ - Corrected grammar in error messages for "ensure sane fds" tests.
+ - Added TEST_ALLOC_SAFE() to "simple test" to ensure
+ destructors run correctly.
+ - "read single null byte with 'console log'": Call
+ log_handle_unflushed() and added missing free.
+ - "read data from forked process": Call
+ log_handle_unflushed().
+ * init/tests/test_log.c:
+ - Updated documentation.
+ - Added calls to log_unflushed_init().
+ - "ensure logger flushes cached data on request": New test
+ for log_handle_unflushed().
+ * util/initctl.c:
+ - notify_disk_writeable_action(): New function to notify
+ Upstart that the disk is writeable.
+ - commands: Added new command "notify-disk-writeable".
+ * util/man/initctl.8: Updated for new notify-disk-writeable command.
+ * util/tests/test_initctl.c:
+ - STOP_UPSTART(): Check return from kill(2).
+ - test_show_config(): Adding missing rmdir(2).
+ - test_check_config(): Adding missing rmdir(2).
+ - test_notify_disk_writeable(): New function embodying new test
+ "with job ending before log disk writeable".
[ Steve Langasek <[email protected]> ]
* init/tests/test_job_process:
=== modified file 'dbus/com.ubuntu.Upstart.xml'
--- dbus/com.ubuntu.Upstart.xml 2010-12-10 07:18:34 +0000
+++ dbus/com.ubuntu.Upstart.xml 2012-03-16 21:02:13 +0000
@@ -57,6 +57,9 @@
<arg name="file" type="h" direction="in" />
</method>
+ <method name="NotifyDiskWriteable">
+ </method>
+
<!-- Basic information about Upstart -->
<property name="version" type="s" access="read" />
<property name="log_priority" type="s" access="readwrite" />
=== modified file 'init/control.c'
--- init/control.c 2011-06-15 13:20:41 +0000
+++ init/control.c 2012-03-16 21:02:13 +0000
@@ -761,3 +761,48 @@
if (use_session_bus)
nih_debug ("Using session bus");
}
+/**
+ * control_notify_disk_writeable:
+ * @data: not used,
+ * @message: D-Bus connection and message received,
+ *
+ * Implements the NotifyDiskWriteable method of the
+ * com.ubuntu.Upstart interface.
+ *
+ * Called to flush the job logs for all jobs that ended before the log
+ * disk became writeable.
+ *
+ * Returns: zero on success, negative value on raised error.
+ **/
+int
+control_notify_disk_writeable (void *data,
+ NihDBusMessage *message)
+{
+ int ret;
+ Session *session;
+
+ nih_assert (message != NULL);
+
+ /* Get the relevant session */
+ session = session_from_dbus (NULL, message);
+
+ if (session && session->user) {
+ nih_dbus_error_raise_printf (
+ DBUS_INTERFACE_UPSTART ".Error.PermissionDenied",
+ _("You do not have permission to notify disk is writeable"));
+ return -1;
+ }
+
+ /* "nop" when run from a chroot */
+ if (session && session->chroot)
+ return 0;
+
+ ret = log_clear_unflushed ();
+
+ if (ret < 0) {
+ nih_error_raise_system ();
+ return -1;
+ }
+
+ return 0;
+}
=== modified file 'init/job_process.c'
--- init/job_process.c 2012-03-16 09:03:02 +0000
+++ init/job_process.c 2012-03-16 21:02:13 +0000
@@ -1604,12 +1604,28 @@
}
if (job->class->console == CONSOLE_LOG && job->log[process]) {
+ int ret;
+
/* It is imperative that we free the log at this stage to ensure
* that jobs which respawn have their log written _now_
* (and not just when the overall Job object is freed at
* some distant future point).
*/
- nih_free (job->log[process]);
+ ret = log_handle_unflushed (job->log, job->log[process]);
+
+ if (ret != 0) {
+ if (ret < 0) {
+ /* Any lingering data will now be lost in what
+ * is probably a low-memory scenario.
+ */
+ nih_warn (_("Failed to add log to unflushed queue"));
+ }
+ nih_free (job->log[process]);
+ }
+
+ /* Either the log has been freed, or it needs to be
+ * severed from its parent job fully.
+ */
job->log[process] = NULL;
}
=== modified file 'init/log.c'
--- init/log.c 2012-02-13 12:08:33 +0000
+++ init/log.c 2012-03-16 21:02:13 +0000
@@ -39,6 +39,21 @@
static void log_flush (Log *log);
/**
+ * log_flushed:
+ *
+ * TRUE if log_clear_unflushed() has been called successfully.
+ **/
+static int log_flushed = 0;
+
+/**
+ * log_unflushed_files:
+ *
+ * List of known sources of configuration; each item is an
+ * NihListEntry.
+ **/
+NihList *log_unflushed_files = NULL;
+
+/**
* log_new:
*
* @parent: parent for new job class,
@@ -101,10 +116,15 @@
if (! log)
return NULL;
- log->fd = -1;
- log->uid = uid;
- log->unflushed = NULL;
- log->io = NULL;
+ log_unflushed_init ();
+
+ log->fd = -1;
+ log->uid = uid;
+ log->unflushed = NULL;
+ log->io = NULL;
+ log->detached = 0;
+ log->remote_closed = 0;
+ log->open_errno = 0;
log->path = nih_strndup (log, path, len);
if (! log->path)
@@ -223,7 +243,8 @@
*
* Therefore, attempt to read from the watch fd until we get an error.
*/
- log_read_watch (log);
+ if (! log->remote_closed)
+ log_read_watch (log);
flags = fcntl (log->io->watch->fd, F_GETFL);
@@ -306,8 +327,10 @@
*/
nih_assert (sizeof (size_t) == sizeof (ssize_t));
- if (log_file_open (log) < 0) {
- if (errno != ENOSPC) {
+ ret = log_file_open (log);
+
+ if (ret < 0) {
+ if (log->open_errno != ENOSPC) {
/* Add new data to unflushed buffer */
if (nih_io_buffer_push (log->unflushed, buf, len) < 0)
return;
@@ -360,6 +383,8 @@
/* Ensure the NihIo is closed */
nih_free (log->io);
log->io = NULL;
+
+ log->remote_closed = 1;
}
/**
@@ -390,7 +415,7 @@
ret = fstat (log->fd, &statbuf);
/* Already open */
- if (log->fd > -1 && (!ret && statbuf.st_nlink))
+ if (log->fd > -1 && (! ret && statbuf.st_nlink))
return 0;
/* File was deleted. This isn't a problem for
@@ -418,6 +443,8 @@
*/
log->fd = open (log->path, flags, mode);
+ log->open_errno = errno;
+
/* Open may have failed due to path being unaccessible
* (disk might not be mounted yet).
*/
@@ -597,8 +624,6 @@
/* Must not be called if there is unflushed data as the log
* would then not be written in order.
*/
- nih_assert (! log->unflushed->len);
-
io = log->io;
if (! io)
@@ -661,3 +686,116 @@
}
}
}
+
+/**
+ * log_unflushed_init:
+ *
+ * Initialise the log_unflushed_files list.
+ **/
+void
+log_unflushed_init (void)
+{
+ if (! log_unflushed_files)
+ log_unflushed_files = NIH_MUST (nih_list_new (NULL));
+}
+
+/**
+ * log_handle_unflushed:
+ * @parent: parent of log,
+ * @log: log.
+ *
+ * Potentially add specified log to list of unflushed log files
+ * (for processing when a disk becomes writeable).
+ *
+ * This function should be called for each log object at the time the
+ * associated process exits to ensure that all data from that process is
+ * captured to the log.
+ *
+ * Returns: 0 on success (log added to list), 1 if log does not need to
+ * be added to the list, or -1 on error.
+ **/
+int
+log_handle_unflushed (void *parent, Log *log)
+{
+ NihListEntry *elem;
+
+ nih_assert (log);
+ nih_assert (log->detached == 0);
+
+ log_read_watch (log);
+
+ if (! log->unflushed->len)
+ return 1;
+
+ if ((log->open_errno != EROFS && log->open_errno != EPERM
+ && log->open_errno != EACCES) || log_flushed)
+ return 1;
+
+ log_unflushed_init ();
+
+ /* re-parent */
+ nih_ref (log, log_unflushed_files);
+ nih_unref (log, parent);
+
+ elem = nih_list_entry_new (log);
+ if (! elem) {
+ /* If memory is low, we discard the unflushed
+ * data buffer too.
+ */
+ nih_unref (log, log_unflushed_files);
+ return -1;
+ }
+
+ /* Indicate separation from parent */
+ log->detached = 1;
+
+ elem->data = log;
+ nih_list_add_after (log_unflushed_files, &elem->entry);
+
+ return 0;
+}
+
+/* log_clear_unflushed:
+ *
+ * Attempt to flush all unflushed log buffers to persistent storage.
+ *
+ * Call once the log disk partition is mounted as read-write.
+ *
+ * Returns: 0 on success, -1 on error.
+ */
+int
+log_clear_unflushed (void)
+{
+ log_unflushed_init ();
+
+ NIH_LIST_FOREACH_SAFE (log_unflushed_files, iter) {
+ NihListEntry *elem;
+ Log *log;
+
+ elem = (NihListEntry *)iter;
+ log = elem->data;
+
+ /* We expect 'an' error (as otherwise why would the log be
+ * in this list?), but don't assert EROFS specifically
+ * as a precaution (since an attempt to flush the log at
+ * another time may result in some other errno value).
+ */
+ nih_assert (log->open_errno);
+
+ nih_assert (log->unflushed->len);
+ nih_assert (log->remote_closed);
+ nih_assert (log->detached);
+
+ if (log_file_open (log) != 0)
+ return -1;
+
+ if (log_file_write (log, NULL, 0) < 0)
+ return -1;
+
+ nih_free (log);
+ }
+
+ log_flushed = 1;
+
+ return 0;
+}
=== modified file 'init/log.h'
--- init/log.h 2012-01-26 08:59:08 +0000
+++ init/log.h 2012-02-14 16:47:34 +0000
@@ -52,9 +52,12 @@
*
* @fd: Write file descriptor associated with @path,
* @path: Full path to log file,
- * @io: NihIo associated with jobs stdout and stderr.
+ * @io: NihIo associated with jobs stdout and stderr,
* @uid: User ID of caller,
- * @unflushed: Unflushed data.
+ * @unflushed: Unflushed data,
+ * @detached: TRUE if log is no longer associated with a parent,(job),
+ * @remote_closed: TRUE if remote end of pty has been closed,
+ * @open_errno: value of errno immediately after last attempt to open @path.
**/
typedef struct log {
int fd;
@@ -62,16 +65,25 @@
NihIo *io;
uid_t uid;
NihIoBuffer *unflushed;
+ int detached;
+ int remote_closed;
+ int open_errno;
} Log;
NIH_BEGIN_EXTERN
+extern NihList *log_unflushed_files;
+
Log *log_new (const void *parent, const char *path,
int fd, uid_t uid)
__attribute__ ((warn_unused_result, malloc));
void log_io_reader (Log *log, NihIo *io, const char *buf, size_t len);
void log_io_error_handler (Log *log, NihIo *io);
int log_destroy (Log *log);
+int log_handle_unflushed (void *parent, Log *log);
+int log_clear_unflushed (void);
+void log_unflushed_init (void);
+
NIH_END_EXTERN
=== modified file 'init/tests/test_job_process.c'
--- init/tests/test_job_process.c 2012-03-16 09:03:02 +0000
+++ init/tests/test_job_process.c 2012-03-16 21:02:13 +0000
@@ -444,6 +444,9 @@
char buffer[1024];
pid_t pid;
int i;
+ siginfo_t siginfo;
+
+ log_unflushed_init ();
TEST_FUNCTION ("job_process_run");
@@ -1186,7 +1189,7 @@
/* 0, 1, 2 */
if (fd < 3) {
if (! valid)
- TEST_FAILED ("fd %d is unexpected invalid", fd);
+ TEST_FAILED ("fd %d is unexpectedly invalid", fd);
} else {
if (valid)
TEST_FAILED ("fd %d is unexpectedly valid", fd);
@@ -1253,7 +1256,7 @@
/* 0, 1, 2 */
if (fd < 3) {
if (! valid)
- TEST_FAILED ("fd %d is unexpected invalid", fd);
+ TEST_FAILED ("fd %d is unexpectedly invalid", fd);
} else {
if (valid)
TEST_FAILED ("fd %d is unexpectedly valid", fd);
@@ -1320,7 +1323,7 @@
/* 0, 1, 2 */
if (fd < 3) {
if (! valid)
- TEST_FAILED ("fd %d is unexpected invalid", fd);
+ TEST_FAILED ("fd %d is unexpectedly invalid", fd);
} else {
if (valid)
TEST_FAILED ("fd %d is unexpectedly valid", fd);
@@ -1387,7 +1390,7 @@
/* 0, 1, 2 */
if (fd < 3) {
if (! valid)
- TEST_FAILED ("fd %d is unexpected invalid", fd);
+ TEST_FAILED ("fd %d is unexpectedly invalid", fd);
} else {
if (valid)
TEST_FAILED ("fd %d is unexpectedly valid", fd);
@@ -3564,6 +3567,15 @@
TEST_CMD_ECHO);
class->process[PROCESS_MAIN]->script = FALSE;
+ /* XXX: Manually add the class so job_process_find() works */
+ nih_hash_add (job_classes, &class->entry);
+
+ NIH_MUST (nih_child_add_watch (NULL,
+ -1,
+ NIH_CHILD_ALL,
+ job_process_handler,
+ NULL));
+
job = job_new (class, "");
job->goal = JOB_START;
job->state = JOB_SPAWNED;
@@ -3695,6 +3707,14 @@
}
TEST_EQ (kill (pid, 0), 0);
+
+ /* Wait until the process is in a known state. This ensures that
+ * when job_process_terminated() calls log_handle_unflushed(),
+ * the log object will _not_ get added to the unflushed list,
+ * meaning it will get destroyed immediately.
+ */
+ waitid (P_PID, pid, &siginfo, WEXITED | WNOWAIT);
+
nih_child_poll ();
/* The process should now be dead */
@@ -3785,7 +3805,12 @@
TEST_FREE_TAG (job);
TEST_FREE_TAG (job->log);
- TEST_FORCE_WATCH_UPDATE ();
+ /* Wait until the process is in a known state. This ensures that
+ * when job_process_terminated() calls log_handle_unflushed(),
+ * the log object will _not_ get added to the unflushed list,
+ * meaning it will get destroyed immediately.
+ */
+ waitid (P_PID, pid, &siginfo, WEXITED | WNOWAIT);
nih_child_poll ();
@@ -3993,6 +4018,9 @@
JobProcessError *perr;
int status;
struct stat statbuf;
+ int ret;
+
+ log_unflushed_init ();
/* reset */
(void) umask (0);
@@ -4457,7 +4485,7 @@
/* 0, 1, 2 */
if (fd < 3) {
if (! valid)
- TEST_FAILED ("fd %d is unexpected invalid", fd);
+ TEST_FAILED ("fd %d is unexpectedly invalid", fd);
} else {
if (valid)
TEST_FAILED ("fd %d is unexpectedly valid", fd);
@@ -4511,7 +4539,7 @@
/* 0, 1, 2 */
if (fd < 3) {
if (! valid)
- TEST_FAILED ("fd %d is unexpected invalid", fd);
+ TEST_FAILED ("fd %d is unexpectedly invalid", fd);
} else {
if (valid)
TEST_FAILED ("fd %d is unexpectedly valid", fd);
@@ -4640,10 +4668,10 @@
TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0);
TEST_ALLOC_FAIL {
TEST_ALLOC_SAFE {
- class = job_class_new (NULL, "test", NULL);
+ class = job_class_new (NULL, "simple-test", NULL);
TEST_NE_P (class, NULL);
- TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
+ TEST_GT (sprintf (filename, "%s/simple-test.log", dirname), 0);
job = job_new (class, "");
TEST_NE_P (job, NULL);
@@ -4664,16 +4692,21 @@
pid = job_process_spawn (job, args_array, NULL, FALSE, -1, PROCESS_MAIN);
if (test_alloc_failed) {
+ TEST_LT (pid, 0);
err = nih_error_get ();
TEST_NE_P (err, NULL);
TEST_EQ (err->number, ENOMEM);
nih_free (err);
- TEST_LT (pid, 0);
} else {
TEST_GT (pid, 0);
TEST_EQ (unlink (script), 0);
unlink (filename);
}
+
+ TEST_ALLOC_SAFE {
+ /* May alloc space if there is log data */
+ nih_free (class);
+ }
}
/************************************************************/
@@ -4691,10 +4724,10 @@
*/
TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0);
- class = job_class_new (NULL, "test", NULL);
+ class = job_class_new (NULL, "with-single-line-script-and-console-log", NULL);
TEST_NE_P (class, NULL);
- TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
+ TEST_GT (sprintf (filename, "%s/with-single-line-script-and-console-log.log", dirname), 0);
job = job_new (class, "");
TEST_NE_P (job, NULL);
@@ -4745,10 +4778,10 @@
TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0);
- class = job_class_new (NULL, "test", NULL);
+ class = job_class_new (NULL, "with-multi-line-script-and-console-log", NULL);
TEST_NE_P (class, NULL);
- TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
+ TEST_GT (sprintf (filename, "%s/with-multi-line-script-and-console-log.log", dirname), 0);
job = job_new (class, "");
TEST_NE_P (job, NULL);
@@ -4801,10 +4834,10 @@
TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0);
- class = job_class_new (NULL, "test", NULL);
+ class = job_class_new (NULL, "read-single-null-bytes-with-console-log", NULL);
TEST_NE_P (class, NULL);
- TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
+ TEST_GT (sprintf (filename, "%s/read-single-null-bytes-with-console-log.log", dirname), 0);
job = job_new (class, "");
TEST_NE_P (job, NULL);
@@ -4821,7 +4854,8 @@
TEST_EQ (waitpid (pid, &status, 0), pid);
TEST_TRUE (WIFEXITED (status));
- TEST_FORCE_WATCH_UPDATE ();
+ ret = log_handle_unflushed (job->log, job->log[PROCESS_MAIN]);
+ TEST_EQ (ret, 1);
output = fopen (filename, "r");
TEST_NE_P (output, NULL);
@@ -4838,6 +4872,7 @@
TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0);
nih_free (job);
+ nih_free (class);
/************************************************************/
TEST_FEATURE ("read data from forked process");
@@ -4848,10 +4883,10 @@
TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0);
- class = job_class_new (NULL, "test", NULL);
+ class = job_class_new (NULL, "read-data-from-forked-process", NULL);
TEST_NE_P (class, NULL);
- TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
+ TEST_GT (sprintf (filename, "%s/read-data-from-forked-process.log", dirname), 0);
job = job_new (class, "");
TEST_NE_P (job, NULL);
@@ -4872,12 +4907,18 @@
TEST_EQ (waitpid (pid, &status, 0), pid);
TEST_TRUE (WIFEXITED (status));
+ TEST_EQ (WEXITSTATUS (status), 0);
+
+ ret = log_handle_unflushed (job->log, job->log[PROCESS_MAIN]);
+ TEST_EQ (ret, 1);
TEST_FORCE_WATCH_UPDATE ();
/* This will eventually call the log destructor */
nih_free (class);
+ TEST_EQ (stat (filename, &statbuf), 0);
+
output = fopen (filename, "r");
TEST_NE_P (output, NULL);
@@ -8221,7 +8262,7 @@
TEST_TRUE (WIFEXITED (status));
TEST_EQ (WEXITSTATUS (status), 0);
- /* Now carray on with the test */
+ /* Now carry on with the test */
job->goal = JOB_START;
job->state = JOB_SPAWNED;
job->pid[PROCESS_MAIN] = pid;
=== modified file 'init/tests/test_log.c'
--- init/tests/test_log.c 2012-02-03 13:17:24 +0000
+++ init/tests/test_log.c 2012-03-16 21:02:13 +0000
@@ -81,6 +81,9 @@
* nih_new
* __nih_alloc # XXX: call 7
*
+ * (There is actually an 8th call to log_unflushed_init(), but we handle
+ * that by calling log_unflushed_init() prior to any tests).
+ *
* XXX: Unfortunately, having created a log, we cannot intelligently test the
* memory failure handling of the asynchronously called log_io_reader() due to the
* underlying complexities of the way NIH re-allocs memory at particular
@@ -116,20 +119,23 @@
/* XXX:
*
- * It is *essential* we call this prior to any TEST_ALLOC_FAIL
- * blocks since TEST_ALLOC_FAIL tracks calls to memory
- * allocation routines and expects the function under test to
- * call said routines *the same number of times* on each loop.
- * NIH will attempt to initialise internal data structures
- * lazily so force it to not be lazy to avoid surprises wrt
- * number of malloc calls.
+ * It is *essential* we call these functions prior to any
+ * TEST_ALLOC_FAIL blocks since TEST_ALLOC_FAIL tracks calls to
+ * memory allocation routines and expects the function under
+ * test to call said routines *the same number of times* on each
+ * loop. NIH will attempt to initialise internal data
+ * structures lazily so force it to not be lazy to avoid
+ * surprises wrt number of malloc calls.
*/
nih_io_init ();
nih_error_init ();
+ log_unflushed_init ();
/************************************************************/
TEST_FEATURE ("object checks with uid 0");
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
+
TEST_ALLOC_FAIL {
TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
log = log_new (NULL, path, pty_master, 0);
@@ -157,10 +163,12 @@
TEST_EQ (log->io->watch->fd, pty_master);
TEST_EQ (log->uid, 0);
TEST_LT (log->fd, 0);
+ TEST_NE (log_unflushed_files, NULL);
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
close (pty_slave);
- /* frees fds[0] */
+ /* frees pty_master */
nih_free (log);
log = NULL;
}
@@ -254,12 +262,21 @@
if (test_alloc_failed == 1+LOG_NEW_ALLOC_CALLS) {
TEST_NE_P (log, NULL);
close (pty_slave);
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
+ ret = log_handle_unflushed (NULL, log);
+ TEST_EQ (ret, 1);
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
nih_free (log);
TEST_EQ (unlink (filename), 0);
continue;
}
close (pty_slave);
+ TEST_FORCE_WATCH_UPDATE ();
+ ret = log_handle_unflushed (NULL, log);
+ TEST_EQ (ret, 1);
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
+
nih_free (log);
TEST_EQ (stat (filename, &statbuf), 0);
@@ -573,6 +590,85 @@
TEST_EQ (unlink (filename), 0);
/************************************************************/
+ TEST_FEATURE ("ensure logger flushes cached data on request");
+
+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
+
+ TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
+
+ TEST_NE (log_unflushed_files, NULL);
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
+
+ TEST_EQ (stat (dirname, &statbuf), 0);
+
+ /* Save */
+ old_perms = statbuf.st_mode;
+
+ /* Make file inaccessible */
+ TEST_EQ (chmod (dirname, 0x0), 0);
+
+ log = log_new (NULL, filename, pty_master, 0);
+ TEST_NE_P (log, NULL);
+
+ ret = write (pty_slave, str, strlen (str));
+ TEST_GT (ret, 0);
+ ret = write (pty_slave, "\n", 1);
+ TEST_EQ (ret, 1);
+
+ close (pty_slave);
+
+ TEST_FORCE_WATCH_UPDATE ();
+
+ /* Ensure no log file written */
+ TEST_LT (stat (filename, &statbuf), 0);
+
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
+
+ TEST_FREE_TAG (log);
+
+ TEST_EQ (log_handle_unflushed (NULL, log), 0);
+
+ TEST_FALSE (NIH_LIST_EMPTY (log_unflushed_files));
+
+ /* Again, ensure no log file written */
+ TEST_LT (stat (filename, &statbuf), 0);
+
+ TEST_EQ (log_clear_unflushed (), -1);
+
+ /* Restore access */
+ TEST_EQ (chmod (dirname, old_perms), 0);
+
+ /* Force flush */
+ TEST_EQ (log_clear_unflushed (), 0);
+
+ TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
+
+ TEST_EQ (stat (filename, &statbuf), 0);
+
+ TEST_TRUE (S_ISREG (statbuf.st_mode));
+ TEST_TRUE (statbuf.st_mode & S_IRUSR);
+ TEST_TRUE (statbuf.st_mode & S_IWUSR);
+ TEST_FALSE (statbuf.st_mode & S_IXUSR);
+
+ TEST_TRUE (statbuf.st_mode & S_IRGRP);
+ TEST_FALSE (statbuf.st_mode & S_IWGRP);
+ TEST_FALSE (statbuf.st_mode & S_IXGRP);
+
+ TEST_FALSE (statbuf.st_mode & S_IROTH);
+ TEST_FALSE (statbuf.st_mode & S_IWOTH);
+ TEST_FALSE (statbuf.st_mode & S_IXOTH);
+
+ output = fopen (filename, "r");
+ TEST_NE_P (output, NULL);
+
+ TEST_FILE_EQ (output, "hello, world!\r\n");
+ TEST_FILE_END (output);
+ fclose (output);
+
+ TEST_EQ (unlink (filename), 0);
+ TEST_FREE (log);
+
+ /************************************************************/
TEST_FEATURE ("ensure logger flushes when destroyed with uid 0");
TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
@@ -650,7 +746,9 @@
TEST_EQ (ret, 1);
close (pty_slave);
- nih_free (log);
+
+ ret = log_handle_unflushed (NULL, log);
+ TEST_EQ (ret, 1);
output = fopen (filename, "r");
TEST_NE_P (output, NULL);
@@ -1163,10 +1261,9 @@
TEST_FREE (log->unflushed);
}
-
- int
+int
main (int argc,
- char *argv[])
+ char *argv[])
{
/* run tests in legacy (pre-session support) mode */
setenv ("UPSTART_NO_SESSIONS", "1", 1);
=== modified file 'po/upstart.pot'
--- po/upstart.pot 2012-03-01 11:27:33 +0000
+++ po/upstart.pot 2012-03-16 21:02:13 +0000
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: upstart 1.5\n"
"Report-Msgid-Bugs-To: [email protected]\n"
-"POT-Creation-Date: 2012-03-01 11:24+0000\n"
+"POT-Creation-Date: 2012-03-16 20:58+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
@@ -85,6 +85,10 @@
msgid "The log priority given was not recognised"
msgstr ""
+#: init/control.c:792
+msgid "You do not have permission to notify disk is writeable"
+msgstr ""
+
#: init/errors.h:60
msgid "Illegal parameter"
msgstr ""
@@ -171,97 +175,97 @@
msgid "Event failed"
msgstr ""
-#: init/job.c:235
+#: init/job.c:246
#, c-format
msgid "%s goal changed from %s to %s"
msgstr ""
-#: init/job.c:306
+#: init/job.c:317
#, c-format
msgid "%s state changed from %s to %s"
msgstr ""
-#: init/job.c:725 init/job.c:763
+#: init/job.c:736 init/job.c:774
msgid "Job failed to start"
msgstr ""
-#: init/job.c:738 init/job.c:774
+#: init/job.c:749 init/job.c:785
msgid "Job failed while stopping"
msgstr ""
-#: init/job.c:750 init/job.c:785
+#: init/job.c:761 init/job.c:796
msgid "Job failed to restart"
msgstr ""
-#: init/job.c:979
+#: init/job.c:990
msgid "stop"
msgstr ""
-#: init/job.c:981
+#: init/job.c:992
msgid "start"
msgstr ""
-#: init/job.c:983
+#: init/job.c:994
msgid "respawn"
msgstr ""
-#: init/job.c:1028
+#: init/job.c:1039
msgid "waiting"
msgstr ""
-#: init/job.c:1030
+#: init/job.c:1041
msgid "starting"
msgstr ""
-#: init/job.c:1032 init/process.c:80
+#: init/job.c:1043 init/process.c:80
msgid "pre-start"
msgstr ""
-#: init/job.c:1034
+#: init/job.c:1045
msgid "spawned"
msgstr ""
-#: init/job.c:1036 init/process.c:82
+#: init/job.c:1047 init/process.c:82
msgid "post-start"
msgstr ""
-#: init/job.c:1038
+#: init/job.c:1049
msgid "running"
msgstr ""
-#: init/job.c:1040 init/process.c:84
+#: init/job.c:1051 init/process.c:84
msgid "pre-stop"
msgstr ""
-#: init/job.c:1042
+#: init/job.c:1053
msgid "stopping"
msgstr ""
-#: init/job.c:1044
+#: init/job.c:1055
msgid "killed"
msgstr ""
-#: init/job.c:1046 init/process.c:86
+#: init/job.c:1057 init/process.c:86
msgid "post-stop"
msgstr ""
-#: init/job.c:1129 init/job.c:1204 init/job.c:1280 init/job_class.c:754
+#: init/job.c:1140 init/job.c:1215 init/job.c:1291 init/job_class.c:754
#: init/job_class.c:894 init/job_class.c:1029
#, c-format
msgid "You do not have permission to modify job: %s"
msgstr ""
-#: init/job.c:1137 init/job_class.c:818
+#: init/job.c:1148 init/job_class.c:818
#, c-format
msgid "Job is already running: %s"
msgstr ""
-#: init/job.c:1212 init/job.c:1288 init/job_class.c:948 init/job_class.c:1082
+#: init/job.c:1223 init/job.c:1299 init/job_class.c:948 init/job_class.c:1082
#, c-format
msgid "Job has already been stopped: %s"
msgstr ""
-#: init/job_class.c:581 init/job_class.c:791 util/initctl.c:1383
+#: init/job_class.c:582 init/job_class.c:792 util/initctl.c:1383
msgid "Usage"
msgstr ""
@@ -285,214 +289,218 @@
msgid "%s %s process (%d)"
msgstr ""
-#: init/job_process.c:461
-msgid "No available ptys"
+#: init/job_process.c:469
+msgid "Failed to create pty - disabling logging for job"
msgstr ""
-#: init/job_process.c:503
+#: init/job_process.c:516
#, c-format
msgid "Pausing %s (%d) [pre-exec] for debug"
msgstr ""
-#: init/job_process.c:705
+#: init/job_process.c:718
#, c-format
msgid "Failed to open system console: %s"
msgstr ""
-#: init/job_process.c:1013
+#: init/job_process.c:1026
#, c-format
msgid "unable to move script fd: %s"
msgstr ""
-#: init/job_process.c:1018
+#: init/job_process.c:1031
#, c-format
msgid "unable to open console: %s"
msgstr ""
-#: init/job_process.c:1073
+#: init/job_process.c:1086
#, c-format
msgid "unable to set \"%s\" resource limit: %s"
msgstr ""
-#: init/job_process.c:1078
+#: init/job_process.c:1091
#, c-format
msgid "unable to set priority: %s"
msgstr ""
-#: init/job_process.c:1083
+#: init/job_process.c:1096
#, c-format
msgid "unable to set oom adjustment: %s"
msgstr ""
-#: init/job_process.c:1088
+#: init/job_process.c:1101
#, c-format
msgid "unable to change root directory: %s"
msgstr ""
-#: init/job_process.c:1093
+#: init/job_process.c:1106
#, c-format
msgid "unable to change working directory: %s"
msgstr ""
-#: init/job_process.c:1098
+#: init/job_process.c:1111
#, c-format
msgid "unable to set trace: %s"
msgstr ""
-#: init/job_process.c:1103
+#: init/job_process.c:1116
#, c-format
msgid "unable to execute: %s"
msgstr ""
-#: init/job_process.c:1108
+#: init/job_process.c:1121
#, c-format
msgid "unable to getpwnam: %s"
msgstr ""
-#: init/job_process.c:1113
+#: init/job_process.c:1126
#, c-format
msgid "unable to getgrnam: %s"
msgstr ""
-#: init/job_process.c:1118
+#: init/job_process.c:1131
msgid "unable to find setuid user"
msgstr ""
-#: init/job_process.c:1122
+#: init/job_process.c:1135
msgid "unable to find setgid group"
msgstr ""
-#: init/job_process.c:1126
+#: init/job_process.c:1139
#, c-format
msgid "unable to setuid: %s"
msgstr ""
-#: init/job_process.c:1131
+#: init/job_process.c:1144
#, c-format
msgid "unable to setgid: %s"
msgstr ""
-#: init/job_process.c:1136
+#: init/job_process.c:1149
#, c-format
msgid "unable to chown: %s"
msgstr ""
-#: init/job_process.c:1141
+#: init/job_process.c:1154
#, c-format
msgid "unable to open pt master: %s"
msgstr ""
-#: init/job_process.c:1146
+#: init/job_process.c:1159
#, c-format
msgid "unable to unlockpt: %s"
msgstr ""
-#: init/job_process.c:1151
+#: init/job_process.c:1164
#, c-format
msgid "unable to get ptsname: %s"
msgstr ""
-#: init/job_process.c:1156
+#: init/job_process.c:1169
#, c-format
msgid "unable to open pt slave: %s"
msgstr ""
-#: init/job_process.c:1187 init/job_process.c:1237
+#: init/job_process.c:1200 init/job_process.c:1250
#, c-format
msgid "Sending %s signal to %s %s process (%d)"
msgstr ""
-#: init/job_process.c:1196 init/job_process.c:1246
+#: init/job_process.c:1209 init/job_process.c:1259
#, c-format
msgid "Failed to send %s signal to %s %s process (%d): %s"
msgstr ""
-#: init/job_process.c:1307
+#: init/job_process.c:1320
#, c-format
msgid "%s %s process (%d) terminated with status %d"
msgstr ""
-#: init/job_process.c:1312
+#: init/job_process.c:1325
#, c-format
msgid "%s %s process (%d) exited normally"
msgstr ""
-#: init/job_process.c:1327
+#: init/job_process.c:1340
#, c-format
msgid "%s %s process (%d) killed by %s signal"
msgstr ""
-#: init/job_process.c:1331
+#: init/job_process.c:1344
#, c-format
msgid "%s %s process (%d) killed by signal %d"
msgstr ""
-#: init/job_process.c:1345
+#: init/job_process.c:1358
#, c-format
msgid "%s %s process (%d) stopped by %s signal"
msgstr ""
-#: init/job_process.c:1349
+#: init/job_process.c:1362
#, c-format
msgid "%s %s process (%d) stopped by signal %d"
msgstr ""
-#: init/job_process.c:1363
+#: init/job_process.c:1376
#, c-format
msgid "%s %s process (%d) continued by %s signal"
msgstr ""
-#: init/job_process.c:1367
+#: init/job_process.c:1380
#, c-format
msgid "%s %s process (%d) continued by signal %d"
msgstr ""
-#: init/job_process.c:1502
+#: init/job_process.c:1515
#, c-format
msgid "%s respawning too fast, stopped"
msgstr ""
-#: init/job_process.c:1508
+#: init/job_process.c:1521
#, c-format
msgid "%s %s process ended, respawning"
msgstr ""
-#: init/job_process.c:1760
+#: init/job_process.c:1621
+msgid "Failed to add log to unflushed queue"
+msgstr ""
+
+#: init/job_process.c:1787
#, c-format
msgid "Failed to set ptrace options for %s %s process (%d): %s"
msgstr ""
-#: init/job_process.c:1773 init/job_process.c:1968
+#: init/job_process.c:1800 init/job_process.c:1995
#, c-format
msgid "Failed to continue traced %s %s process (%d): %s"
msgstr ""
-#: init/job_process.c:1813 init/job_process.c:1904 init/job_process.c:1959
+#: init/job_process.c:1840 init/job_process.c:1931 init/job_process.c:1986
#, c-format
msgid "Failed to detach traced %s %s process (%d): %s"
msgstr ""
-#: init/job_process.c:1853
+#: init/job_process.c:1880
#, c-format
msgid "Failed to deliver signal to traced %s %s process (%d): %s"
msgstr ""
-#: init/job_process.c:1888
+#: init/job_process.c:1915
#, c-format
msgid "Failed to obtain child process id for %s %s process (%d): %s"
msgstr ""
-#: init/job_process.c:1895
+#: init/job_process.c:1922
#, c-format
msgid "%s %s process (%d) became new process (%d)"
msgstr ""
-#: init/job_process.c:1954
+#: init/job_process.c:1981
#, c-format
msgid "%s %s process (%d) executable changed"
msgstr ""
-#: init/log.c:327
+#: init/log.c:350
msgid "Failed to write to log file"
msgstr ""
@@ -645,74 +653,74 @@
msgid "Invalid job class"
msgstr ""
-#: util/initctl.c:2212
+#: util/initctl.c:2248
msgid "unknown event"
msgstr ""
-#: util/initctl.c:2216
+#: util/initctl.c:2252
msgid "unknown job"
msgstr ""
-#: util/initctl.c:2317
+#: util/initctl.c:2353
msgid "use D-Bus session bus to connect to init daemon (for testing)"
msgstr ""
-#: util/initctl.c:2319
+#: util/initctl.c:2355
msgid "use D-Bus system bus to connect to init daemon"
msgstr ""
-#: util/initctl.c:2321
+#: util/initctl.c:2357
msgid "destination well-known name on D-Bus bus"
msgstr ""
-#: util/initctl.c:2334
+#: util/initctl.c:2370
msgid "do not wait for job to start before exiting"
msgstr ""
-#: util/initctl.c:2346
+#: util/initctl.c:2382
msgid "do not wait for job to stop before exiting"
msgstr ""
-#: util/initctl.c:2358
+#: util/initctl.c:2394
msgid "do not wait for job to restart before exiting"
msgstr ""
-#: util/initctl.c:2397
+#: util/initctl.c:2433
msgid "do not wait for event to finish before exiting"
msgstr ""
-#: util/initctl.c:2438
+#: util/initctl.c:2474
msgid ""
"enumerate list of events and jobs causing job created from job config to "
"start/stop"
msgstr ""
-#: util/initctl.c:2451
+#: util/initctl.c:2487
msgid "ignore specified list of events (comma-separated)"
msgstr ""
-#: util/initctl.c:2453
+#: util/initctl.c:2489
msgid "Generate warning for any unreachable events/jobs"
msgstr ""
-#: util/initctl.c:2472
+#: util/initctl.c:2508
msgid "Job"
msgstr ""
-#: util/initctl.c:2479
+#: util/initctl.c:2515
msgid "Event"
msgstr ""
-#: util/initctl.c:2487 util/initctl.c:2499 util/initctl.c:2510
-#: util/initctl.c:2521 util/initctl.c:2528
+#: util/initctl.c:2523 util/initctl.c:2535 util/initctl.c:2546
+#: util/initctl.c:2557 util/initctl.c:2564
msgid "JOB [KEY=VALUE]..."
msgstr ""
-#: util/initctl.c:2488
+#: util/initctl.c:2524
msgid "Start job."
msgstr ""
-#: util/initctl.c:2489
+#: util/initctl.c:2525
msgid ""
"JOB is the name of the job that is to be started, this may be followed by "
"zero or more environment variables to be defined in the new job.\n"
@@ -722,11 +730,11 @@
"an existing instance is already running."
msgstr ""
-#: util/initctl.c:2500
+#: util/initctl.c:2536
msgid "Stop job."
msgstr ""
-#: util/initctl.c:2501
+#: util/initctl.c:2537
msgid ""
"JOB is the name of the job that is to be stopped, this may be followed by "
"zero or more environment variables to be passed to the job's pre-stop and "
@@ -736,11 +744,11 @@
"decide which of multiple instances will be stopped."
msgstr ""
-#: util/initctl.c:2511
+#: util/initctl.c:2547
msgid "Restart job."
msgstr ""
-#: util/initctl.c:2512
+#: util/initctl.c:2548
msgid ""
"JOB is the name of the job that is to be restarted, this may be followed by "
"zero or more environment variables to be defined in the job after "
@@ -750,66 +758,66 @@
"decide which of multiple instances will be restarted."
msgstr ""
-#: util/initctl.c:2522
+#: util/initctl.c:2558
msgid "Send HUP signal to job."
msgstr ""
-#: util/initctl.c:2523
+#: util/initctl.c:2559
msgid ""
"JOB is the name of the job that is to be sent the signal, this may be "
"followed by zero or more environment variables to distinguish between job "
"instances.\n"
msgstr ""
-#: util/initctl.c:2529
+#: util/initctl.c:2565
msgid "Query status of job."
msgstr ""
-#: util/initctl.c:2530
+#: util/initctl.c:2566
msgid ""
"JOB is the name of the job that is to be queried, this may be followed by "
"zero or more environment variables to distguish between job instances.\n"
msgstr ""
-#: util/initctl.c:2536
+#: util/initctl.c:2572
msgid "List known jobs."
msgstr ""
-#: util/initctl.c:2537
+#: util/initctl.c:2573
msgid "The known jobs and their current status will be output."
msgstr ""
-#: util/initctl.c:2540
+#: util/initctl.c:2576
msgid "EVENT [KEY=VALUE]..."
msgstr ""
-#: util/initctl.c:2541
+#: util/initctl.c:2577
msgid "Emit an event."
msgstr ""
-#: util/initctl.c:2542
+#: util/initctl.c:2578
msgid ""
"EVENT is the name of an event the init daemon should emit, this may be "
"followed by zero or more environment variables to be included in the event.\n"
msgstr ""
-#: util/initctl.c:2548
+#: util/initctl.c:2584
msgid "Reload the configuration of the init daemon."
msgstr ""
-#: util/initctl.c:2552
+#: util/initctl.c:2588
msgid "Request the version of the init daemon."
msgstr ""
-#: util/initctl.c:2555
+#: util/initctl.c:2591
msgid "[PRIORITY]"
msgstr ""
-#: util/initctl.c:2556
+#: util/initctl.c:2592
msgid "Change the minimum priority of log messages from the init daemon"
msgstr ""
-#: util/initctl.c:2558
+#: util/initctl.c:2594
msgid ""
"PRIORITY may be one of:\n"
" `debug' (messages useful for debugging upstart are logged, equivalent to --"
@@ -826,42 +834,52 @@
"Without arguments, this outputs the current log priority."
msgstr ""
-#: util/initctl.c:2575 util/initctl.c:2581
+#: util/initctl.c:2611 util/initctl.c:2617
msgid "[CONF]"
msgstr ""
-#: util/initctl.c:2576
+#: util/initctl.c:2612
msgid "Show emits, start on and stop on details for job configurations."
msgstr ""
-#: util/initctl.c:2577
+#: util/initctl.c:2613
msgid ""
"If CONF specified, show configuration details for single job configuration, "
"else show details for all jobs configurations.\n"
msgstr ""
-#: util/initctl.c:2582
+#: util/initctl.c:2618
msgid "Check for unreachable jobs/event conditions."
msgstr ""
-#: util/initctl.c:2583
+#: util/initctl.c:2619
msgid ""
"List all jobs and events which cannot be satisfied by currently available "
"job configuration files"
msgstr ""
-#: util/initctl.c:2587
+#: util/initctl.c:2623
msgid "JOB"
msgstr ""
-#: util/initctl.c:2588
+#: util/initctl.c:2624
msgid "Show job usage message if available."
msgstr ""
-#: util/initctl.c:2589
+#: util/initctl.c:2625
msgid "JOB is the name of the job which usage is to be shown.\n"
msgstr ""
+#: util/initctl.c:2629
+msgid "Inform Upstart that disk is now writeable."
+msgstr ""
+
+#: util/initctl.c:2630
+msgid ""
+"Run to ensure output from jobs ending before disk is writeable are flushed "
+"to disk"
+msgstr ""
+
#: util/reboot.c:113
msgid "don't sync before reboot or halt"
msgstr ""
=== modified file 'util/initctl.c'
--- util/initctl.c 2012-02-16 15:55:57 +0000
+++ util/initctl.c 2012-03-16 21:02:13 +0000
@@ -112,20 +112,20 @@
#endif
/* Prototypes for option and command functions */
-int start_action (NihCommand *command, char * const *args);
-int stop_action (NihCommand *command, char * const *args);
-int restart_action (NihCommand *command, char * const *args);
-int reload_action (NihCommand *command, char * const *args);
-int status_action (NihCommand *command, char * const *args);
-int list_action (NihCommand *command, char * const *args);
-int emit_action (NihCommand *command, char * const *args);
-int reload_configuration_action (NihCommand *command, char * const *args);
-int version_action (NihCommand *command, char * const *args);
-int log_priority_action (NihCommand *command, char * const *args);
-int show_config_action (NihCommand *command, char * const *args);
-int check_config_action (NihCommand *command, char * const *args);
-int usage_action (NihCommand *command, char * const *args);
-
+int start_action (NihCommand *command, char * const *args);
+int stop_action (NihCommand *command, char * const *args);
+int restart_action (NihCommand *command, char * const *args);
+int reload_action (NihCommand *command, char * const *args);
+int status_action (NihCommand *command, char * const *args);
+int list_action (NihCommand *command, char * const *args);
+int emit_action (NihCommand *command, char * const *args);
+int reload_configuration_action (NihCommand *command, char * const *args);
+int version_action (NihCommand *command, char * const *args);
+int log_priority_action (NihCommand *command, char * const *args);
+int show_config_action (NihCommand *command, char * const *args);
+int check_config_action (NihCommand *command, char * const *args);
+int usage_action (NihCommand *command, char * const *args);
+int notify_disk_writeable_action (NihCommand *command, char * const *args);
/**
* use_dbus:
@@ -1643,6 +1643,42 @@
return ret ? 1 : 0;
}
+/**
+ * notify_disk_writeable_action:
+ * @command: NihCommand invoked,
+ * @args: command-line arguments.
+ *
+ * This function is called for the "notify-disk-writeable" command.
+ *
+ * Returns: command exit status.
+ **/
+int
+notify_disk_writeable_action (NihCommand *command,
+ char * const *args)
+{
+ nih_local NihDBusProxy *upstart = NULL;
+ NihError * err;
+
+ nih_assert (command != NULL);
+ nih_assert (args != NULL);
+
+ upstart = upstart_open (NULL);
+ if (! upstart)
+ return 1;
+
+ if (upstart_notify_disk_writeable_sync (NULL, upstart) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ err = nih_error_get ();
+ nih_error ("%s", err->message);
+ nih_free (err);
+
+ return 1;
+}
+
static void
start_reply_handler (char ** job_path,
NihDBusMessage *message,
@@ -2589,6 +2625,11 @@
N_("JOB is the name of the job which usage is to be shown.\n" ),
NULL, usage_options, usage_action },
+ { "notify-disk-writeable", NULL,
+ N_("Inform Upstart that disk is now writeable."),
+ N_("Run to ensure output from jobs ending before "
+ "disk is writeable are flushed to disk"),
+ NULL, NULL, notify_disk_writeable_action },
NIH_COMMAND_LAST
};
=== modified file 'util/man/initctl.8'
--- util/man/initctl.8 2012-02-16 15:45:41 +0000
+++ util/man/initctl.8 2012-03-16 21:02:13 +0000
@@ -510,7 +510,21 @@
\fBstarting\fP(7)) are automatically ignored.
.IP "\fB-w\fP, \fB\-\-warn\fP"
If specified, treat \fIany\fP unknown jobs and events as errors.
-
+.RE
+.\"
+.TP
+.B notify\-disk\-writeable
+Notify the
+.BR init (8)
+daemon that the disk is now writeable. This currently causes the
+.BR init (8)
+daemon to flush its internal cache of \(aqearly job\(aq output data.
+An early job is any job which
+.I finishes
+before the log disk becomes writeable. If job logging is not disabled,
+this command should be called once the log disk becomes writeable
+to ensure that output from all early jobs is flushed. If the data is
+written successfully to disk, the internal cache is deleted.
.RE
.\"
.TP
@@ -537,6 +551,8 @@
.SH AUTHOR
Written by Scott James Remnant
.RB < [email protected] >
+and James Hunt
+.RB < [email protected] > .
.\"
.SH REPORTING BUGS
Report bugs at
=== modified file 'util/tests/test_initctl.c'
--- util/tests/test_initctl.c 2012-02-16 15:45:41 +0000
+++ util/tests/test_initctl.c 2012-03-16 21:02:13 +0000
@@ -123,12 +123,12 @@
assert (pid); \
\
if (kill (pid, 0) == 0) { \
- kill (pid, SIGTERM); \
+ TEST_EQ (kill (pid, SIGTERM), 0); \
sleep (1); \
} \
\
if (kill (pid, 0) == 0) { \
- kill (pid, SIGKILL); \
+ TEST_EQ (kill (pid, SIGKILL), 0); \
} \
}
@@ -169,7 +169,7 @@
TEST_NE_P (ret, NULL); \
} \
\
- TEST_NE ( pclose (f), -1); \
+ TEST_NE (pclose (f), -1); \
}
/**
@@ -11386,6 +11386,7 @@
STOP_UPSTART (upstart_pid);
TEST_EQ (unsetenv ("UPSTART_CONFDIR"), 0);
TEST_DBUS_END (dbus_pid);
+ TEST_EQ (rmdir (dirname), 0);
}
void
@@ -11842,6 +11843,122 @@
STOP_UPSTART (upstart_pid);
TEST_EQ (unsetenv ("UPSTART_CONFDIR"), 0);
TEST_DBUS_END (dbus_pid);
+ TEST_EQ (rmdir (dirname), 0);
+}
+
+void
+test_notify_disk_writeable (void)
+{
+ char confdir_name[PATH_MAX];
+ char logdir_name[PATH_MAX];
+ nih_local char *logfile_name;
+ pid_t upstart_pid = 0;
+ pid_t dbus_pid;
+ nih_local char *cmd;
+ char **output;
+ size_t lines;
+ struct stat statbuf;
+ mode_t old_perms;
+ FILE *file;
+
+ TEST_FILENAME (confdir_name);
+ TEST_EQ (mkdir (confdir_name, 0755), 0);
+
+ TEST_FILENAME (logdir_name);
+ TEST_EQ (mkdir (logdir_name, 0755), 0);
+
+ TEST_EQ (stat (logdir_name, &statbuf), 0);
+ old_perms = statbuf.st_mode;
+
+ /* Make inaccessible */
+ TEST_EQ (chmod (logdir_name, 0x0), 0);
+
+ /* Use the "secret" interfaces */
+ TEST_EQ (setenv ("UPSTART_CONFDIR", confdir_name, 1), 0);
+ TEST_EQ (setenv ("UPSTART_LOGDIR", logdir_name, 1), 0);
+
+ TEST_FUNCTION ("notify-disk-writeable");
+
+ TEST_FEATURE ("with job ending before log disk writeable");
+
+ CREATE_FILE (confdir_name, "foo.conf",
+ "console log\n"
+ "exec echo hello world\n");
+
+ logfile_name = NIH_MUST (nih_sprintf (NULL, "%s/%s",
+ logdir_name,
+ "foo.log"));
+
+ TEST_DBUS (dbus_pid);
+ START_UPSTART (upstart_pid);
+
+ cmd = nih_sprintf (NULL, "%s start %s 2>&1",
+ INITCTL_BINARY, "foo");
+ TEST_NE_P (cmd, NULL);
+
+ RUN_COMMAND (NULL, cmd, &output, &lines);
+ TEST_EQ (lines, 1);
+
+ /* Give Upstart a chance to respond */
+ {
+ int i = 0;
+ int max = 5;
+ int ret;
+
+ for (i=0; i < max; ++i) {
+ nih_free (output);
+ cmd = nih_sprintf (NULL, "%s status %s 2>&1",
+ INITCTL_BINARY, "foo");
+ TEST_NE_P (cmd, NULL);
+
+ RUN_COMMAND (NULL, cmd, &output, &lines);
+ TEST_EQ (lines, 1);
+
+ ret = fnmatch ("foo stop/waiting", output[0], 0);
+
+ if (! ret) {
+ break;
+ }
+
+ sleep (1);
+ }
+ }
+
+ TEST_EQ (fnmatch ("foo stop/waiting", output[0], 0), 0);
+
+ /* Ensure no log file written */
+ TEST_LT (stat (logfile_name, &statbuf), 0);
+
+ /* Restore access */
+ TEST_EQ (chmod (logdir_name, old_perms), 0);
+
+ /* Ensure again that no log file written */
+ TEST_LT (stat (logfile_name, &statbuf), 0);
+
+ cmd = nih_sprintf (NULL, "%s notify-disk-writeable 2>&1", INITCTL_BINARY);
+ TEST_NE_P (cmd, NULL);
+ RUN_COMMAND (NULL, cmd, &output, &lines);
+ TEST_EQ (lines, 0);
+
+ /* Ensure file written now */
+ TEST_EQ (stat (logfile_name, &statbuf), 0);
+
+ file = fopen (logfile_name, "r");
+ TEST_NE_P (file, NULL);
+ TEST_FILE_EQ (file, "hello world\r\n");
+ TEST_FILE_END (file);
+ TEST_EQ (fclose (file), 0);
+
+ STOP_UPSTART (upstart_pid);
+ TEST_EQ (unsetenv ("UPSTART_CONFDIR"), 0);
+ TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0);
+ TEST_DBUS_END (dbus_pid);
+
+ DELETE_FILE (confdir_name, "foo.conf");
+ DELETE_FILE (logdir_name, "foo.log");
+
+ TEST_EQ (rmdir (confdir_name), 0);
+ TEST_EQ (rmdir (logdir_name), 0);
}
@@ -14569,13 +14686,14 @@
if (in_chroot () && !dbus_configured ()) {
fprintf(stderr, "\n\n"
- "WARNING: not running show-config "
- "and check-config tests within chroot "
+ "WARNING: not running show-config, "
+ "check-config & notify-disk-writeable tests within chroot "
"as no D-Bus, or D-Bus not configured (lp:#728988)"
"\n\n");
} else {
test_show_config ();
test_check_config ();
+ test_notify_disk_writeable ();
}
return 0;
--
upstart-devel mailing list
[email protected]
Modify settings or unsubscribe at:
https://lists.ubuntu.com/mailman/listinfo/upstart-devel