diff -r -c postgresql-8.4.orig/src/backend/access/transam/xlog.c postgresql-8.4/src/backend/access/transam/xlog.c
*** postgresql-8.4.orig/src/backend/access/transam/xlog.c	2008-04-22 13:40:25.000000000 +0200
--- postgresql-8.4/src/backend/access/transam/xlog.c	2008-04-22 13:48:17.000000000 +0200
***************
*** 6577,6582 ****
--- 6577,6583 ----
   * create a backup history file in pg_xlog (whence it will immediately be
   * archived).  The backup history file contains the same info found in
   * the label file, plus the backup-end time and WAL location.
+  * Note: different from CancelBackup which just cancels online backup mode.
   */
  Datum
  pg_stop_backup(PG_FUNCTION_ARGS)
***************
*** 7063,7065 ****
--- 7064,7110 ----
  
  	pfree(buf.data);
  }
+ 
+ /*
+  * BackupInProgress: check if online backup mode is active
+  *
+  * This is done by checking for existence of the "backup_label" file.
+  */
+ bool
+ BackupInProgress(void)
+ {
+ 	struct stat stat_buf;
+ 
+ 	return (stat(BACKUP_LABEL_FILE, &stat_buf) < 0) ? false : true;
+ }
+ 
+ /*
+  * CancelBackup: rename the "backup_label" file to cancel backup mode
+  *
+  * If the "backup_label" file exists, it will be renamed to "backup_label.old".
+  * Note that this will render an online backup in progress useless.
+  * To correctly finish an online backup, pg_stop_backup must be called.
+  */
+ void
+ CancelBackup(void)
+ {
+ 	struct stat stat_buf;
+ 
+ 	/* if the file is not there, return */
+ 	if (stat(BACKUP_LABEL_FILE, &stat_buf) < 0)
+ 		return;
+ 
+ 	unlink(BACKUP_LABEL_OLD);
+ 	if (rename(BACKUP_LABEL_FILE, BACKUP_LABEL_OLD) == 0)
+ 	{
+ 		ereport(LOG,
+ 			(errmsg("online backup mode cancelled, \"%s\" renamed to \"%s\"",
+ 				BACKUP_LABEL_FILE, BACKUP_LABEL_OLD)));
+ 	}
+ 	else
+ 	{
+ 		ereport(LOG,
+ 			(errmsg("system error %d renaming \"%s\" to \"%s\", backup mode not cancelled",
+ 				errno, BACKUP_LABEL_FILE, BACKUP_LABEL_OLD)));
+ 	}
+ }
diff -r -c postgresql-8.4.orig/src/backend/postmaster/postmaster.c postgresql-8.4/src/backend/postmaster/postmaster.c
*** postgresql-8.4.orig/src/backend/postmaster/postmaster.c	2008-04-01 15:18:52.000000000 +0200
--- postgresql-8.4/src/backend/postmaster/postmaster.c	2008-04-22 13:48:17.000000000 +0200
***************
*** 253,258 ****
--- 253,259 ----
  	PM_INIT,					/* postmaster starting */
  	PM_STARTUP,					/* waiting for startup subprocess */
  	PM_RUN,						/* normal "database is alive" state */
+ 	PM_WAIT_BACKUP,				/* waiting for online backup mode to end */
  	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 */
***************
*** 1724,1731 ****
  static enum CAC_state
  canAcceptConnections(void)
  {
! 	/* Can't start backends when in startup/shutdown/recovery state. */
! 	if (pmState != PM_RUN)
  	{
  		if (Shutdown > NoShutdown)
  			return CAC_SHUTDOWN;	/* shutdown is pending */
--- 1725,1736 ----
  static enum CAC_state
  canAcceptConnections(void)
  {
! 	/*
! 	 * Can't start backends when in startup/shutdown/recovery state.
! 	 * In state PM_WAIT_BACKUP we must allow connections so that
! 	 * a superuser can end online backup mode.
! 	 */
! 	if ((pmState != PM_RUN) && (pmState != PM_WAIT_BACKUP))
  	{
  		if (Shutdown > NoShutdown)
  			return CAC_SHUTDOWN;	/* shutdown is pending */
***************
*** 1965,1975 ****
  				/* and the walwriter too */
  				if (WalWriterPID != 0)
  					signal_child(WalWriterPID, SIGTERM);
! 				pmState = PM_WAIT_BACKENDS;
  			}
  
  			/*
! 			 * Now wait for backends to exit.  If there are none,
  			 * PostmasterStateMachine will take the next step.
  			 */
  			PostmasterStateMachine();
--- 1970,1981 ----
  				/* and the walwriter too */
  				if (WalWriterPID != 0)
  					signal_child(WalWriterPID, SIGTERM);
! 				pmState = PM_WAIT_BACKUP;
  			}
  
  			/*
! 			 * Now wait for online backup mode to end and
! 			 * backends to exit.  If that is already the case,
  			 * PostmasterStateMachine will take the next step.
  			 */
  			PostmasterStateMachine();
***************
*** 2011,2016 ****
--- 2017,2029 ----
  			 * PostmasterStateMachine will take the next step.
  			 */
  			PostmasterStateMachine();
+ 
+ 			/*
+ 			 * terminate backup mode to avoid recovery after a
+ 			 * clean fast shutdown.
+ 			 */
+ 			CancelBackup();
+ 
  			break;
  
  		case SIGQUIT:
***************
*** 2552,2557 ****
--- 2565,2584 ----
  static void
  PostmasterStateMachine(void)
  {
+ 	if (pmState == PM_WAIT_BACKUP)
+ 	{
+ 		/*
+ 		 * PM_WAIT_BACKUP state ends when online backup mode is no longer
+ 		 * active.  In this state canAcceptConnections() will still allow
+ 		 * client connections, which is necessary because a superuser
+ 		 * has to call pg_stop_backup() to end online backup mode.
+ 		 */
+ 		if (!BackupInProgress())
+ 		{
+ 			pmState = PM_WAIT_BACKENDS;
+ 		}
+ 	}
+ 
  	/*
  	 * If we are in a state-machine state that implies waiting for backends to
  	 * exit, see if they're all gone, and change state if so.
diff -r -c postgresql-8.4.orig/src/bin/pg_ctl/pg_ctl.c postgresql-8.4/src/bin/pg_ctl/pg_ctl.c
*** postgresql-8.4.orig/src/bin/pg_ctl/pg_ctl.c	2008-04-01 15:18:55.000000000 +0200
--- postgresql-8.4/src/bin/pg_ctl/pg_ctl.c	2008-04-22 13:48:17.000000000 +0200
***************
*** 144,149 ****
--- 144,150 ----
  static char postopts_file[MAXPGPATH];
  static char pid_file[MAXPGPATH];
  static char conf_file[MAXPGPATH];
+ static char backup_file[MAXPGPATH];
  
  #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
  static void unlimit_core_size(void);
***************
*** 731,736 ****
--- 732,738 ----
  {
  	int			cnt;
  	pgpid_t		pid;
+ 	struct stat	statbuf;
  
  	pid = get_pgpid();
  
***************
*** 763,768 ****
--- 765,776 ----
  	}
  	else
  	{
+ 		if ((shutdown_mode == SMART_MODE) && (stat(backup_file, &statbuf) == 0))
+ 		{
+ 			print_msg(_("WARNING: online backup mode is active; must be ended\n"
+ 						"   with pg_stop_backup() for shutdown to complete\n\n"));
+ 		}
+ 
  		print_msg(_("waiting for server to shut down..."));
  
  		for (cnt = 0; cnt < wait_seconds; cnt++)
***************
*** 799,804 ****
--- 807,813 ----
  {
  	int			cnt;
  	pgpid_t		pid;
+ 	struct stat	statbuf;
  
  	pid = get_pgpid();
  
***************
*** 833,838 ****
--- 842,853 ----
  			exit(1);
  		}
  
+ 		if ((shutdown_mode == SMART_MODE) && (stat(backup_file, &statbuf) == 0))
+ 		{
+ 			print_msg(_("WARNING: online backup mode is active; must be ended\n"
+ 						"   with pg_stop_backup() for shutdown to complete\n\n"));
+ 		}
+ 
  		print_msg(_("waiting for server to shut down..."));
  
  		/* always wait for restart */
***************
*** 1883,1888 ****
--- 1898,1904 ----
  		snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data);
  		snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
  		snprintf(conf_file, MAXPGPATH, "%s/postgresql.conf", pg_data);
+ 		snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
  	}
  
  	switch (ctl_command)
diff -r -c postgresql-8.4.orig/src/include/miscadmin.h postgresql-8.4/src/include/miscadmin.h
*** postgresql-8.4.orig/src/include/miscadmin.h	2008-04-01 15:18:56.000000000 +0200
--- postgresql-8.4/src/include/miscadmin.h	2008-04-22 13:48:17.000000000 +0200
***************
*** 330,333 ****
--- 330,337 ----
  extern void process_shared_preload_libraries(void);
  extern void process_local_preload_libraries(void);
  
+ /* in access/transam/xlog.c */
+ extern bool BackupInProgress(void);
+ extern void CancelBackup(void);
+ 
  #endif   /* MISCADMIN_H */
