* message to setup a backend process.
*
* The postmaster also manages system-wide operations such as
- * startup, shutdown, and periodic checkpoints. The postmaster
- * itself doesn't do those operations, mind you --- it just forks
- * off a subprocess to do them at the right times. It also takes
- * care of resetting the system if a backend crashes.
+ * startup and shutdown. The postmaster itself doesn't do those
+ * operations, mind you --- it just forks off a subprocess to do them
+ * at the right times. It also takes care of resetting the system
+ * if a backend crashes.
*
* The postmaster process creates the shared memory and semaphore
* pools during startup, but as a rule does not touch them itself.
* clients.
*
*
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.391 2004/05/19 18:58:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.445 2005/02/22 04:36:36 momjian Exp $
*
* NOTES
*
* Initialization:
- * The Postmaster sets up a few shared memory data structures
- * for the backends. It should at the very least initialize the
- * lock manager.
+ * The Postmaster sets up shared memory data structures
+ * for the backends.
*
* Synchronization:
* The Postmaster shares memory with the backends but should avoid
* The Postmaster cleans up after backends if they have an emergency
* exit and/or core dump.
*
+ * Error Reporting:
+ * Use write_stderr() only for reporting "interactive" errors
+ * (essentially, bogus arguments on the command line). Once the
+ * postmaster is launched, use ereport(). In particular, don't use
+ * write_stderr() for anything that occurs after pmdaemonize.
+ *
*-------------------------------------------------------------------------
*/
#include <unistd.h>
#include <signal.h>
+#include <time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
-#include <sys/time.h>
#include <sys/socket.h>
-#include <errno.h>
#include <fcntl.h>
-#include <time.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
#endif
+#include "catalog/pg_control.h"
#include "catalog/pg_database.h"
#include "commands/async.h"
#include "lib/dllist.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "nodes/nodes.h"
+#include "postmaster/postmaster.h"
+#include "postmaster/pgarch.h"
+#include "postmaster/syslogger.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "storage/bufmgr.h"
#include "access/xlog.h"
#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "bootstrap/bootstrap.h"
#include "pgstat.h"
-
-#define INVALID_SOCK (-1)
-
-#ifdef HAVE_SIGPROCMASK
-sigset_t UnBlockSig,
- BlockSig,
- AuthBlockSig;
-
-#else
-int UnBlockSig,
- BlockSig,
- AuthBlockSig;
+#ifdef EXEC_BACKEND
+#include "storage/spin.h"
#endif
+
/*
* List of active backends (or child processes anyway; we don't actually
* know whether a given child has become a backend or is still in the
* authorization phase). This is used mainly to keep track of how many
* children we have and send them appropriate signals when necessary.
+ *
+ * "Special" children such as the startup and bgwriter tasks are not in
+ * this list.
*/
typedef struct bkend
{
char *UnixSocketDir;
char *ListenAddresses;
-/*
- * MaxBackends is the limit on the number of backends we can start.
- * Note that a larger MaxBackends value will increase the size of the
- * shared memory area as well as cause the postmaster to grab more
- * kernel semaphores, even if you never actually use that many
- * backends.
- */
-int MaxBackends;
-
/*
* ReservedBackends is the number of backends reserved for superuser use.
* This number is taken out of the pool size given by MaxBackends so
static const char *progname = NULL;
/* The socket(s) we're listening to. */
-#define MAXLISTEN 10
+#define MAXLISTEN 64
static int ListenSocket[MAXLISTEN];
-/* Used to reduce macros tests */
-#ifdef EXEC_BACKEND
-const bool ExecBackend = true;
-#else
-const bool ExecBackend = false;
-#endif
-
/*
* Set by the -o option
*/
int PreAuthDelay = 0;
int AuthenticationTimeout = 60;
-int CheckPointTimeout = 300;
-int CheckPointWarning = 30;
-time_t LastSignalledCheckpoint = 0;
bool log_hostname; /* for ps display and logging */
bool Log_connections = false;
/* list of library:init-function to be preloaded */
char *preload_libraries_string = NULL;
-/* Startup/shutdown state */
+/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
- ShutdownPID = 0,
- CheckPointPID = 0,
- BgWriterPID = 0;
-static time_t checkpointed = 0;
+ BgWriterPID = 0,
+ PgArchPID = 0,
+ PgStatPID = 0,
+ SysLoggerPID = 0;
+/* Startup/shutdown state */
#define NoShutdown 0
#define SmartShutdown 1
#define FastShutdown 2
* Also, the global MyCancelKey passes the cancel key assigned to a given
* backend from the postmaster to that backend (via fork).
*/
-
static unsigned int random_seed = 0;
-static int debug_flag = 0;
-
extern char *optarg;
extern int optind,
opterr;
/*
* postmaster.c - function prototypes
*/
-static void pmdaemonize(int argc, char *argv[]);
+static void checkDataDir(void);
+
+#ifdef USE_RENDEZVOUS
+static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode,
+ void *context);
+#endif
+static void pmdaemonize(void);
static Port *ConnCreate(int serverFd);
static void ConnFree(Port *port);
static void reset_shared(unsigned short port);
static void reaper(SIGNAL_ARGS);
static void sigusr1_handler(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
-static void CleanupProc(int pid, int exitstatus);
+static void CleanupBackend(int pid, int exitstatus);
+static void HandleChildCrash(int pid, int exitstatus, const char *procname);
static void LogChildExit(int lev, const char *procname,
int pid, int exitstatus);
-static void BackendInit(Port *port);
-static int BackendRun(Port *port);
+static int BackendRun(Port *port);
static void ExitPostmaster(int status);
static void usage(const char *);
static int ServerLoop(void);
static void SignalChildren(int signal);
static int CountChildren(void);
static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
-NON_EXEC_STATIC void SSDataBaseInit(int xlop);
-static pid_t SSDataBase(int xlop);
-static void
-postmaster_error(const char *fmt,...)
-/* This lets gcc check the format string for consistency. */
-__attribute__((format(printf, 1, 2)));
+static pid_t StartChildProcess(int xlop);
#ifdef EXEC_BACKEND
#ifdef WIN32
-pid_t win32_forkexec(const char* path, char *argv[]);
-
-static void win32_AddChild(pid_t pid, HANDLE handle);
-static void win32_RemoveChild(pid_t pid);
+static void win32_AddChild(pid_t pid, HANDLE handle);
+static void win32_RemoveChild(pid_t pid);
static pid_t win32_waitpid(int *exitstatus);
static DWORD WINAPI win32_sigchld_waiter(LPVOID param);
-static pid_t *win32_childPIDArray;
+static pid_t *win32_childPIDArray;
static HANDLE *win32_childHNDArray;
static unsigned long win32_numChildren = 0;
-#endif
-
-static pid_t Backend_forkexec(Port *port);
-static unsigned long tmpBackendFileNum = 0;
-void read_backend_variables(unsigned long id, Port *port);
-static bool write_backend_variables(Port *port);
-
-static void ShmemBackendArrayAdd(Backend *bn);
-static void ShmemBackendArrayRemove(pid_t pid);
+HANDLE PostmasterHandle;
#endif
-#define StartupDataBase() SSDataBase(BS_XLOG_STARTUP)
-#define CheckPointDataBase() SSDataBase(BS_XLOG_CHECKPOINT)
-#define StartBackgroundWriter() SSDataBase(BS_XLOG_BGWRITER)
-#define ShutdownDataBase() SSDataBase(BS_XLOG_SHUTDOWN)
+static pid_t backend_forkexec(Port *port);
+static pid_t internal_forkexec(int argc, char *argv[], Port *port);
-static void
-checkDataDir(const char *checkdir)
+/* Type for a socket that can be inherited to a client process */
+#ifdef WIN32
+typedef struct
{
- char path[MAXPGPATH];
- FILE *fp;
- struct stat stat_buf;
-
- if (checkdir == NULL)
- {
- fprintf(stderr,
- gettext("%s does not know where to find the database system data.\n"
- "You must specify the directory that contains the database system\n"
- "either by specifying the -D invocation option or by setting the\n"
- "PGDATA environment variable.\n"),
- progname);
- ExitPostmaster(2);
- }
-
- if (stat(checkdir, &stat_buf) == -1)
- {
- if (errno == ENOENT)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("data directory \"%s\" does not exist",
- checkdir)));
- else
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not read permissions of directory \"%s\": %m",
- checkdir)));
- }
-
- /*
- * Check if the directory has group or world access. If so, reject.
- *
- * XXX temporarily suppress check when on Windows, because there may not
- * be proper support for Unix-y file permissions. Need to think of a
- * reasonable check to apply on Windows.
- */
-#if !defined(__CYGWIN__) && !defined(WIN32)
- if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
- ereport(FATAL,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("data directory \"%s\" has group or world access",
- checkdir),
- errdetail("Permissions should be u=rwx (0700).")));
+ SOCKET origsocket; /* Original socket value, or -1 if not a socket */
+ WSAPROTOCOL_INFO wsainfo;
+} InheritableSocket;
+#else
+typedef int InheritableSocket;
#endif
- /* Look for PG_VERSION before looking for pg_control */
- ValidatePgVersion(checkdir);
+typedef struct LWLock LWLock; /* ugly kluge */
- snprintf(path, sizeof(path), "%s/global/pg_control", checkdir);
+/*
+ * Structure contains all variables passed to exec:ed backends
+ */
+typedef struct
+{
+ Port port;
+ InheritableSocket portsocket;
+ char DataDir[MAXPGPATH];
+ int ListenSocket[MAXLISTEN];
+ long MyCancelKey;
+ unsigned long UsedShmemSegID;
+ void *UsedShmemSegAddr;
+ slock_t *ShmemLock;
+ slock_t *ShmemIndexLock;
+ VariableCache ShmemVariableCache;
+ void *ShmemIndexAlloc;
+ Backend *ShmemBackendArray;
+ LWLock *LWLockArray;
+ slock_t *ProcStructLock;
+ InheritableSocket pgStatSock;
+ InheritableSocket pgStatPipe0;
+ InheritableSocket pgStatPipe1;
+ pid_t PostmasterPid;
+#ifdef WIN32
+ HANDLE PostmasterHandle;
+ HANDLE initial_signal_pipe;
+ HANDLE syslogPipe[2];
+#else
+ int syslogPipe[2];
+#endif
+ char my_exec_path[MAXPGPATH];
+ char ExtraOptions[MAXPGPATH];
+ char lc_collate[LOCALE_NAME_BUFLEN];
+ char lc_ctype[LOCALE_NAME_BUFLEN];
+} BackendParameters;
- fp = AllocateFile(path, PG_BINARY_R);
- if (fp == NULL)
- {
- fprintf(stderr,
- gettext("%s: could not find the database system\n"
- "Expected to find it in the directory \"%s\",\n"
- "but could not open file \"%s\": %s\n"),
- progname, checkdir, path, strerror(errno));
- ExitPostmaster(2);
- }
- FreeFile(fp);
-}
+static void read_backend_variables(char *id, Port *port);
+static void restore_backend_variables(BackendParameters *param, Port *port);
+#ifndef WIN32
+static bool save_backend_variables(BackendParameters *param, Port *port);
+#else
+static bool save_backend_variables(BackendParameters *param, Port *port,
+ HANDLE childProcess, pid_t childPid);
+#endif
+static void ShmemBackendArrayAdd(Backend *bn);
+static void ShmemBackendArrayRemove(pid_t pid);
-#ifdef USE_RENDEZVOUS
+#endif /* EXEC_BACKEND */
-/* reg_reply -- empty callback function for DNSServiceRegistrationCreate() */
-static void
-reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
-{
+#define StartupDataBase() StartChildProcess(BS_XLOG_STARTUP)
+#define StartBackgroundWriter() StartChildProcess(BS_XLOG_BGWRITER)
-}
-#endif
+/*
+ * Postmaster main entry point
+ */
int
PostmasterMain(int argc, char *argv[])
{
int opt;
int status;
- char original_extraoptions[MAXPGPATH];
- char *potential_DataDir = NULL;
+ char *userDoption = NULL;
int i;
- *original_extraoptions = '\0';
+ /* This will call exit() if strdup() fails. */
+ progname = get_progname(argv[0]);
- progname = get_progname(argv[0]);
+ MyProcPid = PostmasterPid = getpid();
IsPostmasterEnvironment = true;
}
}
+#ifdef WIN32
+ /* Start our win32 signal implementation */
+ pgwin32_signal_initialize();
+#endif
+
/*
* for security, no dir or file created can be group or other
* accessible
*/
umask((mode_t) 0077);
- MyProcPid = PostmasterPid = getpid();
-
/*
* Fire up essential subsystems: memory management
*/
IgnoreSystemIndexes(false);
if (find_my_exec(argv[0], my_exec_path) < 0)
- elog(FATAL,
- gettext("%s: could not locate my own executable path"),
- argv[0]);
-
+ elog(FATAL, "%s: could not locate my own executable path",
+ argv[0]);
+
+ get_pkglib_path(my_exec_path, pkglib_path);
+
/*
* Options setup
*/
InitializeGUCOptions();
- potential_DataDir = getenv("PGDATA"); /* default value */
-
opterr = 1;
while ((opt = getopt(argc, argv, "A:a:B:b:c:D:d:Fh:ik:lm:MN:no:p:Ss-:")) != -1)
#ifdef USE_ASSERT_CHECKING
SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
#else
- postmaster_error("assert checking is not compiled in");
+ write_stderr("%s: assert checking is not compiled in\n", progname);
#endif
break;
case 'a':
/* Can no longer set the backend executable file to use. */
break;
case 'D':
- potential_DataDir = optarg;
+ userDoption = optarg;
break;
case 'd':
- {
- /* Turn on debugging for the postmaster. */
- char *debugstr = palloc(strlen("debug") + strlen(optarg) + 1);
-
- sprintf(debugstr, "debug%s", optarg);
- SetConfigOption("log_min_messages", debugstr,
- PGC_POSTMASTER, PGC_S_ARGV);
- pfree(debugstr);
- debug_flag = atoi(optarg);
- break;
- }
+ set_debug_options(atoi(optarg), PGC_POSTMASTER, PGC_S_ARGV);
+ break;
case 'F':
SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV);
break;
/*
* Other options to pass to the backend on the command
- * line -- useful only for debugging.
+ * line
*/
- strcat(ExtraOptions, " ");
- strcat(ExtraOptions, optarg);
- strcpy(original_extraoptions, optarg);
+ snprintf(ExtraOptions + strlen(ExtraOptions),
+ sizeof(ExtraOptions) - strlen(ExtraOptions),
+ " %s", optarg);
break;
case 'p':
SetConfigOption("port", optarg, PGC_POSTMASTER, PGC_S_ARGV);
}
default:
- fprintf(stderr,
- gettext("Try \"%s --help\" for more information.\n"),
- progname);
+ write_stderr("Try \"%s --help\" for more information.\n",
+ progname);
ExitPostmaster(1);
}
}
*/
if (optind < argc)
{
- postmaster_error("invalid argument: \"%s\"", argv[optind]);
- fprintf(stderr,
- gettext("Try \"%s --help\" for more information.\n"),
- progname);
+ write_stderr("%s: invalid argument: \"%s\"\n",
+ progname, argv[optind]);
+ write_stderr("Try \"%s --help\" for more information.\n",
+ progname);
ExitPostmaster(1);
}
/*
- * Now we can set the data directory, and then read postgresql.conf.
+ * Locate the proper configuration files and data directory, and
+ * read postgresql.conf for the first time.
*/
- checkDataDir(potential_DataDir); /* issues error messages */
- SetDataDir(potential_DataDir);
+ if (!SelectConfigFiles(userDoption, progname))
+ ExitPostmaster(2);
- ProcessConfigFile(PGC_POSTMASTER);
-#ifdef EXEC_BACKEND
- write_nondefault_variables(PGC_POSTMASTER);
-#endif
+ /* Verify that DataDir looks reasonable */
+ checkDataDir();
/*
* Check for invalid combinations of GUC settings.
* for lack of buffers. The specific choices here are somewhat
* arbitrary.
*/
- postmaster_error("the number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16");
+ write_stderr("%s: the number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16\n", progname);
ExitPostmaster(1);
}
if (ReservedBackends >= MaxBackends)
{
- postmaster_error("superuser_reserved_connections must be less than max_connections");
+ write_stderr("%s: superuser_reserved_connections must be less than max_connections\n", progname);
ExitPostmaster(1);
}
*/
if (!CheckDateTokenTables())
{
- postmaster_error("invalid datetoken tables, please fix");
+ write_stderr("%s: invalid datetoken tables, please fix\n", progname);
ExitPostmaster(1);
}
char **p;
ereport(DEBUG3,
- (errmsg_internal("%s: PostmasterMain: initial environ dump:",
- progname)));
+ (errmsg_internal("%s: PostmasterMain: initial environ dump:",
+ progname)));
ereport(DEBUG3,
- (errmsg_internal("-----------------------------------------")));
+ (errmsg_internal("-----------------------------------------")));
for (p = environ; *p; ++p)
ereport(DEBUG3,
(errmsg_internal("\t%s", *p)));
ereport(DEBUG3,
- (errmsg_internal("-----------------------------------------")));
+ (errmsg_internal("-----------------------------------------")));
}
#ifdef EXEC_BACKEND
- if (find_other_exec(argv[0], "postgres", PG_VERSIONSTR, postgres_exec_path) < 0)
+ if (find_other_exec(argv[0], "postgres", PG_VERSIONSTR,
+ postgres_exec_path) < 0)
ereport(FATAL,
- (errmsg("%s: could not locate postgres executable or non-matching version",
- progname)));
+ (errmsg("%s: could not locate matching postgres executable",
+ progname)));
#endif
-
+
/*
* Initialize SSL library, if specified.
*/
* will show the wrong PID.
*/
if (SilentMode)
- pmdaemonize(argc, argv);
+ pmdaemonize();
/*
* Create lockfile for data directory.
* We want to do this before we try to grab the input sockets, because
* the data directory interlock is more reliable than the socket-file
* interlock (thanks to whoever decided to put socket files in /tmp
- * :-(). For the same reason, it's best to grab the TCP socket before
- * the Unix socket.
+ * :-(). For the same reason, it's best to grab the TCP socket(s)
+ * before the Unix socket.
*/
CreateDataDirLockFile(DataDir, true);
if (ListenAddresses)
{
- char *curhost,
- *endptr;
- char c;
+ char *rawstring;
+ List *elemlist;
+ ListCell *l;
- curhost = ListenAddresses;
- for (;;)
+ /* Need a modifiable copy of ListenAddresses */
+ rawstring = pstrdup(ListenAddresses);
+
+ /* Parse string into list of identifiers */
+ if (!SplitIdentifierString(rawstring, ',', &elemlist))
{
- /* ignore whitespace */
- while (isspace((unsigned char) *curhost))
- curhost++;
- if (*curhost == '\0')
- break;
- endptr = curhost;
- while (*endptr != '\0' && !isspace((unsigned char) *endptr))
- endptr++;
- c = *endptr;
- *endptr = '\0';
- if (strcmp(curhost,"*") == 0)
+ /* syntax error in list */
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid list syntax for \"listen_addresses\"")));
+ }
+
+ foreach(l, elemlist)
+ {
+ char *curhost = (char *) lfirst(l);
+
+ if (strcmp(curhost, "*") == 0)
status = StreamServerPort(AF_UNSPEC, NULL,
(unsigned short) PostPortNumber,
UnixSocketDir,
ListenSocket, MAXLISTEN);
if (status != STATUS_OK)
ereport(WARNING,
- (errmsg("could not create listen socket for \"%s\"",
- curhost)));
- *endptr = c;
- if (c != '\0')
- curhost = endptr+1;
- else
- break;
+ (errmsg("could not create listen socket for \"%s\"",
+ curhost)));
}
+
+ list_free(elemlist);
+ pfree(rawstring);
}
#ifdef USE_RENDEZVOUS
"",
htonl(PostPortNumber),
"",
- (DNSServiceRegistrationReply) reg_reply,
+ (DNSServiceRegistrationReply) reg_reply,
NULL);
}
#endif
reset_shared(PostPortNumber);
/*
- * Estimate number of openable files. This must happen after setting up
- * semaphores, because on some platforms semaphores count as open files.
+ * Estimate number of openable files. This must happen after setting
+ * up semaphores, because on some platforms semaphores count as open
+ * files.
*/
set_max_safe_fds();
BackendList = DLNewList();
#ifdef WIN32
+
/*
- * Initialize the child pid/HANDLE arrays
+ * Initialize the child pid/HANDLE arrays for signal handling.
*/
- win32_childPIDArray = (pid_t*)malloc(NUM_BACKENDARRAY_ELEMS*sizeof(pid_t));
- win32_childHNDArray = (HANDLE*)malloc(NUM_BACKENDARRAY_ELEMS*sizeof(HANDLE));
+ win32_childPIDArray = (pid_t *)
+ malloc(NUM_BACKENDARRAY_ELEMS * sizeof(pid_t));
+ win32_childHNDArray = (HANDLE *)
+ malloc(NUM_BACKENDARRAY_ELEMS * sizeof(HANDLE));
if (!win32_childPIDArray || !win32_childHNDArray)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
+
+ /*
+ * Set up a handle that child processes can use to check whether the
+ * postmaster is still running.
+ */
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetCurrentProcess(),
+ GetCurrentProcess(),
+ &PostmasterHandle,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == 0)
+ ereport(FATAL,
+ (errmsg_internal("could not duplicate postmaster handle: error code %d",
+ (int) GetLastError())));
#endif
/*
if (!CreateOptsFile(argc, argv, my_exec_path))
ExitPostmaster(1);
+#ifdef EXEC_BACKEND
+ write_nondefault_variables(PGC_POSTMASTER);
+#endif
+
+ /*
+ * Write the external PID file if requested
+ */
+ if (external_pid_file)
+ {
+ FILE *fpidfile = fopen(external_pid_file, "w");
+
+ if (fpidfile)
+ {
+ fprintf(fpidfile, "%d\n", MyProcPid);
+ fclose(fpidfile);
+ /* Should we remove the pid file on postmaster exit? */
+ }
+ else
+ write_stderr("%s: could not write external PID file \"%s\": %s\n",
+ progname, external_pid_file, strerror(errno));
+ }
+
/*
* Set up signal handlers for the postmaster process.
*
* CAUTION: when changing this list, check for side-effects on the signal
* handling setup of child processes. See tcop/postgres.c,
- * bootstrap/bootstrap.c, and postmaster/pgstat.c.
+ * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c,
+ * postmaster/pgstat.c, and postmaster/syslogger.c.
*/
pqinitmask();
PG_SETMASK(&BlockSig);
pqsignal(SIGHUP, SIGHUP_handler); /* reread config file and have
* children do same */
- pqsignal(SIGINT, pmdie); /* send SIGTERM and ShutdownDataBase */
+ pqsignal(SIGINT, pmdie); /* send SIGTERM and shut down */
pqsignal(SIGQUIT, pmdie); /* send SIGQUIT and die */
- pqsignal(SIGTERM, pmdie); /* wait for children and ShutdownDataBase */
+ pqsignal(SIGTERM, pmdie); /* wait for children and shut down */
pqsignal(SIGALRM, SIG_IGN); /* ignored */
pqsignal(SIGPIPE, SIG_IGN); /* ignored */
pqsignal(SIGUSR1, sigusr1_handler); /* message from child process */
#endif
/*
- * Reset whereToSendOutput from Debug (its starting state) to None.
- * This prevents ereport from sending log messages to stderr unless
- * the syslog/stderr switch permits. We don't do this until the
- * postmaster is fully launched, since startup failures may as well be
- * reported to stderr.
+ * If enabled, start up syslogger collection subprocess
*/
- whereToSendOutput = None;
+ SysLoggerPID = SysLogger_Start();
/*
- * On many platforms, the first call of localtime() incurs significant
- * overhead to load timezone info from the system configuration files.
- * By doing it once in the postmaster, we avoid having to do it in
- * every started child process. The savings are not huge, but they
- * add up...
+ * Reset whereToSendOutput from Debug (its starting state) to None.
+ * This stops ereport from sending log messages to stderr unless
+ * Log_destination permits. We don't do this until the postmaster is
+ * fully launched, since startup failures may as well be reported to
+ * stderr.
*/
- {
- time_t now = time(NULL);
-
- (void) localtime(&now);
- }
+ whereToSendOutput = None;
/*
- * Initialize and try to startup the statistics collector process
+ * Initialize the statistics collector stuff
*/
pgstat_init();
- pgstat_start();
/*
- * Load cached files for client authentication.
+ * Load configuration files for client authentication.
*/
load_hba();
load_ident();
- load_user();
- load_group();
/*
* We're ready to rock and roll...
return 0; /* not reached */
}
+
+/*
+ * Validate the proposed data directory
+ */
static void
-pmdaemonize(int argc, char *argv[])
+checkDataDir(void)
{
-#ifdef WIN32
- /* not supported */
- elog(FATAL,"SilentMode not supported under WIN32");
-#else
+ char path[MAXPGPATH];
+ FILE *fp;
+ struct stat stat_buf;
+
+ Assert(DataDir);
+
+ if (stat(DataDir, &stat_buf) != 0)
+ {
+ if (errno == ENOENT)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("data directory \"%s\" does not exist",
+ DataDir)));
+ else
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not read permissions of directory \"%s\": %m",
+ DataDir)));
+ }
+
+ /*
+ * Check if the directory has group or world access. If so, reject.
+ *
+ * XXX temporarily suppress check when on Windows, because there may not
+ * be proper support for Unix-y file permissions. Need to think of a
+ * reasonable check to apply on Windows.
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
+ ereport(FATAL,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("data directory \"%s\" has group or world access",
+ DataDir),
+ errdetail("Permissions should be u=rwx (0700).")));
+#endif
+
+ /* Look for PG_VERSION before looking for pg_control */
+ ValidatePgVersion(DataDir);
+
+ snprintf(path, sizeof(path), "%s/global/pg_control", DataDir);
+
+ fp = AllocateFile(path, PG_BINARY_R);
+ if (fp == NULL)
+ {
+ write_stderr("%s: could not find the database system\n"
+ "Expected to find it in the directory \"%s\",\n"
+ "but could not open file \"%s\": %s\n",
+ progname, DataDir, path, strerror(errno));
+ ExitPostmaster(2);
+ }
+ FreeFile(fp);
+}
+
+
+#ifdef USE_RENDEZVOUS
+
+/*
+ * empty callback function for DNSServiceRegistrationCreate()
+ */
+static void
+reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
+{
+
+}
+#endif /* USE_RENDEZVOUS */
+
+
+/*
+ * Fork away from the controlling terminal (-S option)
+ */
+static void
+pmdaemonize(void)
+{
+#ifndef WIN32
int i;
pid_t pid;
#endif
#ifdef LINUX_PROFILE
- /* see comments in BackendRun */
+ /* see comments in BackendStartup */
getitimer(ITIMER_PROF, &prof_itimer);
#endif
pid = fork();
if (pid == (pid_t) -1)
{
- postmaster_error("could not fork background process: %s",
- strerror(errno));
+ write_stderr("%s: could not fork background process: %s\n",
+ progname, strerror(errno));
ExitPostmaster(1);
}
else if (pid)
setitimer(ITIMER_PROF, &prof_itimer, NULL);
#endif
- MyProcPid = getpid(); /* reset MyProcPid to child */
+ MyProcPid = PostmasterPid = getpid(); /* reset PID vars to child */
/* GH: If there's no setsid(), we hopefully don't need silent mode.
* Until there's a better solution.
#ifdef HAVE_SETSID
if (setsid() < 0)
{
- postmaster_error("could not dissociate from controlling TTY: %s",
- strerror(errno));
+ write_stderr("%s: could not dissociate from controlling TTY: %s\n",
+ progname, strerror(errno));
ExitPostmaster(1);
}
#endif
- i = open(NULL_DEV, O_RDWR | PG_BINARY);
+ i = open(NULL_DEV, O_RDWR);
dup2(i, 0);
dup2(i, 1);
dup2(i, 2);
close(i);
-#endif
+#else /* WIN32 */
+ /* not supported */
+ elog(FATAL, "SilentMode not supported under WIN32");
+#endif /* WIN32 */
}
-
/*
* Print out help message
*/
static void
usage(const char *progname)
{
- printf(gettext("%s is the PostgreSQL server.\n\n"), progname);
- printf(gettext("Usage:\n %s [OPTION]...\n\n"), progname);
- printf(gettext("Options:\n"));
+ printf(_("%s is the PostgreSQL server.\n\n"), progname);
+ printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
+ printf(_("Options:\n"));
#ifdef USE_ASSERT_CHECKING
- printf(gettext(" -A 1|0 enable/disable run-time assert checking\n"));
-#endif
- printf(gettext(" -B NBUFFERS number of shared buffers\n"));
- printf(gettext(" -c NAME=VALUE set run-time parameter\n"));
- printf(gettext(" -d 1-5 debugging level\n"));
- printf(gettext(" -D DATADIR database directory\n"));
- printf(gettext(" -F turn fsync off\n"));
- printf(gettext(" -h HOSTNAME host name or IP address to listen on\n"));
- printf(gettext(" -i enable TCP/IP connections\n"));
- printf(gettext(" -k DIRECTORY Unix-domain socket location\n"));
+ printf(_(" -A 1|0 enable/disable run-time assert checking\n"));
+#endif
+ printf(_(" -B NBUFFERS number of shared buffers\n"));
+ printf(_(" -c NAME=VALUE set run-time parameter\n"));
+ printf(_(" -d 1-5 debugging level\n"));
+ printf(_(" -D DATADIR database directory\n"));
+ printf(_(" -F turn fsync off\n"));
+ printf(_(" -h HOSTNAME host name or IP address to listen on\n"));
+ printf(_(" -i enable TCP/IP connections\n"));
+ printf(_(" -k DIRECTORY Unix-domain socket location\n"));
#ifdef USE_SSL
- printf(gettext(" -l enable SSL connections\n"));
+ printf(_(" -l enable SSL connections\n"));
#endif
- printf(gettext(" -N MAX-CONNECT maximum number of allowed connections\n"));
- printf(gettext(" -o OPTIONS pass \"OPTIONS\" to each server process\n"));
- printf(gettext(" -p PORT port number to listen on\n"));
- printf(gettext(" -S silent mode (start in background without logging output)\n"));
- printf(gettext(" --help show this help, then exit\n"));
- printf(gettext(" --version output version information, then exit\n"));
-
- printf(gettext("\nDeveloper options:\n"));
- printf(gettext(" -n do not reinitialize shared memory after abnormal exit\n"));
- printf(gettext(" -s send SIGSTOP to all backend servers if one dies\n"));
-
- printf(gettext("\nPlease read the documentation for the complete list of run-time\n"
+ printf(_(" -N MAX-CONNECT maximum number of allowed connections\n"));
+ printf(_(" -o OPTIONS pass \"OPTIONS\" to each server process\n"));
+ printf(_(" -p PORT port number to listen on\n"));
+ printf(_(" -S silent mode (start in background without logging output)\n"));
+ printf(_(" --help show this help, then exit\n"));
+ printf(_(" --version output version information, then exit\n"));
+
+ printf(_("\nDeveloper options:\n"));
+ printf(_(" -n do not reinitialize shared memory after abnormal exit\n"));
+ printf(_(" -s send SIGSTOP to all backend servers if one dies\n"));
+
+ printf(_("\nPlease read the documentation for the complete list of run-time\n"
"configuration settings and how to set them on the command line or in\n"
"the configuration file.\n\n"
"Report bugs to <pgsql-bugs@postgresql.org>.\n"));
}
+
+/*
+ * Main idle loop of postmaster
+ */
static int
ServerLoop(void)
{
fd_set readmask;
int nSockets;
- struct timeval now,
+ time_t now,
+ last_touch_time;
+ struct timeval earlier,
later;
struct timezone tz;
- int i;
- gettimeofday(&now, &tz);
+ gettimeofday(&earlier, &tz);
+ last_touch_time = time(NULL);
nSockets = initMasks(&readmask);
Port *port;
fd_set rmask;
struct timeval timeout;
+ int selres;
+ int i;
/*
- * The timeout for the select() below is normally set on the basis
- * of the time to the next checkpoint. However, if for some
- * reason we don't have a next-checkpoint time, time out after 60
- * seconds. This keeps checkpoint scheduling from locking up when
- * we get new connection requests infrequently (since we are
- * likely to detect checkpoint completion just after enabling
- * signals below, after we've already made the decision about how
- * long to wait this time).
+ * Wait for something to happen.
+ *
+ * We wait at most one minute, to ensure that the other background
+ * tasks handled below get done even when no requests are
+ * arriving.
*/
+ memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set));
+
timeout.tv_sec = 60;
timeout.tv_usec = 0;
- if (CheckPointPID == 0 && checkpointed &&
- StartupPID == 0 && Shutdown == NoShutdown &&
- !FatalError && random_seed != 0)
- {
- time_t now = time(NULL);
+ PG_SETMASK(&UnBlockSig);
+
+ selres = select(nSockets, &rmask, NULL, NULL, &timeout);
+
+ /*
+ * Block all signals until we wait again. (This makes it safe for
+ * our signal handlers to do nontrivial work.)
+ */
+ PG_SETMASK(&BlockSig);
- if (CheckPointTimeout + checkpointed > now)
+ if (selres < 0)
+ {
+ if (errno != EINTR && errno != EWOULDBLOCK)
{
- /*
- * Not time for checkpoint yet, so set select timeout
- */
- timeout.tv_sec = CheckPointTimeout + checkpointed - now;
+ ereport(LOG,
+ (errcode_for_socket_access(),
+ errmsg("select() failed in postmaster: %m")));
+ return STATUS_ERROR;
}
- else
+ }
+
+ /*
+ * New connection pending on any of our sockets? If so, fork a
+ * child process to deal with it.
+ */
+ if (selres > 0)
+ {
+ /*
+ * Select a random seed at the time of first receiving a
+ * request.
+ */
+ while (random_seed == 0)
{
- /* Time to make the checkpoint... */
- CheckPointPID = CheckPointDataBase();
+ gettimeofday(&later, &tz);
/*
- * if fork failed, schedule another try at 0.1 normal
- * delay
+ * We are not sure how much precision is in tv_usec, so we
+ * swap the high and low 16 bits of 'later' and XOR them with
+ * 'earlier'. On the off chance that the result is 0, we
+ * loop until it isn't.
*/
- if (CheckPointPID == 0)
+ random_seed = earlier.tv_usec ^
+ ((later.tv_usec << 16) |
+ ((later.tv_usec >> 16) & 0xffff));
+ }
+
+ for (i = 0; i < MAXLISTEN; i++)
+ {
+ if (ListenSocket[i] == -1)
+ break;
+ if (FD_ISSET(ListenSocket[i], &rmask))
{
- timeout.tv_sec = CheckPointTimeout / 10;
- checkpointed = now + timeout.tv_sec - CheckPointTimeout;
+ port = ConnCreate(ListenSocket[i]);
+ if (port)
+ {
+ BackendStartup(port);
+
+ /*
+ * We no longer need the open socket or port
+ * structure in this process
+ */
+ StreamClose(port->sock);
+ ConnFree(port);
+ }
}
}
}
+ /* If we have lost the system logger, try to start a new one */
+ if (SysLoggerPID == 0 && Redirect_stderr)
+ SysLoggerPID = SysLogger_Start();
+
/*
- * If no background writer process is running and we should
- * do background writing, start one. It doesn't matter if
- * this fails, we'll just try again later.
+ * If no background writer process is running, and we are not in a
+ * state that prevents it, start one. It doesn't matter if this
+ * fails, we'll just try again later.
*/
- if (BgWriterPID == 0 && BgWriterPercent > 0 &&
- StartupPID == 0 && Shutdown == NoShutdown &&
- !FatalError && random_seed != 0)
+ if (BgWriterPID == 0 && StartupPID == 0 && !FatalError)
{
BgWriterPID = StartBackgroundWriter();
+ /* If shutdown is pending, set it going */
+ if (Shutdown > NoShutdown && BgWriterPID != 0)
+ kill(BgWriterPID, SIGUSR2);
}
- /*
- * Wait for something to happen.
- */
- memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set));
-
- PG_SETMASK(&UnBlockSig);
-
- if (select(nSockets, &rmask, NULL, NULL, &timeout) < 0)
- {
- PG_SETMASK(&BlockSig);
- if (errno == EINTR || errno == EWOULDBLOCK)
- continue;
- ereport(LOG,
- (errcode_for_socket_access(),
- errmsg("select() failed in postmaster: %m")));
- return STATUS_ERROR;
- }
-
- /*
- * Block all signals until we wait again. (This makes it safe for
- * our signal handlers to do nontrivial work.)
- */
- PG_SETMASK(&BlockSig);
-
- /*
- * Select a random seed at the time of first receiving a request.
- */
- while (random_seed == 0)
- {
- gettimeofday(&later, &tz);
+ /* If we have lost the archiver, try to start a new one */
+ if (XLogArchivingActive() && PgArchPID == 0 &&
+ StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ PgArchPID = pgarch_start();
- /*
- * We are not sure how much precision is in tv_usec, so we
- * swap the nibbles of 'later' and XOR them with 'now'. On the
- * off chance that the result is 0, we loop until it isn't.
- */
- random_seed = now.tv_usec ^
- ((later.tv_usec << 16) |
- ((later.tv_usec >> 16) & 0xffff));
- }
+ /* If we have lost the stats collector, try to start a new one */
+ if (PgStatPID == 0 &&
+ StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ PgStatPID = pgstat_start();
/*
- * New connection pending on any of our sockets? If so, fork a
- * child process to deal with it.
+ * Touch the socket and lock file at least every ten minutes, to
+ * ensure that they are not removed by overzealous /tmp-cleaning
+ * tasks.
*/
- for (i = 0; i < MAXLISTEN; i++)
+ now = time(NULL);
+ if (now - last_touch_time >= 10 * 60)
{
- if (ListenSocket[i] == -1)
- break;
- if (FD_ISSET(ListenSocket[i], &rmask))
- {
- port = ConnCreate(ListenSocket[i]);
- if (port)
- {
- BackendStartup(port);
-
- /*
- * We no longer need the open socket or port structure
- * in this process
- */
- StreamClose(port->sock);
- ConnFree(port);
- }
- }
+ TouchSocketFile();
+ TouchSocketLockFile();
+ last_touch_time = now;
}
-
- /* If we have lost the stats collector, try to start a new one */
- if (!pgstat_is_running)
- pgstat_start();
}
}
/*
- * Initialise the masks for select() for the ports
- * we are listening on. Return the number of sockets to listen on.
+ * Initialise the masks for select() for the ports we are listening on.
+ * Return the number of sockets to listen on.
*/
-
static int
initMasks(fd_set *rmask)
{
* Now fetch parameters out of startup packet and save them into the
* Port structure. All data structures attached to the Port struct
* must be allocated in TopMemoryContext so that they won't disappear
- * when we pass them to PostgresMain (see BackendRun). We need not
+ * when we pass them to PostgresMain (see BackendRun). We need not
* worry about leaking this storage on failure, since we aren't in the
* postmaster process anymore.
*/
#ifndef EXEC_BACKEND
Dlelem *curr;
#else
- int i;
+ int i;
#endif
backendPID = (int) ntohl(canc->backendPID);
cancelAuthCode = (long) ntohl(canc->cancelAuthCode);
- if (backendPID == CheckPointPID)
- {
- ereport(DEBUG2,
- (errmsg_internal("ignoring cancel request for checkpoint process %d",
- backendPID)));
- return;
- }
- else if (backendPID == BgWriterPID)
- {
- ereport(DEBUG2,
- (errmsg_internal("ignoring cancel request for bgwriter process %d",
- backendPID)));
- return;
- }
-
- /* See if we have a matching backend */
+ /*
+ * See if we have a matching backend. In the EXEC_BACKEND case, we
+ * can no longer access the postmaster's own backend list, and must
+ * rely on the duplicate array in shared memory.
+ */
#ifndef EXEC_BACKEND
for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
{
#else
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
{
- bp = (Backend*) &ShmemBackendArray[i];
+ bp = (Backend *) &ShmemBackendArray[i];
#endif
if (bp->pid == backendPID)
{
* This is called during child process startup to release file descriptors
* that are not needed by that child process. The postmaster still has
* them open, of course.
+ *
+ * Note: we pass am_syslogger as a boolean because we don't want to set
+ * the global variable yet when this is called.
*/
void
-ClosePostmasterPorts(bool pgstat_too)
+ClosePostmasterPorts(bool am_syslogger)
{
int i;
}
}
- /* Close pgstat control sockets, unless we're starting pgstat itself */
- if (pgstat_too)
- pgstat_close_sockets();
+ /* If using syslogger, close the read side of the pipe */
+ if (!am_syslogger)
+ {
+#ifndef WIN32
+ if (syslogPipe[0] >= 0)
+ close(syslogPipe[0]);
+ syslogPipe[0] = -1;
+#else
+ if (syslogPipe[0])
+ CloseHandle(syslogPipe[0]);
+ syslogPipe[0] = 0;
+#endif
+ }
}
ereport(LOG,
(errmsg("received SIGHUP, reloading configuration files")));
ProcessConfigFile(PGC_SIGHUP);
-#ifdef EXEC_BACKEND
- write_nondefault_variables(PGC_SIGHUP);
-#endif
SignalChildren(SIGHUP);
+ if (BgWriterPID != 0)
+ kill(BgWriterPID, SIGHUP);
+ if (PgArchPID != 0)
+ kill(PgArchPID, SIGHUP);
+ if (SysLoggerPID != 0)
+ kill(SysLoggerPID, SIGHUP);
+ /* PgStatPID does not currently need SIGHUP */
+
+ /* Reload authentication config files too */
load_hba();
load_ident();
- /*
- * Tell the background writer to terminate so that we
- * will start a new one with a possibly changed config
- */
- if (BgWriterPID != 0)
- kill(BgWriterPID, SIGTERM);
+#ifdef EXEC_BACKEND
+ /* Update the starting-point file for future children */
+ write_nondefault_variables(PGC_SIGHUP);
+#endif
}
PG_SETMASK(&UnBlockSig);
}
-
/*
* pmdie -- signal handler for processing various postmaster signals.
*/
/*
* Smart Shutdown:
*
- * Wait for children to end their work and ShutdownDataBase.
+ * Wait for children to end their work, then shut down.
*/
if (Shutdown >= SmartShutdown)
break;
ereport(LOG,
(errmsg("received smart shutdown request")));
- /* Must tell bgwriter to quit, or it never will... */
- if (BgWriterPID != 0)
- kill(BgWriterPID, SIGTERM);
-
- if (DLGetHead(BackendList)) /* let reaper() handle this */
- break;
+ if (DLGetHead(BackendList))
+ break; /* let reaper() handle this */
/*
- * No children left. Shutdown data base system.
+ * No children left. Begin shutdown of data base system.
*/
- if (StartupPID > 0 || FatalError) /* let reaper() handle
- * this */
- break;
- if (ShutdownPID > 0)
- {
- elog(PANIC, "shutdown process %d already running",
- (int) ShutdownPID);
- abort();
- }
-
- ShutdownPID = ShutdownDataBase();
+ if (StartupPID != 0 || FatalError)
+ break; /* let reaper() handle this */
+ /* Start the bgwriter if not running */
+ if (BgWriterPID == 0)
+ BgWriterPID = StartBackgroundWriter();
+ /* And tell it to shut down */
+ if (BgWriterPID != 0)
+ kill(BgWriterPID, SIGUSR2);
+ /* Tell pgarch to shut down too; nothing left for it to do */
+ if (PgArchPID != 0)
+ kill(PgArchPID, SIGQUIT);
+ /* Tell pgstat to shut down too; nothing left for it to do */
+ if (PgStatPID != 0)
+ kill(PgStatPID, SIGQUIT);
break;
case SIGINT:
* Fast Shutdown:
*
* Abort all children with SIGTERM (rollback active transactions
- * and exit) and ShutdownDataBase when they are gone.
+ * and exit) and shut down when they are gone.
*/
if (Shutdown >= FastShutdown)
break;
}
/*
- * No children left. Shutdown data base system.
+ * No children left. Begin shutdown of data base system.
*
- * Unlike the previous case, it is not an error for the shutdown
- * process to be running already (we could get SIGTERM followed
- * shortly later by SIGINT).
+ * Note: if we previously got SIGTERM then we may send SIGUSR2 to
+ * the bgwriter a second time here. This should be harmless.
*/
- if (StartupPID > 0 || FatalError) /* let reaper() handle
- * this */
- break;
- if (ShutdownPID == 0)
- ShutdownPID = ShutdownDataBase();
+ if (StartupPID != 0 || FatalError)
+ break; /* let reaper() handle this */
+ /* Start the bgwriter if not running */
+ if (BgWriterPID == 0)
+ BgWriterPID = StartBackgroundWriter();
+ /* And tell it to shut down */
+ if (BgWriterPID != 0)
+ kill(BgWriterPID, SIGUSR2);
+ /* Tell pgarch to shut down too; nothing left for it to do */
+ if (PgArchPID != 0)
+ kill(PgArchPID, SIGQUIT);
+ /* Tell pgstat to shut down too; nothing left for it to do */
+ if (PgStatPID != 0)
+ kill(PgStatPID, SIGQUIT);
break;
case SIGQUIT:
* Immediate Shutdown:
*
* abort all children with SIGQUIT and exit without attempt to
- * properly shutdown data base system.
+ * properly shut down data base system.
*/
ereport(LOG,
(errmsg("received immediate shutdown request")));
- if (ShutdownPID > 0)
- kill(ShutdownPID, SIGQUIT);
- if (StartupPID > 0)
+ if (StartupPID != 0)
kill(StartupPID, SIGQUIT);
+ if (BgWriterPID != 0)
+ kill(BgWriterPID, SIGQUIT);
+ if (PgArchPID != 0)
+ kill(PgArchPID, SIGQUIT);
+ if (PgStatPID != 0)
+ kill(PgStatPID, SIGQUIT);
if (DLGetHead(BackendList))
SignalChildren(SIGQUIT);
ExitPostmaster(0);
#ifdef HAVE_WAITPID
int status; /* backend exit status */
+
#else
#ifndef WIN32
union wait status; /* backend exit status */
while ((pid = win32_waitpid(&exitstatus)) > 0)
{
/*
- * We need to do this here, and not in CleanupProc, since this
+ * We need to do this here, and not in CleanupBackend, since this
* is to be called on all children when we are done with them.
* Could move to LogChildExit, but that seems like asking for
* future trouble...
*/
win32_RemoveChild(pid);
-#endif
-#endif
-
- /*
- * Check if this child was the statistics collector. If so, try to
- * start a new one. (If fail, we'll try again in future cycles of
- * the main loop.)
- */
- if (pgstat_ispgstat(pid))
- {
- LogChildExit(LOG, gettext("statistics collector process"),
- pid, exitstatus);
- pgstat_start();
- continue;
- }
+#endif /* WIN32 */
+#endif /* HAVE_WAITPID */
/*
- * Check if this child was a shutdown or startup process.
+ * Check if this child was a startup process.
*/
- if (ShutdownPID > 0 && pid == ShutdownPID)
- {
- if (exitstatus != 0)
- {
- LogChildExit(LOG, gettext("shutdown process"),
- pid, exitstatus);
- ExitPostmaster(1);
- }
- /* Normal postmaster exit is here */
- ExitPostmaster(0);
- }
-
- if (StartupPID > 0 && pid == StartupPID)
+ if (StartupPID != 0 && pid == StartupPID)
{
+ StartupPID = 0;
if (exitstatus != 0)
{
- LogChildExit(LOG, gettext("startup process"),
+ LogChildExit(LOG, _("startup process"),
pid, exitstatus);
ereport(LOG,
(errmsg("aborting startup due to startup process failure")));
ExitPostmaster(1);
}
- StartupPID = 0;
/*
- * Startup succeeded - remember its ID and RedoRecPtr.
- *
- * NB: this MUST happen before we fork a checkpoint or shutdown
- * subprocess, else they will have wrong local ThisStartUpId.
+ * Startup succeeded - we are done with system startup or
+ * recovery.
*/
- SetThisStartUpID();
+ FatalError = false;
- FatalError = false; /* done with recovery */
+ /*
+ * Load the flat user/group files into postmaster's caches.
+ * The startup process has recomputed these from the database
+ * contents, so we wait till it finishes before loading them.
+ */
+ load_user();
+ load_group();
/*
- * Arrange for first checkpoint to occur after standard delay.
+ * Crank up the background writer. It doesn't matter if this
+ * fails, we'll just try again later.
*/
- CheckPointPID = 0;
- checkpointed = time(NULL);
+ Assert(BgWriterPID == 0);
+ BgWriterPID = StartBackgroundWriter();
/*
* Go to shutdown mode if a shutdown request was pending.
+ * Otherwise, try to start the archiver and stats collector
+ * too.
*/
- if (Shutdown > NoShutdown)
+ if (Shutdown > NoShutdown && BgWriterPID != 0)
+ kill(BgWriterPID, SIGUSR2);
+ else if (Shutdown == NoShutdown)
{
- if (ShutdownPID > 0)
- {
- elog(PANIC, "startup process %d died while shutdown process %d already running",
- pid, (int) ShutdownPID);
- abort();
- }
- ShutdownPID = ShutdownDataBase();
+ if (XLogArchivingActive() && PgArchPID == 0)
+ PgArchPID = pgarch_start();
+ if (PgStatPID == 0)
+ PgStatPID = pgstat_start();
}
- goto reaper_done;
+ continue;
+ }
+
+ /*
+ * Was it the bgwriter?
+ */
+ if (BgWriterPID != 0 && pid == BgWriterPID)
+ {
+ BgWriterPID = 0;
+ if (exitstatus == 0 && Shutdown > NoShutdown &&
+ !FatalError && !DLGetHead(BackendList))
+ {
+ /*
+ * Normal postmaster exit is here: we've seen normal exit
+ * of the bgwriter after it's been told to shut down. We
+ * expect that it wrote a shutdown checkpoint. (If for
+ * some reason it didn't, recovery will occur on next
+ * postmaster start.)
+ *
+ * Note: we do not wait around for exit of the archiver or
+ * stats processes. They've been sent SIGQUIT by this
+ * point, and in any case contain logic to commit
+ * hara-kiri if they notice the postmaster is gone.
+ */
+ ExitPostmaster(0);
+ }
+
+ /*
+ * Any unexpected exit of the bgwriter is treated as a crash.
+ */
+ HandleChildCrash(pid, exitstatus,
+ _("background writer process"));
+ continue;
+ }
+
+ /*
+ * 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 (PgArchPID != 0 && pid == PgArchPID)
+ {
+ PgArchPID = 0;
+ if (exitstatus != 0)
+ LogChildExit(LOG, _("archiver process"),
+ pid, exitstatus);
+ if (XLogArchivingActive() &&
+ StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ PgArchPID = pgarch_start();
+ continue;
}
/*
- * Else do standard child cleanup.
+ * Was it the statistics collector? 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.)
*/
- CleanupProc(pid, exitstatus);
+ if (PgStatPID != 0 && pid == PgStatPID)
+ {
+ PgStatPID = 0;
+ if (exitstatus != 0)
+ LogChildExit(LOG, _("statistics collector process"),
+ pid, exitstatus);
+ if (StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ PgStatPID = pgstat_start();
+ continue;
+ }
+
+ /* Was it the system logger? try to start a new one */
+ if (SysLoggerPID != 0 && pid == SysLoggerPID)
+ {
+ SysLoggerPID = 0;
+ /* for safety's sake, launch new logger *first* */
+ SysLoggerPID = SysLogger_Start();
+ if (exitstatus != 0)
+ LogChildExit(LOG, _("system logger process"),
+ pid, exitstatus);
+ continue;
+ }
+ /*
+ * Else do standard backend child cleanup.
+ */
+ CleanupBackend(pid, exitstatus);
} /* loop over pending child-death reports */
if (FatalError)
{
/*
- * Wait for all children exit, then reset shmem and
- * StartupDataBase.
+ * Wait for all important 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 (DLGetHead(BackendList) || StartupPID > 0 || ShutdownPID > 0)
+ if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0)
goto reaper_done;
ereport(LOG,
(errmsg("all server processes terminated; reinitializing")));
if (Shutdown > NoShutdown)
{
- if (DLGetHead(BackendList))
+ if (DLGetHead(BackendList) || StartupPID != 0)
goto reaper_done;
- if (StartupPID > 0 || ShutdownPID > 0)
- goto reaper_done;
- ShutdownPID = ShutdownDataBase();
+ /* Start the bgwriter if not running */
+ if (BgWriterPID == 0)
+ BgWriterPID = StartBackgroundWriter();
+ /* And tell it to shut down */
+ if (BgWriterPID != 0)
+ kill(BgWriterPID, SIGUSR2);
+ /* Tell pgarch to shut down too; nothing left for it to do */
+ if (PgArchPID != 0)
+ kill(PgArchPID, SIGQUIT);
+ /* Tell pgstat to shut down too; nothing left for it to do */
+ if (PgStatPID != 0)
+ kill(PgStatPID, SIGQUIT);
}
reaper_done:
/*
- * CleanupProc -- cleanup after terminated backend.
+ * CleanupBackend -- cleanup after terminated backend.
*
* Remove all local state associated with backend.
*/
static void
-CleanupProc(int pid,
- int exitstatus) /* child's exit status. */
+CleanupBackend(int pid,
+ int exitstatus) /* child's exit status. */
{
- Dlelem *curr,
- *next;
- Backend *bp;
+ Dlelem *curr;
- LogChildExit(DEBUG2, gettext("child process"), pid, exitstatus);
+ LogChildExit(DEBUG2, _("server process"), pid, exitstatus);
/*
* If a backend dies in an ugly way (i.e. exit status not 0) then we
* we assume everything is hunky dory and simply remove the backend
* from the active backend list.
*/
- if (exitstatus == 0)
+ if (exitstatus != 0)
+ {
+ HandleChildCrash(pid, exitstatus, _("server process"));
+ return;
+ }
+
+ for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
{
- curr = DLGetHead(BackendList);
- while (curr)
+ Backend *bp = (Backend *) DLE_VAL(curr);
+
+ if (bp->pid == pid)
{
- bp = (Backend *) DLE_VAL(curr);
- if (bp->pid == pid)
- {
+ DLRemove(curr);
+ free(bp);
+ DLFreeElem(curr);
#ifdef EXEC_BACKEND
- ShmemBackendArrayRemove(bp->pid);
+ ShmemBackendArrayRemove(pid);
#endif
- DLRemove(curr);
- free(bp);
- DLFreeElem(curr);
- break;
- }
- curr = DLGetSucc(curr);
- }
-
- if (pid == CheckPointPID)
- {
- CheckPointPID = 0;
- if (!FatalError)
- {
- checkpointed = time(NULL);
- /* Update RedoRecPtr for future child backends */
- GetSavedRedoRecPtr();
- }
- }
- else if (pid == BgWriterPID)
- BgWriterPID = 0;
- else
+ /* Tell the collector about backend termination */
pgstat_beterm(pid);
-
- return;
+ break;
+ }
}
+}
- /* below here we're dealing with a non-normal exit */
+/*
+ * HandleChildCrash -- cleanup after failed backend or bgwriter.
+ *
+ * The objectives here are to clean up our local state about the child
+ * process, and to signal all other remaining children to quickdie.
+ */
+static void
+HandleChildCrash(int pid, int exitstatus, const char *procname)
+{
+ Dlelem *curr,
+ *next;
+ Backend *bp;
- /* Make log entry unless we did so already */
+ /*
+ * Make log entry unless there was a previous crash (if so, nonzero
+ * exit status is to be expected in SIGQUIT response; don't clutter
+ * log)
+ */
if (!FatalError)
{
- LogChildExit(LOG,
- (pid == CheckPointPID) ? gettext("checkpoint process") :
- (pid == BgWriterPID) ? gettext("bgwriter process") :
- gettext("server process"),
- pid, exitstatus);
+ LogChildExit(LOG, procname, pid, exitstatus);
ereport(LOG,
(errmsg("terminating any other active server processes")));
}
- curr = DLGetHead(BackendList);
- while (curr)
+ /* Process regular backends */
+ for (curr = DLGetHead(BackendList); curr; curr = next)
{
next = DLGetSucc(curr);
bp = (Backend *) DLE_VAL(curr);
- if (bp->pid != pid)
+ if (bp->pid == pid)
+ {
+ /*
+ * Found entry for freshly-dead backend, so remove it.
+ */
+ DLRemove(curr);
+ free(bp);
+ DLFreeElem(curr);
+#ifdef EXEC_BACKEND
+ ShmemBackendArrayRemove(pid);
+#endif
+ /* Tell the collector about backend termination */
+ pgstat_beterm(pid);
+ /* Keep looping so we can signal remaining backends */
+ }
+ else
{
/*
* This backend is still alive. Unless we did so already,
{
ereport(DEBUG2,
(errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
+ (SendStop ? "SIGSTOP" : "SIGQUIT"),
(int) bp->pid)));
kill(bp->pid, (SendStop ? SIGSTOP : SIGQUIT));
}
}
- else
- {
- /*
- * Found entry for freshly-dead backend, so remove it.
- */
-#ifdef EXEC_BACKEND
- ShmemBackendArrayRemove(bp->pid);
-#endif
- DLRemove(curr);
- free(bp);
- DLFreeElem(curr);
- }
- curr = next;
}
- if (pid == CheckPointPID)
+ /* Take care of the bgwriter too */
+ if (pid == BgWriterPID)
+ BgWriterPID = 0;
+ else if (BgWriterPID != 0 && !FatalError)
{
- CheckPointPID = 0;
- checkpointed = 0;
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ (SendStop ? "SIGSTOP" : "SIGQUIT"),
+ (int) BgWriterPID)));
+ kill(BgWriterPID, (SendStop ? SIGSTOP : SIGQUIT));
}
- else if (pid == BgWriterPID)
+
+ /* Force a power-cycle of the pgarch process too */
+ /* (Shouldn't be necessary, but just for luck) */
+ if (PgArchPID != 0 && !FatalError)
{
- BgWriterPID = 0;
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ "SIGQUIT",
+ (int) PgArchPID)));
+ kill(PgArchPID, SIGQUIT);
}
- else
+
+ /* Force a power-cycle of the pgstat processes too */
+ /* (Shouldn't be necessary, but just for luck) */
+ if (PgStatPID != 0 && !FatalError)
{
- /*
- * Tell the collector about backend termination
- */
- pgstat_beterm(pid);
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ "SIGQUIT",
+ (int) PgStatPID)));
+ kill(PgStatPID, SIGQUIT);
}
+ /* We do NOT restart the syslogger */
+
FatalError = true;
}
}
/*
- * Send a signal to all backend children.
+ * Send a signal to all backend children (but NOT special children)
*/
static void
SignalChildren(int signal)
{
- Dlelem *curr,
- *next;
- Backend *bp;
+ Dlelem *curr;
- curr = DLGetHead(BackendList);
- while (curr)
+ for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
{
- next = DLGetSucc(curr);
- bp = (Backend *) DLE_VAL(curr);
-
- if (bp->pid != MyProcPid)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending signal %d to process %d",
- signal,
- (int) bp->pid)));
- kill(bp->pid, signal);
- }
+ Backend *bp = (Backend *) DLE_VAL(curr);
- curr = next;
+ ereport(DEBUG4,
+ (errmsg_internal("sending signal %d to process %d",
+ signal, (int) bp->pid)));
+ kill(bp->pid, signal);
}
}
return STATUS_ERROR;
}
+ /* Pass down canAcceptConnections state (kluge for EXEC_BACKEND case) */
+ port->canAcceptConnections = canAcceptConnections();
+
/*
* Flush stdio channels just before fork, to avoid double-output
* problems. Ideally we'd use fflush(NULL) here, but there are still a
fflush(stdout);
fflush(stderr);
+#ifdef EXEC_BACKEND
+
+ pid = backend_forkexec(port);
+
+#else /* !EXEC_BACKEND */
+
#ifdef LINUX_PROFILE
/*
beos_before_backend_startup();
#endif
- port->canAcceptConnections = canAcceptConnections();
-#ifdef EXEC_BACKEND
- pid = Backend_forkexec(port);
-#else
pid = fork();
if (pid == 0) /* child */
proc_exit(BackendRun(port));
}
-#endif
+#endif /* EXEC_BACKEND */
- /* in parent, error */
if (pid < 0)
{
+ /* in parent, fork failed */
int save_errno = errno;
#ifdef __BEOS__
return STATUS_ERROR;
}
- /* in parent, normal */
+ /* in parent, successful fork */
ereport(DEBUG2,
(errmsg_internal("forked new backend, pid=%d socket=%d",
(int) pid, port->sock)));
*/
bn->pid = pid;
bn->cancel_key = MyCancelKey;
+ DLAddHead(BackendList, DLNewElem(bn));
#ifdef EXEC_BACKEND
ShmemBackendArrayAdd(bn);
#endif
- DLAddHead(BackendList, DLNewElem(bn));
return STATUS_OK;
}
/* Format the error message packet (always V2 protocol) */
snprintf(buffer, sizeof(buffer), "E%s%s\n",
- gettext("could not fork new process for connection: "),
+ _("could not fork new process for connection: "),
strerror(errnum));
/* Set port to non-blocking. Don't do send() if this fails */
/*
- * BackendInit/Run -- perform authentication [BackendInit], and if successful,
- * set up the backend's argument list [BackendRun] and invoke
- * backend main()
+ * BackendRun -- perform authentication, and if successful,
+ * set up the backend's argument list and invoke PostgresMain()
*
* returns:
* Shouldn't return at all.
* If PostgresMain() fails, return status.
*/
-static void
-BackendInit(Port *port)
+static int
+BackendRun(Port *port)
{
int status;
- struct timeval now;
- struct timezone tz;
char remote_host[NI_MAXHOST];
char remote_port[NI_MAXSERV];
char remote_ps_data[NI_MAXHOST];
+ char **av;
+ int maxac;
+ int ac;
+ char protobuf[32];
+ int i;
IsUnderPostmaster = true; /* we are a postmaster subprocess now */
- ClientAuthInProgress = true; /* limit visibility of log messages */
+ /*
+ * Let's clean up ourselves as the postmaster child, and close the
+ * postmaster's listen sockets
+ */
+ ClosePostmasterPorts(false);
/* We don't want the postmaster's proc_exit() handlers */
on_exit_reset();
* Signal handlers setting is moved to tcop/postgres...
*/
- /* save start time for end of session reporting */
- gettimeofday(&(port->session_start),NULL);
-
- /* set these to empty in case they are needed before we set them up */
- port->remote_host = "";
- port->remote_port = "";
- port->commandTag = "";
-
/* Save port etc. for ps status */
MyProcPort = port;
/* Reset MyProcPid to new backend's pid */
MyProcPid = getpid();
+ /*
+ * PreAuthDelay is a debugging aid for investigating problems in the
+ * authentication cycle: it can be set in postgresql.conf to allow
+ * time to attach to the newly-forked backend with a debugger. (See
+ * also the -W backend switch, which we allow clients to pass through
+ * PGOPTIONS, but it is not honored until after authentication.)
+ */
+ if (PreAuthDelay > 0)
+ pg_usleep(PreAuthDelay * 1000000L);
+
+ ClientAuthInProgress = true; /* limit visibility of log messages */
+
+ /* save start time for end of session reporting */
+ gettimeofday(&(port->session_start), NULL);
+
+ /* set these to empty in case they are needed before we set them up */
+ port->remote_host = "";
+ port->remote_port = "";
+ port->commandTag = "";
+
/*
* Initialize libpq and enable reporting of ereport errors to the
* client. Must do this now because authentication uses libpq to send
remote_port, sizeof(remote_port),
(log_hostname ? 0 : NI_NUMERICHOST) | NI_NUMERICSERV))
{
- getnameinfo_all(&port->raddr.addr, port->raddr.salen,
- remote_host, sizeof(remote_host),
- remote_port, sizeof(remote_port),
- NI_NUMERICHOST | NI_NUMERICSERV);
+ int ret = getnameinfo_all(&port->raddr.addr, port->raddr.salen,
+ remote_host, sizeof(remote_host),
+ remote_port, sizeof(remote_port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (ret)
+ ereport(WARNING,
+ (errmsg("getnameinfo_all() failed: %s",
+ gai_strerror(ret))));
}
snprintf(remote_ps_data, sizeof(remote_ps_data),
remote_port[0] == '\0' ? "%s" : "%s(%s)",
port->remote_host = strdup(remote_host);
port->remote_port = strdup(remote_port);
+ /*
+ * In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf
+ * etcetera from the postmaster, and have to load them ourselves.
+ * Build the PostmasterContext (which didn't exist before, in this
+ * process) to contain the data.
+ *
+ * FIXME: [fork/exec] Ugh. Is there a way around this overhead?
+ */
+#ifdef EXEC_BACKEND
+ Assert(PostmasterContext == NULL);
+ PostmasterContext = AllocSetContextCreate(TopMemoryContext,
+ "Postmaster",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ MemoryContextSwitchTo(PostmasterContext);
+
+ load_hba();
+ load_ident();
+ load_user();
+ load_group();
+#endif
+
/*
* Ready to begin client interaction. We will give up and exit(0)
* after a time delay, so that a broken client can't hog a connection
* start a new random sequence in the random() library function.
*/
random_seed = 0;
- gettimeofday(&now, &tz);
- srandom((unsigned int) now.tv_usec);
-}
-
-
-static int
-BackendRun(Port *port)
-{
- char **av;
- int maxac;
- int ac;
- char debugbuf[32];
- char protobuf[32];
- int i;
-
- /*
- * Let's clean up ourselves as the postmaster child, and
- * close the postmaster's other sockets
- */
- ClosePostmasterPorts(true);
-
- /*
- * PreAuthDelay is a debugging aid for investigating problems in the
- * authentication cycle: it can be set in postgresql.conf to allow
- * time to attach to the newly-forked backend with a debugger. (See
- * also the -W backend switch, which we allow clients to pass through
- * PGOPTIONS, but it is not honored until after authentication.)
- */
- if (PreAuthDelay > 0)
- pg_usleep(PreAuthDelay*1000000L);
-
- /* Will exit on failure */
- BackendInit(port);
-
+ srandom((unsigned int) (MyProcPid ^ port->session_start.tv_usec));
/* ----------------
* Now, build the argv vector that will be given to PostgresMain.
av[ac++] = "postgres";
- /*
- * Pass the requested debugging level along to the backend.
- */
- if (debug_flag > 0)
- {
- snprintf(debugbuf, sizeof(debugbuf), "-d%d", debug_flag);
- av[ac++] = debugbuf;
- }
-
/*
* Pass any backend switches specified with -o in the postmaster's own
- * command line. We assume these are secure.
+ * command line. We assume these are secure. (It's OK to mangle
+ * ExtraOptions now, since we're safely inside a subprocess.)
*/
split_opts(av, &ac, ExtraOptions);
snprintf(protobuf, sizeof(protobuf), "-v%u", port->proto);
av[ac++] = protobuf;
-#ifdef EXEC_BACKEND
- /* pass data dir before end of secure switches (-p) */
- av[ac++] = "-D";
- av[ac++] = DataDir;
-#endif
-
/*
* Tell the backend it is being called from the postmaster, and which
* database to use. -p marks the end of secure switches.
* username isn't lost either; see ProcessStartupPacket().
*/
MemoryContextSwitchTo(TopMemoryContext);
-#ifndef EXEC_BACKEND
MemoryContextDelete(PostmasterContext);
-#endif
PostmasterContext = NULL;
/*
*/
ereport(DEBUG3,
(errmsg_internal("%s child[%d]: starting with (",
- progname, getpid())));
+ progname, (int)getpid())));
for (i = 0; i < ac; ++i)
ereport(DEBUG3,
(errmsg_internal("\t%s", av[i])));
#ifdef EXEC_BACKEND
+/*
+ * postmaster_forkexec -- fork and exec a postmaster subprocess
+ *
+ * The caller must have set up the argv array already, except for argv[2]
+ * which will be filled with the name of the temp variable file.
+ *
+ * Returns the child process PID, or -1 on fork failure (a suitable error
+ * message has been logged on failure).
+ *
+ * All uses of this routine will dispatch to SubPostmasterMain in the
+ * child process.
+ */
+pid_t
+postmaster_forkexec(int argc, char *argv[])
+{
+ Port port;
+
+ /* This entry point passes dummy values for the Port variables */
+ memset(&port, 0, sizeof(port));
+ return internal_forkexec(argc, argv, &port);
+}
/*
- * SubPostmasterMain -- prepare the fork/exec'd process to be in an equivalent
- * state (for calling BackendRun) as a forked process.
+ * backend_forkexec -- fork/exec off a backend process
*
- * returns:
- * Shouldn't return at all.
+ * returns the pid of the fork/exec'd process, or -1 on failure
*/
-void
-SubPostmasterMain(int argc, char* argv[])
+static pid_t
+backend_forkexec(Port *port)
{
- unsigned long backendID;
- Port port;
+ char *av[4];
+ int ac = 0;
- memset((void*)&port, 0, sizeof(Port));
- Assert(argc == 2);
+ av[ac++] = "postgres";
+ av[ac++] = "-forkbackend";
+ av[ac++] = NULL; /* filled in by internal_forkexec */
- /* Do this sooner rather than later... */
- IsUnderPostmaster = true; /* we are a postmaster subprocess now */
+ av[ac] = NULL;
+ Assert(ac < lengthof(av));
- /* In EXEC case we will not have inherited these settings */
- IsPostmasterEnvironment = true;
- whereToSendOutput = None;
+ return internal_forkexec(ac, av, port);
+}
- /* Setup global context */
- MemoryContextInit();
- InitializeGUCOptions();
+#ifndef WIN32
- /* Parse passed-in context */
- argc = 0;
- backendID = (unsigned long)atol(argv[argc++]);
- DataDir = strdup(argv[argc++]);
+/*
+ * internal_forkexec non-win32 implementation
+ *
+ * - writes out backend variables to the parameter file
+ * - fork():s, and then exec():s the child process
+ */
+static pid_t
+internal_forkexec(int argc, char *argv[], Port *port)
+{
+ static unsigned long tmpBackendFileNum = 0;
+ pid_t pid;
+ char tmpfilename[MAXPGPATH];
+ BackendParameters param;
+ FILE *fp;
- /* Read in file-based context */
- read_nondefault_variables();
- read_backend_variables(backendID,&port);
+ if (!save_backend_variables(¶m, port))
+ return -1; /* log made by save_backend_variables */
- /* Remaining initialization */
- pgstat_init_forkexec_backend();
+ /* Calculate name for temp file */
+ Assert(DataDir);
+ snprintf(tmpfilename, MAXPGPATH, "%s/%s/%s.backend_var.%d.%lu",
+ DataDir, PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
+ MyProcPid, ++tmpBackendFileNum);
- /* FIXME: [fork/exec] Ugh */
- load_hba();
- load_ident();
- load_user();
- load_group();
+ /* Open file */
+ fp = AllocateFile(tmpfilename, PG_BINARY_W);
+ if (!fp)
+ {
+ /* As per OpenTemporaryFile... */
+ char dirname[MAXPGPATH];
- /* Attach process to shared segments */
- CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
+ snprintf(dirname, MAXPGPATH, "%s/%s", DataDir, PG_TEMP_FILES_DIR);
+ mkdir(dirname, S_IRWXU);
- /* Run backend */
- proc_exit(BackendRun(&port));
+ fp = AllocateFile(tmpfilename, PG_BINARY_W);
+ if (!fp)
+ {
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("could not create file \"%s\": %m",
+ tmpfilename)));
+ return -1;
+ }
+ }
+
+ if (fwrite(¶m, sizeof(param), 1, fp) != 1)
+ {
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("could not write to file \"%s\": %m", tmpfilename)));
+ FreeFile(fp);
+ return -1;
+ }
+
+ /* Release file */
+ if (FreeFile(fp))
+ {
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("could not write to file \"%s\": %m", tmpfilename)));
+ return -1;
+ }
+
+ /* Make sure caller set up argv properly */
+ Assert(argc >= 3);
+ Assert(argv[argc] == NULL);
+ Assert(strncmp(argv[1], "-fork", 5) == 0);
+ Assert(argv[2] == NULL);
+
+ /* Insert temp file name after -fork argument */
+ argv[2] = tmpfilename;
+
+ /* Fire off execv in child */
+ if ((pid = fork()) == 0)
+ {
+ if (execv(postgres_exec_path, argv) < 0)
+ {
+ ereport(LOG,
+ (errmsg("could not execute server process \"%s\": %m",
+ postgres_exec_path)));
+ /* We're already in the child process here, can't return */
+ exit(1);
+ }
+ }
+
+ return pid; /* Parent returns pid, or -1 on fork
+ * failure */
}
+#else /* WIN32 */
/*
- * Backend_forkexec -- fork/exec off a backend process
+ * internal_forkexec win32 implementation
*
- * returns:
- * the pid of the fork/exec'd process
+ * - starts backend using CreateProcess(), in suspended state
+ * - writes out backend variables to the parameter file
+ * - during this, duplicates handles and sockets required for
+ * inheritance into the new process
+ * - resumes execution of the new process once the backend parameter
+ * file is complete.
*/
static pid_t
-Backend_forkexec(Port *port)
+internal_forkexec(int argc, char *argv[], Port *port)
{
- pid_t pid;
- char *av[5];
- int ac = 0, bufc = 0, i;
- char buf[2][MAXPGPATH];
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ int i;
+ int j;
+ char cmdLine[MAXPGPATH * 2];
+ HANDLE childHandleCopy;
+ HANDLE waiterThread;
+ HANDLE paramHandle;
+ BackendParameters *param;
+ SECURITY_ATTRIBUTES sa;
+ char paramHandleStr[32];
+
+ /* Make sure caller set up argv properly */
+ Assert(argc >= 3);
+ Assert(argv[argc] == NULL);
+ Assert(strncmp(argv[1], "-fork", 5) == 0);
+ Assert(argv[2] == NULL);
+
+ /* Set up shared memory for parameter passing */
+ ZeroMemory(&sa,sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ paramHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
+ &sa,
+ PAGE_READWRITE,
+ 0,
+ sizeof(BackendParameters),
+ NULL);
+ if (paramHandle == INVALID_HANDLE_VALUE)
+ {
+ elog(LOG, "could not create backend parameter file mapping: error code %d",
+ (int) GetLastError());
+ return -1;
+ }
- if (!write_backend_variables(port))
- return -1; /* log made by write_backend_variables */
+ param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters));
+ if (!param)
+ {
+ elog(LOG, "could not map backend parameter memory: error code %d",
+ (int) GetLastError());
+ CloseHandle(paramHandle);
+ return -1;
+ }
- av[ac++] = "postgres";
- av[ac++] = "-forkexec";
+ /* Insert temp file name after -fork argument */
+ sprintf(paramHandleStr, "%lu", (DWORD)paramHandle);
+ argv[2] = paramHandleStr;
- /* Format up context to pass to exec'd process */
- snprintf(buf[bufc++],MAXPGPATH,"%lu",tmpBackendFileNum);
- snprintf(buf[bufc++],MAXPGPATH,"\"%s\"",DataDir);
+ /* Format the cmd line */
+ cmdLine[sizeof(cmdLine) - 1] = '\0';
+ cmdLine[sizeof(cmdLine) - 2] = '\0';
+ snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\"", postgres_exec_path);
+ i = 0;
+ while (argv[++i] != NULL)
+ {
+ j = strlen(cmdLine);
+ snprintf(cmdLine + j, sizeof(cmdLine) - 1 - j, " \"%s\"", argv[i]);
+ }
+ if (cmdLine[sizeof(cmdLine) - 2] != '\0')
+ {
+ elog(LOG, "subprocess command line too long");
+ return -1;
+ }
- /* Add to the arg list */
- Assert(bufc <= lengthof(buf));
- for (i = 0; i < bufc; i++)
- av[ac++] = buf[i];
+ memset(&pi, 0, sizeof(pi));
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ /*
+ * Create the subprocess in a suspended state. This will be resumed
+ * later, once we have written out the parameter file.
+ */
+ if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi))
+ {
+ elog(LOG, "CreateProcess call failed: %m (error code %d)",
+ (int) GetLastError());
+ return -1;
+ }
- /* FIXME: [fork/exec] ExtraOptions? */
+ if (!save_backend_variables(param, port, pi.hProcess, pi.dwProcessId))
+ {
+ /*
+ * log made by save_backend_variables, but we have to clean
+ * up the mess with the half-started process
+ */
+ if (!TerminateProcess(pi.hProcess, 255))
+ ereport(ERROR,
+ (errmsg_internal("could not terminate unstarted process: error code %d",
+ (int) GetLastError())));
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return -1; /* log made by save_backend_variables */
+ }
+
+ /* Drop the shared memory that is now inherited to the backend */
+ if (!UnmapViewOfFile(param))
+ elog(LOG, "could not unmap view of backend parameter file: error code %d",
+ (int) GetLastError());
+ if (!CloseHandle(paramHandle))
+ elog(LOG, "could not close handle to backend parameter file: error code %d",
+ (int) GetLastError());
+
+ /*
+ * Now that the backend variables are written out, we start the
+ * child thread so it can start initializing while we set up
+ * the rest of the parent state.
+ */
+ if (ResumeThread(pi.hThread) == -1)
+ {
+ if (!TerminateProcess(pi.hProcess, 255))
+ {
+ ereport(ERROR,
+ (errmsg_internal("could not terminate unstartable process: error code %d",
+ (int) GetLastError())));
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return -1;
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ ereport(ERROR,
+ (errmsg_internal("could not resume thread of unstarted process: error code %d",
+ (int) GetLastError())));
+ return -1;
+ }
- av[ac++] = NULL;
- Assert(ac <= lengthof(av));
+ if (!IsUnderPostmaster)
+ {
+ /* We are the Postmaster creating a child... */
+ win32_AddChild(pi.dwProcessId, pi.hProcess);
+ }
+
+ /* Set up the thread to handle the SIGCHLD for this process */
+ if (DuplicateHandle(GetCurrentProcess(),
+ pi.hProcess,
+ GetCurrentProcess(),
+ &childHandleCopy,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS) == 0)
+ ereport(FATAL,
+ (errmsg_internal("could not duplicate child handle: error code %d",
+ (int) GetLastError())));
+
+ waiterThread = CreateThread(NULL, 64 * 1024, win32_sigchld_waiter,
+ (LPVOID) childHandleCopy, 0, NULL);
+ if (!waiterThread)
+ ereport(FATAL,
+ (errmsg_internal("could not create sigchld waiter thread: error code %d",
+ (int) GetLastError())));
+ CloseHandle(waiterThread);
+
+ if (IsUnderPostmaster)
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ return pi.dwProcessId;
+}
+
+#endif /* WIN32 */
+
+
+/*
+ * SubPostmasterMain -- Get the fork/exec'd process into a state equivalent
+ * to what it would be if we'd simply forked on Unix, and then
+ * dispatch to the appropriate place.
+ *
+ * The first two command line arguments are expected to be "-forkFOO"
+ * (where FOO indicates which postmaster child we are to become), and
+ * the name of a variables file that we can read to load data that would
+ * have been inherited by fork() on Unix. Remaining arguments go to the
+ * subprocess FooMain() routine.
+ */
+int
+SubPostmasterMain(int argc, char *argv[])
+{
+ Port port;
+ /* Do this sooner rather than later... */
+ IsUnderPostmaster = true; /* we are a postmaster subprocess now */
+
+ MyProcPid = getpid(); /* reset MyProcPid */
+
+ /* In EXEC_BACKEND case we will not have inherited these settings */
+ IsPostmasterEnvironment = true;
+ whereToSendOutput = None;
+
+ /* Setup essential subsystems (to ensure elog() behaves sanely) */
+ MemoryContextInit();
+ InitializeGUCOptions();
+
+ /* Read in the variables file */
+ memset(&port, 0, sizeof(Port));
+ read_backend_variables(argv[2], &port);
+
+ /* Check we got appropriate args */
+ if (argc < 3)
+ elog(FATAL, "invalid subpostmaster invocation");
+
+ /*
+ * If appropriate, physically re-attach to shared memory segment.
+ * We want to do this before going any further to ensure that we
+ * can attach at the same address the postmaster used.
+ */
+ if (strcmp(argv[1], "-forkbackend") == 0 ||
+ strcmp(argv[1], "-forkboot") == 0)
+ PGSharedMemoryReAttach();
+
+ /*
+ * Start our win32 signal implementation. This has to be done
+ * after we read the backend variables, because we need to pick
+ * up the signal pipe from the parent process.
+ */
#ifdef WIN32
- pid = win32_forkexec(postgres_exec_path, av); /* logs on error */
-#else
- /* Fire off execv in child */
- if ((pid = fork()) == 0 && (execv(postgres_exec_path, av) == -1))
+ pgwin32_signal_initialize();
+#endif
+
+ /* In EXEC_BACKEND case we will not have inherited these settings */
+ pqinitmask();
+ PG_SETMASK(&BlockSig);
+
+ /* Read in remaining GUC variables */
+ read_nondefault_variables();
+
+ /* Run backend or appropriate child */
+ if (strcmp(argv[1], "-forkbackend") == 0)
+ {
+ /* BackendRun will close sockets */
+
+ /* Attach process to shared data structures */
+ CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
+
+#ifdef USE_SSL
/*
- * FIXME: [fork/exec] suggestions for what to do here?
- * Probably OK to issue error (unlike pgstat case)
+ * Need to reinitialize the SSL library in the backend,
+ * since the context structures contain function pointers
+ * and cannot be passed through the parameter file.
*/
- abort();
+ if (EnableSSL)
+ secure_initialize();
#endif
- return pid; /* Parent returns pid */
+
+ Assert(argc == 3); /* shouldn't be any more args */
+ proc_exit(BackendRun(&port));
+ }
+ if (strcmp(argv[1], "-forkboot") == 0)
+ {
+ /* Close the postmaster's sockets */
+ ClosePostmasterPorts(false);
+
+ /* Attach process to shared data structures */
+ CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
+
+ BootstrapMain(argc - 2, argv + 2);
+ proc_exit(0);
+ }
+ if (strcmp(argv[1], "-forkarch") == 0)
+ {
+ /* Close the postmaster's sockets */
+ ClosePostmasterPorts(false);
+
+ /* Do not want to attach to shared memory */
+
+ PgArchiverMain(argc, argv);
+ proc_exit(0);
+ }
+ if (strcmp(argv[1], "-forkbuf") == 0)
+ {
+ /* Close the postmaster's sockets */
+ ClosePostmasterPorts(false);
+
+ /* Do not want to attach to shared memory */
+
+ PgstatBufferMain(argc, argv);
+ proc_exit(0);
+ }
+ if (strcmp(argv[1], "-forkcol") == 0)
+ {
+ /*
+ * Do NOT close postmaster sockets here, because we are forking
+ * from pgstat buffer process, which already did it.
+ */
+
+ /* Do not want to attach to shared memory */
+
+ PgstatCollectorMain(argc, argv);
+ proc_exit(0);
+ }
+ if (strcmp(argv[1], "-forklog") == 0)
+ {
+ /* Close the postmaster's sockets */
+ ClosePostmasterPorts(true);
+
+ /* Do not want to attach to shared memory */
+
+ SysLoggerMain(argc, argv);
+ proc_exit(0);
+ }
+
+ return 1; /* shouldn't get here */
}
-#endif
+#endif /* EXEC_BACKEND */
/*
*
* MUST -- vadim 05-10-1999
*/
- /* Should I use true instead? */
- ClosePostmasterPorts(false);
proc_exit(status);
}
PG_SETMASK(&BlockSig);
- if (CheckPostmasterSignal(PMSIGNAL_DO_CHECKPOINT))
- {
- if (CheckPointWarning != 0)
- {
- /*
- * This only times checkpoints forced by running out of
- * segment files. Other checkpoints could reduce the
- * frequency of forced checkpoints.
- */
- time_t now = time(NULL);
-
- if (LastSignalledCheckpoint != 0)
- {
- int elapsed_secs = now - LastSignalledCheckpoint;
-
- if (elapsed_secs < CheckPointWarning)
- ereport(LOG,
- (errmsg("checkpoints are occurring too frequently (%d seconds apart)",
- elapsed_secs),
- errhint("Consider increasing the configuration parameter \"checkpoint_segments\".")));
- }
- LastSignalledCheckpoint = now;
- }
-
- /*
- * Request to schedule a checkpoint
- *
- * Ignore request if checkpoint is already running or checkpointing
- * is currently disabled
- */
- if (CheckPointPID == 0 && checkpointed &&
- StartupPID == 0 && Shutdown == NoShutdown &&
- !FatalError && random_seed != 0)
- {
- CheckPointPID = CheckPointDataBase();
- /* note: if fork fails, CheckPointPID stays 0; nothing happens */
- }
- }
-
if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
{
/*
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
{
/*
- * Send SIGUSR2 to all children (triggers AsyncNotifyHandler). See
- * storage/ipc/sinvaladt.c for the use of this.
+ * Send SIGUSR1 to all children (triggers
+ * CatchupInterruptHandler). See storage/ipc/sinval[adt].c for the
+ * use of this.
*/
- if (Shutdown == NoShutdown)
- SignalChildren(SIGUSR2);
+ if (Shutdown <= SmartShutdown)
+ SignalChildren(SIGUSR1);
+ }
+
+ if (PgArchPID != 0 && Shutdown == NoShutdown)
+ {
+ if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
+ {
+ /*
+ * Send SIGUSR1 to archiver process, to wake it up and begin
+ * archiving next transaction log file.
+ */
+ kill(PgArchPID, SIGUSR1);
+ }
}
PG_SETMASK(&UnBlockSig);
* Dummy signal handler
*
* We use this for signals that we don't actually use in the postmaster,
- * but we do use in backends. If we SIG_IGN such signals in the postmaster,
- * then a newly started backend might drop a signal that arrives before it's
- * able to reconfigure its signal processing. (See notes in postgres.c.)
+ * but we do use in backends. If we were to SIG_IGN such signals in the
+ * postmaster, then a newly started backend might drop a signal that arrives
+ * before it's able to reconfigure its signal processing. (See notes in
+ * tcop/postgres.c.)
*/
static void
dummy_handler(SIGNAL_ARGS)
}
/*
- * Count up number of child processes.
+ * Count up number of child processes (regular backends only)
*/
static int
CountChildren(void)
{
Dlelem *curr;
- Backend *bp;
int cnt = 0;
for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
- {
- bp = (Backend *) DLE_VAL(curr);
- if (bp->pid != MyProcPid)
- cnt++;
- }
- /* Checkpoint and bgwriter will be in the list, discount them */
- if (CheckPointPID != 0)
- cnt--;
- if (BgWriterPID != 0)
- cnt--;
+ cnt++;
return cnt;
}
+
/*
- * Fire off a subprocess for startup/shutdown/checkpoint/bgwriter.
+ * StartChildProcess -- start a non-backend child process for the postmaster
*
- * Return value of SSDataBase is subprocess' PID, or 0 if failed to start subprocess
- * (0 is returned only for checkpoint/bgwriter cases).
+ * xlog determines what kind of child will be started. All child types
+ * initially go to BootstrapMain, which will handle common setup.
*
- * note: in the EXEC_BACKEND case, we delay the fork until argument list has been
- * established
+ * Return value of StartChildProcess is subprocess' PID, or 0 if failed
+ * to start subprocess.
*/
-NON_EXEC_STATIC void
-SSDataBaseInit(int xlop)
-{
- const char *statmsg;
-
- IsUnderPostmaster = true; /* we are a postmaster subprocess
- * now */
-
-#ifdef EXEC_BACKEND
- /* In EXEC case we will not have inherited these settings */
- IsPostmasterEnvironment = true;
- whereToSendOutput = None;
-#endif
-
- MyProcPid = getpid(); /* reset MyProcPid */
-
- /* Lose the postmaster's on-exit routines and port connections */
- on_exit_reset();
-
- /*
- * Identify myself via ps
- */
- switch (xlop)
- {
- case BS_XLOG_STARTUP:
- statmsg = "startup subprocess";
- break;
- case BS_XLOG_CHECKPOINT:
- statmsg = "checkpoint subprocess";
- break;
- case BS_XLOG_BGWRITER:
- statmsg = "bgwriter subprocess";
- break;
- case BS_XLOG_SHUTDOWN:
- statmsg = "shutdown subprocess";
- break;
- default:
- statmsg = "??? subprocess";
- break;
- }
- init_ps_display(statmsg, "", "");
- set_ps_display("");
-}
-
-
static pid_t
-SSDataBase(int xlop)
+StartChildProcess(int xlop)
{
pid_t pid;
- Backend *bn;
-#ifndef EXEC_BACKEND
+ char *av[10];
+ int ac = 0;
+ char xlbuf[32];
+
#ifdef LINUX_PROFILE
struct itimerval prof_itimer;
#endif
-#else
- char idbuf[32];
- char ddirbuf[MAXPGPATH];
+
+ /*
+ * Set up command-line arguments for subprocess
+ */
+ av[ac++] = "postgres";
+
+#ifdef EXEC_BACKEND
+ av[ac++] = "-forkboot";
+ av[ac++] = NULL; /* filled in by postmaster_forkexec */
#endif
+ snprintf(xlbuf, sizeof(xlbuf), "-x%d", xlop);
+ av[ac++] = xlbuf;
+
+ av[ac++] = "-p";
+ av[ac++] = "template1";
+
+ av[ac] = NULL;
+ Assert(ac < lengthof(av));
+
+ /*
+ * Flush stdio channels (see comments in BackendStartup)
+ */
fflush(stdout);
fflush(stderr);
-#ifndef EXEC_BACKEND
+#ifdef EXEC_BACKEND
+
+ pid = postmaster_forkexec(ac, av);
+
+#else /* !EXEC_BACKEND */
+
#ifdef LINUX_PROFILE
- /* see comments in BackendRun */
+ /* see comments in BackendStartup */
getitimer(ITIMER_PROF, &prof_itimer);
#endif
beos_before_backend_startup();
#endif
- /* Non EXEC_BACKEND case; fork here */
- if ((pid = fork()) == 0) /* child */
-#endif
- {
- char *av[10];
- int ac = 0;
- char nbbuf[32];
- char xlbuf[32];
+ pid = fork();
-#ifndef EXEC_BACKEND
+ if (pid == 0) /* child */
+ {
#ifdef LINUX_PROFILE
setitimer(ITIMER_PROF, &prof_itimer, NULL);
#endif
beos_backend_startup();
#endif
- /* Close the postmaster's sockets */
- ClosePostmasterPorts(true);
-
- SSDataBaseInit(xlop);
-#else
- if (!write_backend_variables(NULL))
- return -1; /* log issued by write_backend_variables */
-#endif
-
- /* Set up command-line arguments for subprocess */
- av[ac++] = "postgres";
-
-#ifdef EXEC_BACKEND
- av[ac++] = "-boot";
-#endif
- snprintf(nbbuf, sizeof(nbbuf), "-B%d", NBuffers);
- av[ac++] = nbbuf;
-
- snprintf(xlbuf, sizeof(xlbuf), "-x%d", xlop);
- av[ac++] = xlbuf;
+ IsUnderPostmaster = true; /* we are a postmaster subprocess
+ * now */
-#ifdef EXEC_BACKEND
- /* pass data dir before end of secure switches (-p) */
- snprintf(ddirbuf, MAXPGPATH, "\"%s\"", DataDir);
- av[ac++] = "-D";
- av[ac++] = ddirbuf;
-
- /* and the backend identifier + dbname */
- snprintf(idbuf, sizeof(idbuf), "-p%lu,template1", tmpBackendFileNum);
- av[ac++] = idbuf;
-#else
- av[ac++] = "-p";
- av[ac++] = "template1";
-#endif
+ /* Close the postmaster's sockets */
+ ClosePostmasterPorts(false);
- av[ac] = NULL;
+ /* Lose the postmaster's on-exit routines and port connections */
+ on_exit_reset();
- Assert(ac < lengthof(av));
+ /* Release postmaster's working memory context */
+ MemoryContextSwitchTo(TopMemoryContext);
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
-#ifdef EXEC_BACKEND
- /* EXEC_BACKEND case; fork/exec here */
-#ifdef WIN32
- pid = win32_forkexec(postgres_exec_path, av); /* logs on error */
-#else
- if ((pid = fork()) == 0 && (execv(postgres_exec_path, av) == -1))
- {
- /* in child */
- elog(ERROR, "unable to execv in SSDataBase: %m");
- exit(0);
- }
-#endif
-#else
BootstrapMain(ac, av);
ExitPostmaster(0);
-#endif
}
+#endif /* EXEC_BACKEND */
- /* in parent */
if (pid < 0)
{
-#ifndef EXEC_BACKEND
+ /* in parent, fork failed */
+ int save_errno = errno;
+
#ifdef __BEOS__
/* Specific beos actions before backend startup */
beos_backend_startup_failed();
#endif
-#endif
+ errno = save_errno;
switch (xlop)
{
case BS_XLOG_STARTUP:
ereport(LOG,
(errmsg("could not fork startup process: %m")));
break;
- case BS_XLOG_CHECKPOINT:
- ereport(LOG,
- (errmsg("could not fork checkpoint process: %m")));
- break;
case BS_XLOG_BGWRITER:
ereport(LOG,
- (errmsg("could not fork bgwriter process: %m")));
- break;
- case BS_XLOG_SHUTDOWN:
- ereport(LOG,
- (errmsg("could not fork shutdown process: %m")));
+ (errmsg("could not fork background writer process: %m")));
break;
default:
ereport(LOG,
}
/*
- * fork failure is fatal during startup/shutdown, but there's no
- * need to choke if a routine checkpoint or starting a background
- * writer fails.
+ * fork failure is fatal during startup, but there's no need to
+ * choke immediately if starting other child types fails.
*/
- if (xlop == BS_XLOG_CHECKPOINT)
- return 0;
- if (xlop == BS_XLOG_BGWRITER)
- return 0;
- ExitPostmaster(1);
+ if (xlop == BS_XLOG_STARTUP)
+ ExitPostmaster(1);
+ return 0;
}
/*
- * The startup and shutdown processes are not considered normal
- * backends, but the checkpoint and bgwriter processes are.
- * They must be added to the list of backends.
+ * in parent, successful fork
*/
- if (xlop == BS_XLOG_CHECKPOINT || xlop == BS_XLOG_BGWRITER)
- {
- if (!(bn = (Backend *) malloc(sizeof(Backend))))
- {
- ereport(LOG,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory")));
- ExitPostmaster(1);
- }
-
- bn->pid = pid;
- bn->cancel_key = PostmasterRandom();
-#ifdef EXEC_BACKEND
- ShmemBackendArrayAdd(bn);
-#endif
- DLAddHead(BackendList, DLNewElem(bn));
-
- /*
- * Since this code is executed periodically, it's a fine place to
- * do other actions that should happen every now and then on no
- * particular schedule. Such as...
- */
- TouchSocketFile();
- TouchSocketLockFile();
- }
-
return pid;
}
fprintf(fp, "%s", fullprogname);
for (i = 1; i < argc; i++)
- fprintf(fp, " '%s'", argv[i]);
+ fprintf(fp, " %s%s%s", SYSTEMQUOTE, argv[i], SYSTEMQUOTE);
fputs("\n", fp);
if (fclose(fp))
return true;
}
-/*
- * This should be used only for reporting "interactive" errors (essentially,
- * bogus arguments on the command line). Once the postmaster is launched,
- * use ereport. In particular, don't use this for anything that occurs
- * after pmdaemonize.
- */
-static void
-postmaster_error(const char *fmt,...)
-{
- va_list ap;
-
- fprintf(stderr, "%s: ", progname);
- va_start(ap, fmt);
- vfprintf(stderr, gettext(fmt), ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-
#ifdef EXEC_BACKEND
/*
- * The following need to be available to the read/write_backend_variables
+ * The following need to be available to the save/restore_backend_variables
* functions
*/
-#include "storage/spin.h"
-extern XLogRecPtr RedoRecPtr;
-extern XLogwrtResult LogwrtResult;
extern slock_t *ShmemLock;
extern slock_t *ShmemIndexLock;
extern void *ShmemIndexAlloc;
-typedef struct LWLock LWLock;
extern LWLock *LWLockArray;
-extern slock_t *ProcStructLock;
+extern slock_t *ProcStructLock;
extern int pgStatSock;
+extern int pgStatPipe[2];
+
+#ifndef WIN32
+#define write_inheritable_socket(dest, src, childpid) (*(dest) = (src))
+#define read_inheritable_socket(dest, src) (*(dest) = *(src))
+#else
+static void write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE child);
+static void write_inheritable_socket(InheritableSocket *dest, SOCKET src,
+ pid_t childPid);
+static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
+#endif
-#define write_var(var,fp) fwrite((void*)&(var),sizeof(var),1,fp)
-#define read_var(var,fp) fread((void*)&(var),sizeof(var),1,fp)
-#define get_tmp_backend_file_name(buf,id) \
- do { \
- Assert(DataDir); \
- sprintf((buf), \
- "%s/%s/%s.backend_var.%lu", \
- DataDir, \
- PG_TEMP_FILES_DIR, \
- PG_TEMP_FILE_PREFIX, \
- (id)); \
- } while (0)
+/* Save critical backend variables into the BackendParameters struct */
+#ifndef WIN32
+static bool
+save_backend_variables(BackendParameters *param, Port *port)
+#else
static bool
-write_backend_variables(Port *port)
+save_backend_variables(BackendParameters *param, Port *port,
+ HANDLE childProcess, pid_t childPid)
+#endif
{
- char filename[MAXPGPATH];
- FILE *fp;
- get_tmp_backend_file_name(filename,++tmpBackendFileNum);
+ memcpy(¶m->port, port, sizeof(Port));
+ write_inheritable_socket(¶m->portsocket, port->sock, childPid);
- /* Open file */
- fp = AllocateFile(filename, PG_BINARY_W);
- if (!fp)
- {
- /* As per OpenTemporaryFile... */
- char dirname[MAXPGPATH];
- sprintf(dirname,"%s/%s",DataDir,PG_TEMP_FILES_DIR);
- mkdir(dirname, S_IRWXU);
+ StrNCpy(param->DataDir, DataDir, MAXPGPATH);
- fp = AllocateFile(filename, PG_BINARY_W);
- if (!fp)
- {
+ memcpy(¶m->ListenSocket, &ListenSocket, sizeof(ListenSocket));
+
+ param->MyCancelKey = MyCancelKey;
+
+ param->UsedShmemSegID = UsedShmemSegID;
+ param->UsedShmemSegAddr = UsedShmemSegAddr;
+
+ param->ShmemLock = ShmemLock;
+ param->ShmemIndexLock = ShmemIndexLock;
+ param->ShmemVariableCache = ShmemVariableCache;
+ param->ShmemIndexAlloc = ShmemIndexAlloc;
+ param->ShmemBackendArray = ShmemBackendArray;
+
+ param->LWLockArray = LWLockArray;
+ param->ProcStructLock = ProcStructLock;
+ write_inheritable_socket(¶m->pgStatSock, pgStatSock, childPid);
+ write_inheritable_socket(¶m->pgStatPipe0, pgStatPipe[0], childPid);
+ write_inheritable_socket(¶m->pgStatPipe1, pgStatPipe[1], childPid);
+
+ param->PostmasterPid = PostmasterPid;
+
+#ifdef WIN32
+ param->PostmasterHandle = PostmasterHandle;
+ write_duplicated_handle(¶m->initial_signal_pipe,
+ pgwin32_create_signal_listener(childPid),
+ childProcess);
+#endif
+
+ memcpy(¶m->syslogPipe, &syslogPipe, sizeof(syslogPipe));
+
+ StrNCpy(param->my_exec_path, my_exec_path, MAXPGPATH);
+
+ StrNCpy(param->ExtraOptions, ExtraOptions, MAXPGPATH);
+
+ StrNCpy(param->lc_collate, setlocale(LC_COLLATE, NULL), LOCALE_NAME_BUFLEN);
+ StrNCpy(param->lc_ctype, setlocale(LC_CTYPE, NULL), LOCALE_NAME_BUFLEN);
+
+ return true;
+}
+
+
+#ifdef WIN32
+/*
+ * Duplicate a handle for usage in a child process, and write the child
+ * process instance of the handle to the parameter file.
+ */
+static void
+write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE childProcess)
+{
+ HANDLE hChild = INVALID_HANDLE_VALUE;
+
+ if (!DuplicateHandle(GetCurrentProcess(),
+ src,
+ childProcess,
+ &hChild,
+ 0,
+ TRUE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
+ ereport(ERROR,
+ (errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %d",
+ (int) GetLastError())));
+
+ *dest = hChild;
+}
+
+/*
+ * Duplicate a socket for usage in a child process, and write the resulting
+ * structure to the parameter file.
+ * This is required because a number of LSPs (Layered Service Providers) very
+ * common on Windows (antivirus, firewalls, download managers etc) break
+ * straight socket inheritance.
+ */
+static void
+write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid)
+{
+ dest->origsocket = src;
+ if (src != 0 && src != -1)
+ {
+ /* Actual socket */
+ if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0)
ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to file \"%s\": %m", filename)));
- return false;
- }
+ (errmsg("could not duplicate socket %d for use in backend: error code %d",
+ src, WSAGetLastError())));
}
+}
+
+/*
+ * Read a duplicate socket structure back, and get the socket descriptor.
+ */
+static void
+read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
+{
+ SOCKET s;
- /* Write vars */
- if (port)
+ if (src->origsocket == -1 || src->origsocket == 0)
{
- write_var(port->sock,fp);
- write_var(port->proto,fp);
- write_var(port->laddr,fp);
- write_var(port->raddr,fp);
- write_var(port->canAcceptConnections,fp);
- write_var(port->cryptSalt,fp);
- write_var(port->md5Salt,fp);
+ /* Not a real socket! */
+ *dest = src->origsocket;
}
- write_var(MyCancelKey,fp);
+ else
+ {
+ /* Actual socket, so create from structure */
+ s = WSASocket(FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ &src->wsainfo,
+ 0,
+ 0);
+ if (s == INVALID_SOCKET)
+ {
+ write_stderr("could not create inherited socket: error code %d\n",
+ WSAGetLastError());
+ exit(1);
+ }
+ *dest = s;
- write_var(RedoRecPtr,fp);
- write_var(LogwrtResult,fp);
+ /*
+ * To make sure we don't get two references to the same socket,
+ * close the original one. (This would happen when inheritance
+ * actually works..
+ */
+ closesocket(src->origsocket);
+ }
+}
+#endif
- write_var(UsedShmemSegID,fp);
- write_var(UsedShmemSegAddr,fp);
+static void
+read_backend_variables(char *id, Port *port)
+{
+ BackendParameters param;
- write_var(ShmemLock,fp);
- write_var(ShmemIndexLock,fp);
- write_var(ShmemVariableCache,fp);
- write_var(ShmemIndexAlloc,fp);
- write_var(ShmemBackendArray,fp);
+#ifndef WIN32
+ /* Non-win32 implementation reads from file */
+ FILE *fp;
- write_var(LWLockArray,fp);
- write_var(ProcStructLock,fp);
- write_var(pgStatSock,fp);
+ /* Open file */
+ fp = AllocateFile(id, PG_BINARY_R);
+ if (!fp)
+ {
+ write_stderr("could not read from backend variables file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
- write_var(PreAuthDelay,fp);
- write_var(debug_flag,fp);
- write_var(PostmasterPid,fp);
+ if (fread(¶m, sizeof(param), 1, fp) != 1)
+ {
+ write_stderr("could not read from backend variables file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
/* Release file */
- if (FreeFile(fp))
+ FreeFile(fp);
+ if (unlink(id) != 0)
{
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to file \"%s\": %m", filename)));
- return false;
+ write_stderr("could not remove file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
}
+#else
+ /* Win32 version uses mapped file */
+ HANDLE paramHandle;
+ BackendParameters *paramp;
- return true;
-}
+ paramHandle = (HANDLE)atol(id);
+ paramp = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
+ if (!paramp)
+ {
+ write_stderr("could not map view of backend variables: error code %d\n",
+ (int) GetLastError());
+ exit(1);
+ }
-void
-read_backend_variables(unsigned long id, Port *port)
-{
- char filename[MAXPGPATH];
- FILE *fp;
- get_tmp_backend_file_name(filename,id);
+ memcpy(¶m, paramp, sizeof(BackendParameters));
- /* Open file */
- fp = AllocateFile(filename, PG_BINARY_R);
- if (!fp)
+ if (!UnmapViewOfFile(paramp))
{
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not read from backend_variables file \"%s\": %m", filename)));
- return;
+ write_stderr("could not unmap view of backend variables: error code %d\n",
+ (int) GetLastError());
+ exit(1);
}
- /* Read vars */
- if (port)
+ if (!CloseHandle(paramHandle))
{
- read_var(port->sock,fp);
- read_var(port->proto,fp);
- read_var(port->laddr,fp);
- read_var(port->raddr,fp);
- read_var(port->canAcceptConnections,fp);
- read_var(port->cryptSalt,fp);
- read_var(port->md5Salt,fp);
+ write_stderr("could not close handle to backend parameter variables: error code %d\n",
+ (int) GetLastError());
+ exit(1);
}
- read_var(MyCancelKey,fp);
+#endif
- read_var(RedoRecPtr,fp);
- read_var(LogwrtResult,fp);
+ restore_backend_variables(¶m, port);
+}
- read_var(UsedShmemSegID,fp);
- read_var(UsedShmemSegAddr,fp);
+/* Restore critical backend variables from the BackendParameters struct */
+static void
+restore_backend_variables(BackendParameters *param, Port *port)
+{
+ memcpy(port, ¶m->port, sizeof(Port));
+ read_inheritable_socket(&port->sock, ¶m->portsocket);
- read_var(ShmemLock,fp);
- read_var(ShmemIndexLock,fp);
- read_var(ShmemVariableCache,fp);
- read_var(ShmemIndexAlloc,fp);
- read_var(ShmemBackendArray,fp);
+ SetDataDir(param->DataDir);
- read_var(LWLockArray,fp);
- read_var(ProcStructLock,fp);
- read_var(pgStatSock,fp);
+ memcpy(&ListenSocket, ¶m->ListenSocket, sizeof(ListenSocket));
- read_var(PreAuthDelay,fp);
- read_var(debug_flag,fp);
- read_var(PostmasterPid,fp);
+ MyCancelKey = param->MyCancelKey;
- /* Release file */
- FreeFile(fp);
- if (unlink(filename) != 0)
- ereport(WARNING,
- (errcode_for_file_access(),
- errmsg("could not remove file \"%s\": %m", filename)));
+ UsedShmemSegID = param->UsedShmemSegID;
+ UsedShmemSegAddr = param->UsedShmemSegAddr;
+
+ ShmemLock = param->ShmemLock;
+ ShmemIndexLock = param->ShmemIndexLock;
+ ShmemVariableCache = param->ShmemVariableCache;
+ ShmemIndexAlloc = param->ShmemIndexAlloc;
+ ShmemBackendArray = param->ShmemBackendArray;
+
+ LWLockArray = param->LWLockArray;
+ ProcStructLock = param->ProcStructLock;
+ read_inheritable_socket(&pgStatSock, ¶m->pgStatSock);
+ read_inheritable_socket(&pgStatPipe[0], ¶m->pgStatPipe0);
+ read_inheritable_socket(&pgStatPipe[1], ¶m->pgStatPipe1);
+
+ PostmasterPid = param->PostmasterPid;
+
+#ifdef WIN32
+ PostmasterHandle = param->PostmasterHandle;
+ pgwin32_initial_signal_pipe = param->initial_signal_pipe;
+#endif
+
+ memcpy(&syslogPipe, ¶m->syslogPipe, sizeof(syslogPipe));
+
+ StrNCpy(my_exec_path, param->my_exec_path, MAXPGPATH);
+
+ StrNCpy(ExtraOptions, param->ExtraOptions, MAXPGPATH);
+
+ setlocale(LC_COLLATE, param->lc_collate);
+ setlocale(LC_CTYPE, param->lc_ctype);
}
-size_t ShmemBackendArraySize(void)
+size_t
+ShmemBackendArraySize(void)
{
- return (NUM_BACKENDARRAY_ELEMS*sizeof(Backend));
+ return (NUM_BACKENDARRAY_ELEMS * sizeof(Backend));
}
-void ShmemBackendArrayAllocation(void)
+void
+ShmemBackendArrayAllocation(void)
{
- size_t size = ShmemBackendArraySize();
- ShmemBackendArray = (Backend*)ShmemAlloc(size);
+ size_t size = ShmemBackendArraySize();
+
+ ShmemBackendArray = (Backend *) ShmemAlloc(size);
+ /* Mark all slots as empty */
memset(ShmemBackendArray, 0, size);
}
-static void ShmemBackendArrayAdd(Backend *bn)
+static void
+ShmemBackendArrayAdd(Backend *bn)
{
- int i;
+ int i;
+
+ /* Find an empty slot */
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
{
- /* Find an empty slot */
if (ShmemBackendArray[i].pid == 0)
{
ShmemBackendArray[i] = *bn;
}
ereport(FATAL,
- (errmsg_internal("unable to add backend entry")));
+ (errmsg_internal("no free slots in shmem backend array")));
}
-static void ShmemBackendArrayRemove(pid_t pid)
+static void
+ShmemBackendArrayRemove(pid_t pid)
{
- int i;
+ int i;
+
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
{
if (ShmemBackendArray[i].pid == pid)
}
ereport(WARNING,
- (errmsg_internal("unable to find backend entry with pid %d",
- pid)));
+ (errmsg_internal("could not find backend entry with pid %d",
+ (int) pid)));
}
-#endif
-
-#ifdef WIN32
-
-pid_t win32_forkexec(const char* path, char *argv[])
-{
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- char *p;
- int i;
- char cmdLine[MAXPGPATH];
- HANDLE childHandleCopy;
- HANDLE waiterThread;
-
- /* Format the cmd line */
- snprintf(cmdLine,sizeof(cmdLine),"\"%s\"",path);
- i = 0;
- while (argv[++i] != NULL)
- {
- /* FIXME: [fork/exec] some strlen checks might be prudent here */
- strcat(cmdLine," ");
- strcat(cmdLine,argv[i]);
- }
-
- /*
- * The following snippet can disappear when we consistently
- * use forward slashes.
- */
- p = cmdLine;
- while (*(p++) != '\0')
- if (*p == '/') *p = '\\';
-
- memset(&pi,0,sizeof(pi));
- memset(&si,0,sizeof(si));
- si.cb = sizeof(si);
- if (!CreateProcess(NULL,cmdLine,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi))
- {
- elog(ERROR,"CreateProcess call failed (%i): %m",(int)GetLastError());
- return -1;
- }
-
- if (!IsUnderPostmaster)
- /* We are the Postmaster creating a child... */
- win32_AddChild(pi.dwProcessId,pi.hProcess);
-
- if (!DuplicateHandle(GetCurrentProcess(),
- pi.hProcess,
- GetCurrentProcess(),
- &childHandleCopy,
- 0,
- FALSE,
- DUPLICATE_SAME_ACCESS))
- ereport(FATAL,
- (errmsg_internal("failed to duplicate child handle: %i",(int)GetLastError())));
- waiterThread = CreateThread(NULL, 64*1024, win32_sigchld_waiter, (LPVOID)childHandleCopy, 0, NULL);
- if (!waiterThread)
- ereport(FATAL,
- (errmsg_internal("failed to create sigchld waiter thread: %i",(int)GetLastError())));
- CloseHandle(waiterThread);
+#endif /* EXEC_BACKEND */
- if (IsUnderPostmaster)
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- return pi.dwProcessId;
-}
+#ifdef WIN32
/*
- * Note: The following three functions must not be interrupted (eg. by signals).
- * As the Postgres Win32 signalling architecture (currently) requires polling,
- * or APC checking functions which aren't used here, this is not an issue.
+ * Note: The following three functions must not be interrupted (eg. by
+ * signals). As the Postgres Win32 signalling architecture (currently)
+ * requires polling, or APC checking functions which aren't used here, this
+ * is not an issue.
*
- * We keep two separate arrays, instead of a single array of pid/HANDLE structs,
- * to avoid having to re-create a handle array for WaitForMultipleObjects on
- * each call to win32_waitpid.
+ * We keep two separate arrays, instead of a single array of pid/HANDLE
+ * structs, to avoid having to re-create a handle array for
+ * WaitForMultipleObjects on each call to win32_waitpid.
*/
-static void win32_AddChild(pid_t pid, HANDLE handle)
+static void
+win32_AddChild(pid_t pid, HANDLE handle)
{
Assert(win32_childPIDArray && win32_childHNDArray);
if (win32_numChildren < NUM_BACKENDARRAY_ELEMS)
}
else
ereport(FATAL,
- (errmsg_internal("unable to add child entry with pid %lu",
- pid)));
+ (errmsg_internal("no room for child entry with pid %lu",
+ (unsigned long) pid)));
}
-static void win32_RemoveChild(pid_t pid)
+static void
+win32_RemoveChild(pid_t pid)
{
- int i;
+ int i;
+
Assert(win32_childPIDArray && win32_childHNDArray);
for (i = 0; i < win32_numChildren; i++)
}
ereport(WARNING,
- (errmsg_internal("unable to find child entry with pid %lu",
- pid)));
+ (errmsg_internal("could not find child entry with pid %lu",
+ (unsigned long) pid)));
}
-static pid_t win32_waitpid(int *exitstatus)
+static pid_t
+win32_waitpid(int *exitstatus)
{
+ /*
+ * Note: Do NOT use WaitForMultipleObjectsEx, as we don't want to run
+ * queued APCs here.
+ */
+ int index;
+ DWORD exitCode;
+ DWORD ret;
+ unsigned long offset;
+
Assert(win32_childPIDArray && win32_childHNDArray);
- elog(DEBUG3,"waiting on %lu children",win32_numChildren);
+ elog(DEBUG3, "waiting on %lu children", win32_numChildren);
- if (win32_numChildren > 0)
+ for (offset = 0; offset < win32_numChildren; offset += MAXIMUM_WAIT_OBJECTS)
{
- /*
- * Note: Do NOT use WaitForMultipleObjectsEx, as we don't
- * want to run queued APCs here.
- */
- int index;
- DWORD exitCode;
- DWORD ret = WaitForMultipleObjects(win32_numChildren,win32_childHNDArray,FALSE,0);
+ unsigned long num = Min(MAXIMUM_WAIT_OBJECTS, win32_numChildren - offset);
+ ret = WaitForMultipleObjects(num, &win32_childHNDArray[offset], FALSE, 0);
switch (ret)
{
case WAIT_FAILED:
- ereport(ERROR,
- (errmsg_internal("failed to wait on %lu children: %i",
- win32_numChildren,(int)GetLastError())));
- /* Fall through to WAIT_TIMEOUTs return */
+ ereport(LOG,
+ (errmsg_internal("failed to wait on %lu of %lu children: error code %d",
+ num, win32_numChildren, (int) GetLastError())));
+ return -1;
case WAIT_TIMEOUT:
- /* No children have finished */
- return -1;
+ /* No children (in this chunk) have finished */
+ break;
default:
- /* Get the exit code, and return the PID of, the respective process */
- index = ret-WAIT_OBJECT_0;
+
+ /*
+ * Get the exit code, and return the PID of, the
+ * respective process
+ */
+ index = offset + ret - WAIT_OBJECT_0;
Assert(index >= 0 && index < win32_numChildren);
- if (!GetExitCodeProcess(win32_childHNDArray[index],&exitCode))
+ if (!GetExitCodeProcess(win32_childHNDArray[index], &exitCode))
+ {
/*
- * If we get this far, this should never happen, but, then again...
- * No choice other than to assume a catastrophic failure.
+ * If we get this far, this should never happen, but,
+ * then again... No choice other than to assume a
+ * catastrophic failure.
*/
ereport(FATAL,
(errmsg_internal("failed to get exit code for child %lu",
- win32_childPIDArray[index])));
- *exitstatus = (int)exitCode;
+ (unsigned long) win32_childPIDArray[index])));
+ }
+ *exitstatus = (int) exitCode;
return win32_childPIDArray[index];
}
}
- /* No children */
+ /* No children have finished */
return -1;
}
-/* Note! Code belows executes on separate threads, one for
- each child process created */
-static DWORD WINAPI win32_sigchld_waiter(LPVOID param) {
- HANDLE procHandle = (HANDLE)param;
+/*
+ * Note! Code below executes on separate threads, one for
+ * each child process created
+ */
+static DWORD WINAPI
+win32_sigchld_waiter(LPVOID param)
+{
+ HANDLE procHandle = (HANDLE) param;
+
+ DWORD r = WaitForSingleObject(procHandle, INFINITE);
- DWORD r = WaitForSingleObject(procHandle, INFINITE);
if (r == WAIT_OBJECT_0)
pg_queue_signal(SIGCHLD);
else
- fprintf(stderr,"ERROR: Failed to wait on child process handle: %i\n",(int)GetLastError());
+ write_stderr("could not wait on child process handle: error code %d\n",
+ (int) GetLastError());
CloseHandle(procHandle);
return 0;
}
-#endif
+#endif /* WIN32 */