Index: src/backend/postmaster/pgarch.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/postmaster/pgarch.c,v
retrieving revision 1.37
diff -c -r1.37 pgarch.c
*** src/backend/postmaster/pgarch.c	1 Jan 2008 19:45:51 -0000	1.37
--- src/backend/postmaster/pgarch.c	10 Jan 2008 18:59:54 -0000
***************
*** 77,88 ****
--- 77,91 ----
   * ----------
   */
  static time_t last_pgarch_start_time;
+ static time_t last_sigterm_time = 0;
  
  /*
   * Flags set by interrupt handlers for later service in the main loop.
   */
  static volatile sig_atomic_t got_SIGHUP = false;
+ static volatile sig_atomic_t got_SIGTERM = false;
  static volatile sig_atomic_t wakened = false;
+ static volatile sig_atomic_t ready_to_stop = false;
  
  /* ----------
   * Local function forward declarations
***************
*** 95,101 ****
--- 98,106 ----
  NON_EXEC_STATIC void PgArchiverMain(int argc, char *argv[]);
  static void pgarch_exit(SIGNAL_ARGS);
  static void ArchSigHupHandler(SIGNAL_ARGS);
+ static void ArchSigTermHandler(SIGNAL_ARGS);
  static void pgarch_waken(SIGNAL_ARGS);
+ static void pgarch_waken_stop(SIGNAL_ARGS);
  static void pgarch_MainLoop(void);
  static void pgarch_ArchiverCopyLoop(void);
  static bool pgarch_archiveXlog(char *xlog);
***************
*** 236,251 ****
  
  	/*
  	 * Ignore all signals usually bound to some action in the postmaster,
! 	 * except for SIGHUP, SIGUSR1 and SIGQUIT.
  	 */
  	pqsignal(SIGHUP, ArchSigHupHandler);
  	pqsignal(SIGINT, SIG_IGN);
! 	pqsignal(SIGTERM, SIG_IGN);
  	pqsignal(SIGQUIT, pgarch_exit);
  	pqsignal(SIGALRM, SIG_IGN);
  	pqsignal(SIGPIPE, SIG_IGN);
  	pqsignal(SIGUSR1, pgarch_waken);
! 	pqsignal(SIGUSR2, SIG_IGN);
  	pqsignal(SIGCHLD, SIG_DFL);
  	pqsignal(SIGTTIN, SIG_DFL);
  	pqsignal(SIGTTOU, SIG_DFL);
--- 241,256 ----
  
  	/*
  	 * Ignore all signals usually bound to some action in the postmaster,
! 	 * except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT.
  	 */
  	pqsignal(SIGHUP, ArchSigHupHandler);
  	pqsignal(SIGINT, SIG_IGN);
! 	pqsignal(SIGTERM, ArchSigTermHandler);
  	pqsignal(SIGQUIT, pgarch_exit);
  	pqsignal(SIGALRM, SIG_IGN);
  	pqsignal(SIGPIPE, SIG_IGN);
  	pqsignal(SIGUSR1, pgarch_waken);
! 	pqsignal(SIGUSR2, pgarch_waken_stop);
  	pqsignal(SIGCHLD, SIG_DFL);
  	pqsignal(SIGTTIN, SIG_DFL);
  	pqsignal(SIGTTOU, SIG_DFL);
***************
*** 267,294 ****
  static void
  pgarch_exit(SIGNAL_ARGS)
  {
! 	/*
! 	 * For now, we just nail the doors shut and get out of town.  It might
! 	 * seem cleaner to finish up any pending archive copies, but there's a
! 	 * nontrivial risk that init will kill us partway through.
! 	 */
! 	exit(0);
  }
  
! /* SIGHUP: set flag to re-read config file at next convenient time */
  static void
  ArchSigHupHandler(SIGNAL_ARGS)
  {
  	got_SIGHUP = true;
  }
  
  /* SIGUSR1 signal handler for archiver process */
  static void
  pgarch_waken(SIGNAL_ARGS)
  {
  	wakened = true;
  }
  
  /*
   * pgarch_MainLoop
   *
--- 272,318 ----
  static void
  pgarch_exit(SIGNAL_ARGS)
  {
! 	/* SIGQUIT means curl up and die ... */
! 	exit(1);
  }
  
! /* SIGHUP signal handler for archiver process */
  static void
  ArchSigHupHandler(SIGNAL_ARGS)
  {
+ 	/* set flag to re-read config file at next convenient time */
  	got_SIGHUP = true;
  }
  
+ /* SIGTERM signal handler for archiver process */
+ static void
+ ArchSigTermHandler(SIGNAL_ARGS)
+ {
+ 	/*
+ 	 * The postmaster never sends us SIGTERM, so we assume that this means
+ 	 * that init is trying to shut down the whole system.  If we hang around
+ 	 * too long we'll get SIGKILL'd.  Set flag to prevent starting any more
+ 	 * archive commands.
+ 	 */
+ 	got_SIGTERM = true;
+ }
+ 
  /* SIGUSR1 signal handler for archiver process */
  static void
  pgarch_waken(SIGNAL_ARGS)
  {
+ 	/* set flag that there is work to be done */
  	wakened = true;
  }
  
+ /* SIGUSR2 signal handler for archiver process */
+ static void
+ pgarch_waken_stop(SIGNAL_ARGS)
+ {
+ 	/* set flag to do a final cycle and shut down afterwards */
+ 	ready_to_stop = true;
+ }
+ 
  /*
   * pgarch_MainLoop
   *
***************
*** 298,303 ****
--- 322,328 ----
  pgarch_MainLoop(void)
  {
  	time_t		last_copy_time = 0;
+ 	bool		time_to_stop;
  
  	/*
  	 * We run the copy loop immediately upon entry, in case there are
***************
*** 309,314 ****
--- 334,342 ----
  
  	do
  	{
+ 		/* When we get SIGUSR2, we do one more archive cycle, then exit */
+ 		time_to_stop = ready_to_stop;
+ 
  		/* Check for config update */
  		if (got_SIGHUP)
  		{
***************
*** 316,323 ****
  			ProcessConfigFile(PGC_SIGHUP);
  		}
  
  		/* Do what we're here for */
! 		if (wakened)
  		{
  			wakened = false;
  			pgarch_ArchiverCopyLoop();
--- 344,369 ----
  			ProcessConfigFile(PGC_SIGHUP);
  		}
  
+ 		/*
+ 		 * If we've gotten SIGTERM, we normally just sit and do nothing until
+ 		 * SIGUSR2 arrives.  However, that means a random SIGTERM would
+ 		 * disable archiving indefinitely, which doesn't seem like a good
+ 		 * idea.  If more than 60 seconds pass since SIGTERM, exit anyway,
+ 		 * so that the postmaster can start a new archiver if needed.
+ 		 */
+ 		if (got_SIGTERM)
+ 		{
+ 			time_t		curtime = time(NULL);
+ 
+ 			if (last_sigterm_time == 0)
+ 				last_sigterm_time = curtime;
+ 			else if ((unsigned int) (curtime - last_sigterm_time) >=
+ 					 (unsigned int) 60)
+ 				break;
+ 		}
+ 
  		/* Do what we're here for */
! 		if (wakened || time_to_stop)
  		{
  			wakened = false;
  			pgarch_ArchiverCopyLoop();
***************
*** 334,340 ****
  		 * sleep into 1-second increments, and check for interrupts after each
  		 * nap.
  		 */
! 		while (!(wakened || got_SIGHUP))
  		{
  			time_t		curtime;
  
--- 380,387 ----
  		 * sleep into 1-second increments, and check for interrupts after each
  		 * nap.
  		 */
! 		while (!(wakened || ready_to_stop || got_SIGHUP ||
! 				 !PostmasterIsAlive(true)))
  		{
  			time_t		curtime;
  
***************
*** 344,350 ****
  				(unsigned int) PGARCH_AUTOWAKE_INTERVAL)
  				wakened = true;
  		}
! 	} while (PostmasterIsAlive(true));
  }
  
  /*
--- 391,403 ----
  				(unsigned int) PGARCH_AUTOWAKE_INTERVAL)
  				wakened = true;
  		}
! 
! 		/*
! 		 * The archiver quits either when the postmaster dies (not expected)
! 		 * or after completing one more archiving cycle after receiving
! 		 * SIGUSR2.
! 		 */
! 	} while (PostmasterIsAlive(true) && !time_to_stop);
  }
  
  /*
***************
*** 377,384 ****
  
  		for (;;)
  		{
! 			/* Abandon processing if we notice our postmaster has died */
! 			if (!PostmasterIsAlive(true))
  				return;
  
  			if (pgarch_archiveXlog(xlog))
--- 430,443 ----
  
  		for (;;)
  		{
! 			/*
! 			 * Do not initiate any more archive commands after receiving
! 			 * SIGTERM, nor after the postmaster has died unexpectedly.
! 			 * The first condition is to try to keep from having init
! 			 * SIGKILL the command, and the second is to avoid conflicts
! 			 * with another archiver spawned by a newer postmaster.
! 			 */
! 			if (got_SIGTERM || !PostmasterIsAlive(true))
  				return;
  
  			if (pgarch_archiveXlog(xlog))
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.550
diff -c -r1.550 postmaster.c
*** src/backend/postmaster/postmaster.c	1 Jan 2008 19:45:51 -0000	1.550
--- src/backend/postmaster/postmaster.c	10 Jan 2008 18:59:55 -0000
***************
*** 244,250 ****
   * Notice that this state variable does not distinguish *why* we entered
   * PM_WAIT_BACKENDS or later states --- Shutdown and FatalError must be
   * consulted to find that out.	FatalError is never true in PM_RUN state, nor
!  * in PM_SHUTDOWN state (because we don't enter that state when trying to
   * recover from a crash).  It can be true in PM_STARTUP state, because we
   * don't clear it until we've successfully recovered.
   */
--- 244,250 ----
   * Notice that this state variable does not distinguish *why* we entered
   * PM_WAIT_BACKENDS or later states --- Shutdown and FatalError must be
   * consulted to find that out.	FatalError is never true in PM_RUN state, nor
!  * in PM_SHUTDOWN states (because we don't enter those states when trying to
   * recover from a crash).  It can be true in PM_STARTUP state, because we
   * don't clear it until we've successfully recovered.
   */
***************
*** 255,260 ****
--- 255,261 ----
  	PM_RUN,						/* normal "database is alive" state */
  	PM_WAIT_BACKENDS,			/* waiting for live backends to exit */
  	PM_SHUTDOWN,				/* waiting for bgwriter to do shutdown ckpt */
+ 	PM_SHUTDOWN_2,				/* waiting for archiver to finish */
  	PM_WAIT_DEAD_END,			/* waiting for dead_end children to exit */
  	PM_NO_CHILDREN				/* all important children have exited */
  } PMState;
***************
*** 1312,1323 ****
  				start_autovac_launcher = false; /* signal processed */
  		}
  
! 		/*
! 		 * If we have lost the archiver, try to start a new one. We do this
! 		 * even if we are shutting down, to allow archiver to take care of any
! 		 * remaining WAL files.
! 		 */
! 		if (XLogArchivingActive() && PgArchPID == 0 && pmState >= PM_RUN)
  			PgArchPID = pgarch_start();
  
  		/* If we have lost the stats collector, try to start a new one */
--- 1313,1320 ----
  				start_autovac_launcher = false; /* signal processed */
  		}
  
! 		/* If we have lost the archiver, try to start a new one */
! 		if (XLogArchivingActive() && PgArchPID == 0 && pmState == PM_RUN)
  			PgArchPID = pgarch_start();
  
  		/* If we have lost the stats collector, try to start a new one */
***************
*** 2175,2186 ****
  				 * checkpoint.	(If for some reason it didn't, recovery will
  				 * occur on next postmaster start.)
  				 *
! 				 * At this point we should have no normal children left (else
! 				 * we'd not be in PM_SHUTDOWN state) but we might have
! 				 * dead_end children.
  				 */
  				Assert(Shutdown > NoShutdown);
! 				pmState = PM_WAIT_DEAD_END;
  			}
  			else
  			{
--- 2172,2202 ----
  				 * checkpoint.	(If for some reason it didn't, recovery will
  				 * occur on next postmaster start.)
  				 *
! 				 * At this point we should have no normal backend children
! 				 * left (else we'd not be in PM_SHUTDOWN state) but we might
! 				 * have dead_end children to wait for.
! 				 *
! 				 * If we have an archiver subprocess, tell it to do a last
! 				 * archive cycle and quit; otherwise we can go directly to
! 				 * PM_WAIT_DEAD_END state.
  				 */
  				Assert(Shutdown > NoShutdown);
! 
! 				if (PgArchPID != 0)
! 				{
! 					/* Waken archiver for the last time */
! 					signal_child(PgArchPID, SIGUSR2);
! 					pmState = PM_SHUTDOWN_2;
! 				}
! 				else
! 					pmState = PM_WAIT_DEAD_END;
! 
! 				/*
! 				 * We can also shut down the stats collector now; there's
! 				 * nothing left for it to do.
! 				 */
! 				if (PgStatPID != 0)
! 					signal_child(PgStatPID, SIGQUIT);
  			}
  			else
  			{
***************
*** 2227,2233 ****
  		/*
  		 * Was it the archiver?  If so, just try to start a new one; no need
  		 * to force reset of the rest of the system.  (If fail, we'll try
! 		 * again in future cycles of the main loop.)
  		 */
  		if (pid == PgArchPID)
  		{
--- 2243,2250 ----
  		/*
  		 * Was it the archiver?  If so, just try to start a new one; no need
  		 * to force reset of the rest of the system.  (If fail, we'll try
! 		 * again in future cycles of the main loop.)  But if we were waiting
! 		 * for it to shut down, advance to the next shutdown step.
  		 */
  		if (pid == PgArchPID)
  		{
***************
*** 2235,2242 ****
  			if (!EXIT_STATUS_0(exitstatus))
  				LogChildExit(LOG, _("archiver process"),
  							 pid, exitstatus);
! 			if (XLogArchivingActive() && pmState >= PM_RUN)
  				PgArchPID = pgarch_start();
  			continue;
  		}
  
--- 2252,2261 ----
  			if (!EXIT_STATUS_0(exitstatus))
  				LogChildExit(LOG, _("archiver process"),
  							 pid, exitstatus);
! 			if (XLogArchivingActive() && pmState == PM_RUN)
  				PgArchPID = pgarch_start();
+ 			else if (pmState == PM_SHUTDOWN_2)
+ 				pmState = PM_WAIT_DEAD_END;
  			continue;
  		}
  
***************
*** 2563,2568 ****
--- 2582,2592 ----
  				 * change causes ServerLoop to stop creating new ones.
  				 */
  				pmState = PM_WAIT_DEAD_END;
+ 
+ 				/*
+ 				 * We already SIGQUIT'd the archiver and stats processes,
+ 				 * if any, when we entered FatalError state.
+ 				 */
  			}
  			else
  			{
***************
*** 2591,2603 ****
  					 */
  					FatalError = true;
  					pmState = PM_WAIT_DEAD_END;
  				}
- 				/* Tell pgarch to shut down too; nothing left for it to do */
- 				if (PgArchPID != 0)
- 					signal_child(PgArchPID, SIGQUIT);
- 				/* Tell pgstat to shut down too; nothing left for it to do */
- 				if (PgStatPID != 0)
- 					signal_child(PgStatPID, SIGQUIT);
  			}
  		}
  	}
--- 2615,2627 ----
  					 */
  					FatalError = true;
  					pmState = PM_WAIT_DEAD_END;
+ 
+ 					/* Kill the archiver and stats collector too */
+ 					if (PgArchPID != 0)
+ 						signal_child(PgArchPID, SIGQUIT);
+ 					if (PgStatPID != 0)
+ 						signal_child(PgStatPID, SIGQUIT);
  				}
  			}
  		}
  	}
***************
*** 2606,2621 ****
  	{
  		/*
  		 * PM_WAIT_DEAD_END state ends when the BackendList is entirely empty
! 		 * (ie, no dead_end children remain).
  		 */
! 		if (!DLGetHead(BackendList))
  		{
  			/* These other guys should be dead already */
  			Assert(StartupPID == 0);
  			Assert(BgWriterPID == 0);
  			Assert(WalWriterPID == 0);
  			Assert(AutoVacPID == 0);
! 			/* archiver, stats, and syslogger are not considered here */
  			pmState = PM_NO_CHILDREN;
  		}
  	}
--- 2630,2655 ----
  	{
  		/*
  		 * PM_WAIT_DEAD_END state ends when the BackendList is entirely empty
! 		 * (ie, no dead_end children remain), and the archiver and stats
! 		 * collector are gone too.
! 		 *
! 		 * The reason we wait for those two is to protect them against a new
! 		 * postmaster starting conflicting subprocesses; this isn't an
! 		 * ironclad protection, but it at least helps in the
! 		 * shutdown-and-immediately-restart scenario.  Note that they have
! 		 * already been sent appropriate shutdown signals, either during a
! 		 * normal state transition leading up to PM_WAIT_DEAD_END, or during
! 		 * FatalError processing.
  		 */
! 		if (DLGetHead(BackendList) == NULL &&
! 			PgArchPID == 0 && PgStatPID == 0)
  		{
  			/* These other guys should be dead already */
  			Assert(StartupPID == 0);
  			Assert(BgWriterPID == 0);
  			Assert(WalWriterPID == 0);
  			Assert(AutoVacPID == 0);
! 			/* syslogger is not considered here */
  			pmState = PM_NO_CHILDREN;
  		}
  	}
***************
*** 2628,2641 ****
  	 * we got SIGTERM from init --- there may well not be time for recovery
  	 * before init decides to SIGKILL us.)
  	 *
! 	 * Note: we do not wait around for exit of the archiver or stats
! 	 * processes.  They've been sent SIGQUIT by this point (either when we
! 	 * entered PM_SHUTDOWN state, or when we set FatalError, and at least one
! 	 * of those must have happened by now).  In any case they contain logic to
! 	 * commit hara-kiri if they notice the postmaster is gone.	Since they
! 	 * aren't connected to shared memory, they pose no problem for shutdown.
! 	 * The syslogger is not considered either, since it's intended to survive
! 	 * till the postmaster exits.
  	 */
  	if (Shutdown > NoShutdown && pmState == PM_NO_CHILDREN)
  	{
--- 2662,2670 ----
  	 * we got SIGTERM from init --- there may well not be time for recovery
  	 * before init decides to SIGKILL us.)
  	 *
! 	 * Note that the syslogger continues to run.  It will exit when it sees
! 	 * EOF on its input pipe, which happens when there are no more upstream
! 	 * processes.
  	 */
  	if (Shutdown > NoShutdown && pmState == PM_NO_CHILDREN)
  	{
***************
*** 2652,2661 ****
  	}
  
  	/*
! 	 * If we need to recover from a crash, wait for all shmem-connected
! 	 * children to exit, then reset shmem and StartupDataBase.	(We can ignore
! 	 * the archiver and stats processes here since they are not connected to
! 	 * shmem.)
  	 */
  	if (FatalError && pmState == PM_NO_CHILDREN)
  	{
--- 2681,2688 ----
  	}
  
  	/*
! 	 * If we need to recover from a crash, wait for all non-syslogger
! 	 * children to exit, then reset shmem and StartupDataBase.
  	 */
  	if (FatalError && pmState == PM_NO_CHILDREN)
  	{
***************
*** 3782,3788 ****
  	}
  
  	if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) &&
! 		PgArchPID != 0 && Shutdown <= SmartShutdown)
  	{
  		/*
  		 * Send SIGUSR1 to archiver process, to wake it up and begin archiving
--- 3809,3815 ----
  	}
  
  	if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) &&
! 		PgArchPID != 0)
  	{
  		/*
  		 * Send SIGUSR1 to archiver process, to wake it up and begin archiving
