Merge authors:
  James Hunt (jamesodhunt)
Related merge proposals:
  
https://code.launchpad.net/~jamesodhunt/upstart/bug-1447756-the-actual-fix/+merge/259020
  proposed by: James Hunt (jamesodhunt)
------------------------------------------------------------
revno: 1666 [merge]
committer: Steve Langasek <steve.langa...@canonical.com>
branch nick: upstream
timestamp: Thu 2015-05-14 10:30:42 -0700
message:
  Merge lp:~jamesodhunt/upstart/bug-1447756-the-actual-fix
modified:
  ChangeLog
  init/log.c
  init/tests/test_log.c


--
lp:upstart
https://code.launchpad.net/~upstart-devel/upstart/trunk

Your team Upstart Reviewers is subscribed to branch lp:upstart.
To unsubscribe from this branch go to 
https://code.launchpad.net/~upstart-devel/upstart/trunk/+edit-subscription
=== modified file 'ChangeLog'
--- ChangeLog	2015-04-24 12:57:48 +0000
+++ ChangeLog	2015-05-12 15:46:47 +0000
@@ -1,3 +1,21 @@
+2015-05-12  James Hunt  <james.h...@ubuntu.com>
+
+	* init/log.c:
+	  - log_clear_unflushed(): Handle logs on the unflushed list which
+	    get flushed automatically by their corresponding NihIo before
+	    a request to clear the unflushed list is made. This scenario
+	    is triggered when a job which has spawned processes exits, but
+	    where the spawned processes continue to run and then produce
+	    output before the request to clear the unflushed list is made
+	    (at the time the log partition becomes writable) but after the
+	    log partition becomes writable (LP: #1447756).
+	* init/tests/test_log.c:
+	  - test_log_new(): Added new test:
+	    "ensure logger unflushed list ignores already flushed data".
+	  - test_log_destroy(): Improved test
+	     "ensure unflushed data moved to unflushed list with uid 0" by giving 
+	    the log a parent and checking parent handling behaviour.
+
 2015-04-24  James Hunt  <james.h...@ubuntu.com>
 
 	* init/log.c:

=== modified file 'init/log.c'
--- init/log.c	2015-04-24 12:57:48 +0000
+++ init/log.c	2015-05-14 17:30:42 +0000
@@ -802,6 +802,48 @@
 
 		nih_assert (log);
 
+		if (! log->unflushed->len) {
+			/* The job that originally owned this log has
+			 * exited, but it spawned one or more other
+			 * processes which still live on. If those
+			 * processes produce output, the NihIo will get
+			 * called automatically to flush the data.
+			 * However, that happens asynchronously to
+			 * clearing the unflushed list.
+			 *
+			 * Hence, if no unflushed data remains, remove
+			 * the entry from the list.
+			 *
+			 * Note that doing so frees the log and stops
+			 * any further output being logged from the
+			 * out-of-job process(es). This isn't ideal
+			 * however:
+			 *
+			 * - Better this than having the Log objects remain indefinitely.
+			 *
+			 * - The problem is ultimately caused by a rogue
+			 *   job which should be fixed (see the warning
+			 *   that is produced for such jobs in
+			 *   log_read_watch()).
+			 *
+			 * - Even if the Log was retained, it would not
+			 *   be flushed unless multiple calls to this
+			 *   function are made (which is not going to
+			 *   occur by default).
+			 *
+			 * Note that it is necessary to set the io to
+			 * NULL since it has already been freed by
+			 * nih_io_closed(), but the io is not set to
+			 * NULL. If we don't do this, when log_destroy()
+			 * is called, it will attempt to dereference the 
+			 * already freed log->io.
+			 */
+			log->io = NULL;
+			nih_free (elem);
+
+			continue;
+		}
+
 		/* To be added to this list, log should have been
 		 * detached from its parent job.
 		 */

=== modified file 'init/tests/test_log.c'
--- init/tests/test_log.c	2015-04-24 12:57:48 +0000
+++ init/tests/test_log.c	2015-05-12 15:46:47 +0000
@@ -654,6 +654,94 @@
 	TEST_FREE (log);
 
 	/************************************************************/
+	TEST_FEATURE ("ensure logger unflushed list ignores already flushed data");
+
+	/* Reset */
+	log_flushed = 0;
+
+	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);
+
+	/* Write data to simulate a job process, but _DON'T_ close the
+	 * fd (to simulate the job spawning another process which olds
+	 * the fd open).
+	 */
+	ret = write (pty_slave, str, strlen (str));
+	TEST_GT (ret, 0);
+	ret = write (pty_slave, "\n", 1);
+	TEST_EQ (ret, 1);
+
+	assert0 (log->unflushed->len);
+	TEST_EQ_P (log->unflushed->buf, NULL);
+
+	TEST_NE_P (log->io, NULL);
+
+	TEST_WATCH_UPDATE ();
+
+	/* nih_io_closed() should not have been called */
+	TEST_NE_P (log->io, NULL);
+
+	TEST_EQ_STR (log->unflushed->buf, "hello, world!\r\n");
+
+	/* +2 for '\r\n' */
+	TEST_EQ (log->unflushed->len, 2 + strlen (str));
+
+	/* Ensure no log file written */
+	TEST_LT (stat (filename, &statbuf), 0);
+
+	TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
+
+	TEST_FREE_TAG (log);
+
+	/* Force the usual code path for early boot on a normal system
+	 * when a job produces output before the disks are writable.
+	 */
+	log->open_errno = EROFS;
+
+	TEST_EQ (log_handle_unflushed (NULL, log), 0);
+
+	TEST_FALSE (NIH_LIST_EMPTY (log_unflushed_files));
+
+	/* Restore access */
+	TEST_EQ (chmod (dirname, old_perms), 0);
+
+	TEST_NE_P (log->io, NULL);
+
+	/* Simulate the data having already been flushed to disk
+	 * and nih_io_closed() having been called.
+	 */
+	log->io = NULL;
+	log->unflushed->len = 0;
+	log->unflushed->buf = NULL;
+	log->remote_closed = 1;
+
+	ret = log_clear_unflushed ();
+	TEST_EQ (ret, 0);
+
+	TEST_FREE (log);
+
+	/* Ensure no log file written */
+	TEST_LT (stat (filename, &statbuf), 0);
+
+	/* Tidy up */
+	close (pty_slave);
+	
+	/************************************************************/
 	TEST_FEATURE ("ensure logger flushes when destroyed with uid 0");
 
 	TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
@@ -1143,6 +1231,7 @@
 	int   fd;
 	NihListEntry *entry;
 	struct stat  statbuf;
+	char  *p;
 
 	TEST_FUNCTION ("log_destroy");
 
@@ -1227,9 +1316,14 @@
 	TEST_NE (fd, -1);
 	close (fd);
 
-	log = log_new (NULL, filename, pty_master, 0);
+	p = nih_strdup (NULL, "hello");
+	TEST_NE_P (p, NULL);
+
+	log = log_new (p, filename, pty_master, 0);
 	TEST_NE_P (log, NULL);
 
+	TEST_ALLOC_PARENT (log, p);
+
 	ret = write (pty_slave, str, strlen (str));
 	TEST_GT (ret, 0);
 
@@ -1244,16 +1338,26 @@
 	log_flushed = 0;
 
 	TEST_TRUE (NIH_LIST_EMPTY (log_unflushed_files));
-	ret = log_handle_unflushed (NULL, log);
-
+	ret = log_handle_unflushed (p, log);
 	TEST_EQ (ret, 0);
+
 	TEST_FALSE (NIH_LIST_EMPTY (log_unflushed_files));
 
+	TEST_ALLOC_NOT_PARENT (log, p);
+
 	entry = (NihListEntry *)log_unflushed_files->next;
 	TEST_NE_P (entry, NULL);
 	TEST_FREE_TAG (entry);
 	TEST_ALLOC_PARENT (entry, log_unflushed_files);
 
+	/* Free the original parent object and ensure it doesn't free
+	 * the log.
+	 */
+	nih_free (p);
+
+	TEST_FALSE (NIH_LIST_EMPTY (log_unflushed_files));
+	TEST_NOT_FREE (log);
+
 	tmp_log = entry->data;
 	TEST_EQ_P (tmp_log, log);
 	TEST_ALLOC_PARENT (log, entry);

-- 
upstart-devel mailing list
upstart-devel@lists.ubuntu.com
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/upstart-devel

Reply via email to