* worker and dealt with just by having the worker exit normally.  The launcher
  * will launch a new worker again later, per schedule.
  *
- * When the worker is done vacuuming it sends SIGUSR1 to the launcher. The
+ * When the worker is done vacuuming it sends SIGUSR2 to the launcher. The
  * launcher then wakes up and is able to launch another worker, if the schedule
  * is so tight that a new worker is needed immediately.  At this time the
  * launcher can also balance the settings for the various remaining workers'
 #include <time.h>
 #include <unistd.h>
 
-#include "access/genam.h"
 #include "access/heapam.h"
 #include "access/reloptions.h"
 #include "access/transam.h"
 #include "access/xact.h"
 #include "catalog/dependency.h"
-#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_database.h"
 #include "commands/dbcommands.h"
 #include "commands/vacuum.h"
-#include "libpq/hba.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
-#include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
-#include "storage/procarray.h"
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "tcop/tcopprot.h"
-#include "utils/dynahash.h"
-#include "utils/flatfiles.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
+#include "utils/snapmgr.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
 
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
-static volatile sig_atomic_t got_SIGUSR1 = false;
+static volatile sig_atomic_t got_SIGUSR2 = false;
 static volatile sig_atomic_t got_SIGTERM = false;
 
 /* Comparison point for determining whether freeze_max_age is exceeded */
                                                  PgStat_StatDBEntry *dbentry);
 static void autovac_report_activity(autovac_table *tab);
 static void avl_sighup_handler(SIGNAL_ARGS);
-static void avl_sigusr1_handler(SIGNAL_ARGS);
+static void avl_sigusr2_handler(SIGNAL_ARGS);
 static void avl_sigterm_handler(SIGNAL_ARGS);
-static void avl_quickdie(SIGNAL_ARGS);
 static void autovac_refresh_stats(void);
 
 
        /* Identify myself via ps */
        init_ps_display("autovacuum launcher process", "", "", "");
 
+       ereport(LOG,
+                       (errmsg("autovacuum launcher started")));
+
        if (PostAuthDelay)
                pg_usleep(PostAuthDelay * 1000000L);
 
 #endif
 
        /*
-        * Set up signal handlers.      Since this is an auxiliary process, it has
-        * particular signal requirements -- no deadlock checker or sinval
-        * catchup, for example.
+        * Set up signal handlers.      We operate on databases much like a regular
+        * backend, so we use the same signal handling.  See equivalent code in
+        * tcop/postgres.c.
         */
        pqsignal(SIGHUP, avl_sighup_handler);
-
-       pqsignal(SIGINT, SIG_IGN);
+       pqsignal(SIGINT, StatementCancelHandler);
        pqsignal(SIGTERM, avl_sigterm_handler);
-       pqsignal(SIGQUIT, avl_quickdie);
-       pqsignal(SIGALRM, SIG_IGN);
+
+       pqsignal(SIGQUIT, quickdie);
+       pqsignal(SIGALRM, handle_sig_alarm);
 
        pqsignal(SIGPIPE, SIG_IGN);
-       pqsignal(SIGUSR1, avl_sigusr1_handler);
-       pqsignal(SIGUSR2, SIG_IGN);
+       pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+       pqsignal(SIGUSR2, avl_sigusr2_handler);
        pqsignal(SIGFPE, FloatExceptionHandler);
        pqsignal(SIGCHLD, SIG_DFL);
 
         * had to do some stuff with LWLocks).
         */
 #ifndef EXEC_BACKEND
-       InitAuxiliaryProcess();
+       InitProcess();
 #endif
 
+       InitPostgres(NULL, InvalidOid, NULL, NULL);
+
+       SetProcessingMode(NormalProcessing);
+
        /*
         * Create a memory context that we will do all our work in.  We do this so
         * that we can reset the context during error recovery and thereby avoid
                                                                                  ALLOCSET_DEFAULT_MAXSIZE);
        MemoryContextSwitchTo(AutovacMemCxt);
 
-
        /*
         * If an exception is encountered, processing resumes here.
         *
-        * This code is heavily based on bgwriter.c, q.v.
+        * This code is a stripped down version of PostgresMain error recovery.
         */
        if (sigsetjmp(local_sigjmp_buf, 1) != 0)
        {
                /* Prevents interrupts while cleaning up */
                HOLD_INTERRUPTS();
 
+               /* Forget any pending QueryCancel request */
+               QueryCancelPending = false;
+               disable_sig_alarm(true);
+               QueryCancelPending = false;             /* again in case timeout occurred */
+
                /* Report the error to the server log */
                EmitErrorReport();
 
-               /*
-                * These operations are really just a minimal subset of
-                * AbortTransaction().  We don't have very many resources to worry
-                * about, but we do have LWLocks.
-                */
-               LWLockReleaseAll();
-               AtEOXact_Files();
-               AtEOXact_HashTables(false);
+               /* Abort the current transaction in order to recover */
+               AbortCurrentTransaction();
 
                /*
                 * Now return to normal top-level context and clear ErrorContext for
        /* We can now handle ereport(ERROR) */
        PG_exception_stack = &local_sigjmp_buf;
 
-       ereport(LOG,
-                       (errmsg("autovacuum launcher started")));
-
        /* must unblock signals before calling rebuild_database_list */
        PG_SETMASK(&UnBlockSig);
 
                 * necessity for manual cleanup of all postmaster children.
                 */
                if (!PostmasterIsAlive(true))
-                       exit(1);
+                       proc_exit(1);
 
                launcher_determine_sleep((AutoVacuumShmem->av_freeWorkers != NULL),
                                                                 false, &nap);
 
+               /* Allow sinval catchup interrupts while sleeping */
+               EnableCatchupInterrupt();
+
                /*
                 * Sleep for a while according to schedule.
                 *
                         * necessity for manual cleanup of all postmaster children.
                         */
                        if (!PostmasterIsAlive(true))
-                               exit(1);
+                               proc_exit(1);
 
-                       if (got_SIGTERM || got_SIGHUP || got_SIGUSR1)
+                       if (got_SIGTERM || got_SIGHUP || got_SIGUSR2)
                                break;
                }
 
+               DisableCatchupInterrupt();
+
                /* the normal shutdown case */
                if (got_SIGTERM)
                        break;
                        got_SIGHUP = false;
                        ProcessConfigFile(PGC_SIGHUP);
 
-                       /* shutdown requested in config file */
+                       /* shutdown requested in config file? */
                        if (!AutoVacuumingActive())
                                break;
 
                 * a worker finished, or postmaster signalled failure to start a
                 * worker
                 */
-               if (got_SIGUSR1)
+               if (got_SIGUSR2)
                {
-                       got_SIGUSR1 = false;
+                       got_SIGUSR2 = false;
 
                        /* rebalance cost limits, if needed */
                        if (AutoVacuumShmem->av_signal[AutoVacRebalance])
 
 /*
  * Called from postmaster to signal a failure to fork a process to become
- * worker.     The postmaster should kill(SIGUSR1) the launcher shortly
+ * worker.     The postmaster should kill(SIGUSR2) the launcher shortly
  * after calling this function.
  */
 void
        got_SIGHUP = true;
 }
 
-/* SIGUSR1: a worker is up and running, or just finished, or failed to fork */
+/* SIGUSR2: a worker is up and running, or just finished, or failed to fork */
 static void
-avl_sigusr1_handler(SIGNAL_ARGS)
+avl_sigusr2_handler(SIGNAL_ARGS)
 {
-       got_SIGUSR1 = true;
+       got_SIGUSR2 = true;
 }
 
 /* SIGTERM: time to die */
        got_SIGTERM = true;
 }
 
-/*
- * avl_quickdie occurs when signalled SIGQUIT from postmaster.
- *
- * Some backend has bought the farm, so we need to stop what we're doing
- * and exit.
- */
-static void
-avl_quickdie(SIGNAL_ARGS)
-{
-       PG_SETMASK(&BlockSig);
-
-       /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
-       /*
-        * Note we do exit(2) not exit(0).      This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
-        * backend.  This is necessary precisely because we don't clean up our
-        * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
-        * should ensure the postmaster sees this as a crash, too, but no harm in
-        * being doubly sure.)
-        */
-       exit(2);
-}
-
 
 /********************************************************************
  *                                       AUTOVACUUM WORKER CODE
 
                /* wake up the launcher */
                if (AutoVacuumShmem->av_launcherpid != 0)
-                       kill(AutoVacuumShmem->av_launcherpid, SIGUSR1);
+                       kill(AutoVacuumShmem->av_launcherpid, SIGUSR2);
        }
        else
        {
 
 /*
  * get_database_list
+ *             Return a list of all databases found in pg_database.
  *
- *             Return a list of all databases.  Note we cannot use pg_database,
- *             because we aren't connected; we use the flat database file.
+ * Note: this is the only function in which the autovacuum launcher uses a
+ * transaction.  Although we aren't attached to any particular database and
+ * therefore can't access most catalogs, we do have enough infrastructure
+ * to do a seqscan on pg_database.
  */
 static List *
 get_database_list(void)
 {
-       char       *filename;
        List       *dblist = NIL;
-       char            thisname[NAMEDATALEN];
-       FILE       *db_file;
-       Oid                     db_id;
-       Oid                     db_tablespace;
-       TransactionId db_frozenxid;
-
-       filename = database_getflatfilename();
-       db_file = AllocateFile(filename, "r");
-       if (db_file == NULL)
-               ereport(FATAL,
-                               (errcode_for_file_access(),
-                                errmsg("could not open file \"%s\": %m", filename)));
+       Relation        rel;
+       HeapScanDesc scan;
+       HeapTuple       tup;
+
+       /*
+        * Start a transaction so we can access pg_database, and get a snapshot.
+        * We don't have a use for the snapshot itself, but we're interested in
+        * the secondary effect that it sets RecentGlobalXmin.  (This is critical
+        * for anything that reads heap pages, because HOT may decide to prune
+        * them even if the process doesn't attempt to modify any tuples.)
+        */
+       StartTransactionCommand();
+       (void) GetTransactionSnapshot();
+
+       /* Allocate our results in AutovacMemCxt, not transaction context */
+       MemoryContextSwitchTo(AutovacMemCxt);
 
-       while (read_pg_database_line(db_file, thisname, &db_id,
-                                                                &db_tablespace, &db_frozenxid))
+       rel = heap_open(DatabaseRelationId, AccessShareLock);
+       scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+       while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
        {
-               avw_dbase  *avdb;
+               Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
+               avw_dbase   *avdb;
 
                avdb = (avw_dbase *) palloc(sizeof(avw_dbase));
 
-               avdb->adw_datid = db_id;
-               avdb->adw_name = pstrdup(thisname);
-               avdb->adw_frozenxid = db_frozenxid;
+               avdb->adw_datid = HeapTupleGetOid(tup);
+               avdb->adw_name = pstrdup(NameStr(pgdatabase->datname));
+               avdb->adw_frozenxid = pgdatabase->datfrozenxid;
                /* this gets set later: */
                avdb->adw_entry = NULL;
 
                dblist = lappend(dblist, avdb);
        }
 
-       FreeFile(db_file);
-       pfree(filename);
+       heap_endscan(scan);
+       heap_close(rel, AccessShareLock);
+
+       CommitTransactionCommand();
 
        return dblist;
 }
 
        size = add_size(size, sizeof(PROC_HDR));
        /* AuxiliaryProcs */
        size = add_size(size, mul_size(NUM_AUXILIARY_PROCS, sizeof(PGPROC)));
-       /* MyProcs, including autovacuum */
+       /* MyProcs, including autovacuum workers and launcher */
        size = add_size(size, mul_size(MaxBackends, sizeof(PGPROC)));
        /* ProcStructLock */
        size = add_size(size, sizeof(slock_t));
                ProcGlobal->freeProcs = &procs[i];
        }
 
-       procs = (PGPROC *) ShmemAlloc((autovacuum_max_workers) * sizeof(PGPROC));
+       /*
+        * Likewise for the PGPROCs reserved for autovacuum.
+        *
+        * Note: the "+1" here accounts for the autovac launcher
+        */
+       procs = (PGPROC *) ShmemAlloc((autovacuum_max_workers + 1) * sizeof(PGPROC));
        if (!procs)
                ereport(FATAL,
                                (errcode(ERRCODE_OUT_OF_MEMORY),
                                 errmsg("out of shared memory")));
-       MemSet(procs, 0, autovacuum_max_workers * sizeof(PGPROC));
-       for (i = 0; i < autovacuum_max_workers; i++)
+       MemSet(procs, 0, (autovacuum_max_workers + 1) * sizeof(PGPROC));
+       for (i = 0; i < autovacuum_max_workers + 1; i++)
        {
                PGSemaphoreCreate(&(procs[i].sem));
                procs[i].links.next = (SHM_QUEUE *) ProcGlobal->autovacFreeProcs;
                ProcGlobal->autovacFreeProcs = &procs[i];
        }
 
+       /*
+        * And auxiliary procs.
+        */
        MemSet(AuxiliaryProcs, 0, NUM_AUXILIARY_PROCS * sizeof(PGPROC));
        for (i = 0; i < NUM_AUXILIARY_PROCS; i++)
        {
 
        set_spins_per_delay(procglobal->spins_per_delay);
 
-       if (IsAutoVacuumWorkerProcess())
+       if (IsAnyAutoVacuumProcess())
                MyProc = procglobal->autovacFreeProcs;
        else
                MyProc = procglobal->freeProcs;
 
        if (MyProc != NULL)
        {
-               if (IsAutoVacuumWorkerProcess())
+               if (IsAnyAutoVacuumProcess())
                        procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
                else
                        procglobal->freeProcs = (PGPROC *) MyProc->links.next;
        /*
         * Now that we have a PGPROC, mark ourselves as an active postmaster
         * child; this is so that the postmaster can detect it if we exit without
-        * cleaning up.
+        * cleaning up.  (XXX autovac launcher currently doesn't participate in
+        * this; it probably should.)
         */
-       if (IsUnderPostmaster)
+       if (IsUnderPostmaster && !IsAutoVacuumLauncherProcess())
                MarkPostmasterChildActive();
 
        /*
        MyProc->roleId = InvalidOid;
        MyProc->inCommit = false;
        MyProc->vacuumFlags = 0;
+       /* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */
        if (IsAutoVacuumWorkerProcess())
                MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
        MyProc->lwWaiting = false;
        MyProc->databaseId = InvalidOid;
        MyProc->roleId = InvalidOid;
        MyProc->inCommit = false;
-       /* we don't set the "is autovacuum" flag in the launcher */
        MyProc->vacuumFlags = 0;
        MyProc->lwWaiting = false;
        MyProc->lwExclusive = false;
 
        SpinLockAcquire(ProcStructLock);
 
-       /* Return PGPROC structure (and semaphore) to freelist */
-       if (IsAutoVacuumWorkerProcess())
+       /* Return PGPROC structure (and semaphore) to appropriate freelist */
+       if (IsAnyAutoVacuumProcess())
        {
                MyProc->links.next = (SHM_QUEUE *) procglobal->autovacFreeProcs;
                procglobal->autovacFreeProcs = MyProc;
        /*
         * This process is no longer present in shared memory in any meaningful
         * way, so tell the postmaster we've cleaned up acceptably well.
+        * (XXX autovac launcher should be included here someday)
         */
-       if (IsUnderPostmaster)
+       if (IsUnderPostmaster && !IsAutoVacuumLauncherProcess())
                MarkPostmasterChildInactive();
 
        /* wake autovac launcher if needed -- see comments in FreeWorkerInfo */
        if (AutovacuumLauncherPid != 0)
-               kill(AutovacuumLauncherPid, SIGUSR1);
+               kill(AutovacuumLauncherPid, SIGUSR2);
 }
 
 /*