* message to setup a backend process.
*
* The postmaster also manages system-wide operations such as
- * startup and shutdown. The postmaster itself doesn't do those
+ * 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.
* clients.
*
*
- * Portions Copyright (c) 1996-2004, 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.423 2004/08/29 04:12:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.445 2005/02/22 04:36:36 momjian Exp $
*
* NOTES
*
* 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
+ * postmaster is launched, use ereport(). In particular, don't use
* write_stderr() for anything that occurs after pmdaemonize.
*
*-------------------------------------------------------------------------
#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
#endif
+#include "catalog/pg_control.h"
#include "catalog/pg_database.h"
#include "commands/async.h"
#include "lib/dllist.h"
#include "bootstrap/bootstrap.h"
#include "pgstat.h"
+#ifdef EXEC_BACKEND
+#include "storage/spin.h"
+#endif
+
/*
* List of active backends (or child processes anyway; we don't actually
static const char *progname = NULL;
/* The socket(s) we're listening to. */
-#define MAXLISTEN 10
+#define MAXLISTEN 64
static int ListenSocket[MAXLISTEN];
/*
*/
static unsigned int random_seed = 0;
-static int debug_flag = 0;
-
extern char *optarg;
extern int optind,
opterr;
/*
* postmaster.c - function prototypes
*/
-static void checkDataDir(const char *checkdir);
-static bool onlyConfigSpecified(const char *checkdir);
+static void checkDataDir(void);
+
#ifdef USE_RENDEZVOUS
static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode,
- void *context);
+ void *context);
#endif
static void pmdaemonize(void);
static Port *ConnCreate(int serverFd);
#ifdef EXEC_BACKEND
#ifdef WIN32
-static 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 pid_t win32_waitpid(int *exitstatus);
static HANDLE *win32_childHNDArray;
static unsigned long win32_numChildren = 0;
-HANDLE PostmasterHandle;
+HANDLE PostmasterHandle;
#endif
static pid_t backend_forkexec(Port *port);
static pid_t internal_forkexec(int argc, char *argv[], Port *port);
-static void read_backend_variables(char *filename, Port *port);
-static bool write_backend_variables(char *filename, Port *port);
+/* Type for a socket that can be inherited to a client process */
+#ifdef WIN32
+typedef struct
+{
+ SOCKET origsocket; /* Original socket value, or -1 if not a socket */
+ WSAPROTOCOL_INFO wsainfo;
+} InheritableSocket;
+#else
+typedef int InheritableSocket;
+#endif
+
+typedef struct LWLock LWLock; /* ugly kluge */
+
+/*
+ * 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;
+
+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);
-#endif /* EXEC_BACKEND */
+#endif /* EXEC_BACKEND */
#define StartupDataBase() StartChildProcess(BS_XLOG_STARTUP)
#define StartBackgroundWriter() StartChildProcess(BS_XLOG_BGWRITER)
{
int opt;
int status;
- char *userPGDATA = NULL;
+ char *userDoption = NULL;
int i;
- progname = get_progname(argv[0]);
+ /* This will call exit() if strdup() fails. */
+ progname = get_progname(argv[0]);
MyProcPid = PostmasterPid = getpid();
}
}
+#ifdef WIN32
+ /* Start our win32 signal implementation */
+ pgwin32_signal_initialize();
+#endif
+
/*
* for security, no dir or file created can be group or other
* accessible
*/
InitializeGUCOptions();
- userPGDATA = 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)
/* Can no longer set the backend executable file to use. */
break;
case 'D':
- userPGDATA = 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;
case 'o':
/*
- * Other options to pass to the backend on the command line
+ * Other options to pass to the backend on the command
+ * line
*/
snprintf(ExtraOptions + strlen(ExtraOptions),
sizeof(ExtraOptions) - strlen(ExtraOptions),
ExitPostmaster(1);
}
- if (userPGDATA)
- {
- userPGDATA = strdup(userPGDATA);
- canonicalize_path(userPGDATA);
- }
-
- if (onlyConfigSpecified(userPGDATA))
- {
- /*
- * It is either a file name or a directory with no
- * global/pg_control file, and hence not a data directory.
- */
- user_pgconfig = userPGDATA;
- ProcessConfigFile(PGC_POSTMASTER);
-
- if (!guc_pgdata) /* Got a pgdata from the config file? */
- {
- write_stderr("%s does not know where to find the database system data.\n"
- "This should be specified as \"pgdata\" in %s%s.\n",
- progname, userPGDATA,
- user_pgconfig_is_dir ? "/postgresql.conf" : "");
- ExitPostmaster(2);
- }
- checkDataDir(guc_pgdata);
- SetDataDir(guc_pgdata);
- }
- else
- {
- /* Now we can set the data directory, and then read postgresql.conf. */
- checkDataDir(userPGDATA);
- SetDataDir(userPGDATA);
- ProcessConfigFile(PGC_POSTMASTER);
- }
-
- if (external_pidfile)
- {
- FILE *fpidfile = fopen(external_pidfile, "w");
-
- if (fpidfile)
- {
- fprintf(fpidfile, "%d\n", MyProcPid);
- fclose(fpidfile);
- /* Should we remove the pid file on postmaster exit? */
- }
- else
- fprintf(stderr,
- gettext("%s could not write to external pid file %s\n"),
- progname, external_pidfile);
- }
-
- /* If timezone is not set, determine what the OS uses */
- pg_timezone_initialize();
+ /*
+ * Locate the proper configuration files and data directory, and
+ * read postgresql.conf for the first time.
+ */
+ if (!SelectConfigFiles(userDoption, progname))
+ ExitPostmaster(2);
-#ifdef EXEC_BACKEND
- write_nondefault_variables(PGC_POSTMASTER);
-#endif
+ /* Verify that DataDir looks reasonable */
+ checkDataDir();
/*
* Check for invalid combinations of GUC settings.
if (find_other_exec(argv[0], "postgres", PG_VERSIONSTR,
postgres_exec_path) < 0)
ereport(FATAL,
- (errmsg("%s: could not locate matching postgres executable",
- progname)));
+ (errmsg("%s: could not locate matching postgres executable",
+ progname)));
#endif
/*
* 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(s) 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 *rawstring;
- List *elemlist;
- ListCell *l;
+ char *rawstring;
+ List *elemlist;
+ ListCell *l;
/* Need a modifiable copy of ListenAddresses */
rawstring = pstrdup(ListenAddresses);
/* Parse string into list of identifiers */
- if (!SplitIdentifierString(rawstring, ',', &elemlist))
+ if (!SplitIdentifierString(rawstring, ',', &elemlist))
{
/* syntax error in list */
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid list syntax for \"listen_addresses\"")));
+ errmsg("invalid list syntax for \"listen_addresses\"")));
}
foreach(l, elemlist)
{
- char *curhost = (char *) lfirst(l);
+ char *curhost = (char *) lfirst(l);
if (strcmp(curhost, "*") == 0)
status = StreamServerPort(AF_UNSPEC, NULL,
BackendList = DLNewList();
#ifdef WIN32
+
/*
* Initialize the child pid/HANDLE arrays for signal handling.
*/
TRUE,
DUPLICATE_SAME_ACCESS) == 0)
ereport(FATAL,
- (errmsg_internal("could not duplicate postmaster handle: %d",
- (int) GetLastError())));
+ (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.
*
/*
* 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.
+ * Log_destination permits. We don't do this until the postmaster is
+ * fully launched, since startup failures may as well be reported to
+ * stderr.
*/
whereToSendOutput = None;
pgstat_init();
/*
- * 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...
*/
StartupPID = StartupDataBase();
-#ifdef EXEC_BACKEND
- write_nondefault_variables(PGC_POSTMASTER);
-#endif
-
status = ServerLoop();
/*
}
-
-static bool
-onlyConfigSpecified(const char *checkdir)
-{
- char path[MAXPGPATH];
- struct stat stat_buf;
-
- if (checkdir == NULL) /* checkDataDir handles this */
- return FALSE;
-
- if (stat(checkdir, &stat_buf) == -1) /* ditto */
- return FALSE;
-
- if (S_ISREG(stat_buf.st_mode)) /* It's a regular file, so assume it's explict */
- return TRUE;
- else if (S_ISDIR(stat_buf.st_mode)) /* It's a directory, is it a config or system dir? */
- {
- snprintf(path, MAXPGPATH, "%s/global/pg_control", checkdir);
- /* If this is not found, it is a config-only directory */
- if (stat(path, &stat_buf) == -1)
- return TRUE;
- }
- return FALSE;
-}
-
-
/*
* Validate the proposed data directory
*/
static void
-checkDataDir(const char *checkdir)
+checkDataDir(void)
{
char path[MAXPGPATH];
FILE *fp;
struct stat stat_buf;
- if (checkdir == NULL)
- {
- write_stderr("%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);
- }
+ Assert(DataDir);
- if (stat(checkdir, &stat_buf) == -1)
+ if (stat(DataDir, &stat_buf) != 0)
{
if (errno == ENOENT)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("data directory \"%s\" does not exist",
- checkdir)));
+ DataDir)));
else
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not read permissions of directory \"%s\": %m",
- checkdir)));
+ DataDir)));
}
/*
* 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 !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",
- checkdir),
+ DataDir),
errdetail("Permissions should be u=rwx (0700).")));
#endif
/* Look for PG_VERSION before looking for pg_control */
- ValidatePgVersion(checkdir);
+ ValidatePgVersion(DataDir);
- snprintf(path, sizeof(path), "%s/global/pg_control", checkdir);
+ 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, checkdir, path, strerror(errno));
+ progname, DataDir, path, strerror(errno));
ExitPostmaster(2);
}
FreeFile(fp);
{
}
-
-#endif /* USE_RENDEZVOUS */
+#endif /* USE_RENDEZVOUS */
/*
setitimer(ITIMER_PROF, &prof_itimer, NULL);
#endif
- MyProcPid = PostmasterPid = getpid(); /* reset PID vars 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.
dup2(i, 1);
dup2(i, 2);
close(i);
-#else /* WIN32 */
+#else /* WIN32 */
/* not supported */
elog(FATAL, "SilentMode not supported under WIN32");
-#endif /* WIN32 */
+#endif /* WIN32 */
}
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"));
+ printf(_(" -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(_(" -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"));
* 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.
+ * tasks handled below get done even when no requests are
+ * arriving.
*/
memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set));
if (selres > 0)
{
/*
- * Select a random seed at the time of first receiving a request.
+ * Select a random seed at the time of first receiving a
+ * request.
*/
while (random_seed == 0)
{
/*
* We are not sure how much precision is in tv_usec, so we
- * swap the nibbles of 'later' and XOR them with 'earlier'. On
- * the off chance that the result is 0, we loop until it isn't.
+ * 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.
*/
random_seed = earlier.tv_usec ^
((later.tv_usec << 16) |
BackendStartup(port);
/*
- * We no longer need the open socket or port structure
- * in this process
+ * We no longer need the open socket or port
+ * structure in this process
*/
StreamClose(port->sock);
ConnFree(port);
SysLoggerPID = SysLogger_Start();
/*
- * 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
+ * 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 && StartupPID == 0 && !FatalError)
}
/* If we have lost the archiver, try to start a new one */
- if (XLogArchivingActive() && PgArchPID == 0 &&
- StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ if (XLogArchivingActive() && PgArchPID == 0 &&
+ StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
PgArchPID = pgarch_start();
-
+
/* If we have lost the stats collector, try to start a new one */
if (PgStatPID == 0 &&
StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
PgStatPID = pgstat_start();
/*
- * Touch the socket and lock file at least every ten minutes, to ensure
- * that they are not removed by overzealous /tmp-cleaning tasks.
+ * Touch the socket and lock file at least every ten minutes, to
+ * ensure that they are not removed by overzealous /tmp-cleaning
+ * tasks.
*/
now = time(NULL);
if (now - last_touch_time >= 10 * 60)
if (SysLoggerPID != 0)
kill(SysLoggerPID, SIGHUP);
/* PgStatPID does not currently need SIGHUP */
+
+ /* Reload authentication config files too */
load_hba();
load_ident();
switch (postgres_signal_arg)
{
case SIGTERM:
+
/*
* Smart Shutdown:
*
break;
case SIGINT:
+
/*
* Fast Shutdown:
*
/*
* No children left. Begin shutdown of data base system.
*
- * Note: if we previously got SIGTERM then we may send SIGUSR2
- * to the bgwriter a second time here. This should be harmless.
+ * 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)
break; /* let reaper() handle this */
break;
case SIGQUIT:
+
/*
* Immediate Shutdown:
*
while ((pid = win32_waitpid(&exitstatus)) > 0)
{
/*
- * 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...
+ * 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 /* WIN32 */
-#endif /* HAVE_WAITPID */
+#endif /* WIN32 */
+#endif /* HAVE_WAITPID */
/*
* Check if this child was a startup process.
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")));
}
/*
- * Startup succeeded - we are done with system startup or recovery.
+ * Startup succeeded - we are done with system startup or
+ * recovery.
*/
FatalError = false;
/*
- * Crank up the background writer. It doesn't matter if this
+ * 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();
+
+ /*
+ * Crank up the background writer. It doesn't matter if this
* fails, we'll just try again later.
*/
Assert(BgWriterPID == 0);
/*
* Go to shutdown mode if a shutdown request was pending.
- * Otherwise, try to start the archiver and stats collector too.
+ * Otherwise, try to start the archiver and stats collector
+ * too.
*/
if (Shutdown > NoShutdown && BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
- else if (Shutdown == NoShutdown) {
- if (XLogArchivingActive() && PgArchPID == 0)
- PgArchPID = pgarch_start();
- if (PgStatPID == 0)
- PgStatPID = pgstat_start();
- }
+ else if (Shutdown == NoShutdown)
+ {
+ if (XLogArchivingActive() && PgArchPID == 0)
+ PgArchPID = pgarch_start();
+ if (PgStatPID == 0)
+ PgStatPID = pgstat_start();
+ }
continue;
}
!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
+ * 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.
+ * 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,
- gettext("background writer process"));
+ _("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.)
+ * 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, gettext("archiver process"),
+ LogChildExit(LOG, _("archiver process"),
pid, exitstatus);
if (XLogArchivingActive() &&
StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
}
/*
- * 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.)
+ * 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.)
*/
if (PgStatPID != 0 && pid == PgStatPID)
{
PgStatPID = 0;
if (exitstatus != 0)
- LogChildExit(LOG, gettext("statistics collector process"),
+ LogChildExit(LOG, _("statistics collector process"),
pid, exitstatus);
if (StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
PgStatPID = pgstat_start();
/* for safety's sake, launch new logger *first* */
SysLoggerPID = SysLogger_Start();
if (exitstatus != 0)
- LogChildExit(LOG, gettext("system logger process"),
+ LogChildExit(LOG, _("system logger process"),
pid, exitstatus);
continue;
}
{
/*
* 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.)
+ * StartupDataBase. (We can ignore the archiver and stats
+ * processes here since they are not connected to shmem.)
*/
if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0)
goto reaper_done;
*/
static void
CleanupBackend(int pid,
- int exitstatus) /* child's exit status. */
+ int exitstatus) /* child's exit status. */
{
Dlelem *curr;
- LogChildExit(DEBUG2, gettext("server 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
*/
if (exitstatus != 0)
{
- HandleChildCrash(pid, exitstatus, gettext("server process"));
+ HandleChildCrash(pid, exitstatus, _("server process"));
return;
}
/*
* 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)
+ * exit status is to be expected in SIGQUIT response; don't clutter
+ * log)
*/
if (!FatalError)
{
LogChildExit(LOG, procname, pid, exitstatus);
ereport(LOG,
- (errmsg("terminating any other active server processes")));
+ (errmsg("terminating any other active server processes")));
}
/* Process regular backends */
pid = backend_forkexec(port);
-#else /* !EXEC_BACKEND */
+#else /* !EXEC_BACKEND */
#ifdef LINUX_PROFILE
proc_exit(BackendRun(port));
}
-
-#endif /* EXEC_BACKEND */
+#endif /* EXEC_BACKEND */
if (pid < 0)
{
/* 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 */
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 debugbuf[32];
char protobuf[32];
int i;
port->remote_port = strdup(remote_port);
/*
- * In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.c
+ * 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?
+ * FIXME: [fork/exec] Ugh. Is there a way around this overhead?
*/
#ifdef EXEC_BACKEND
Assert(PostmasterContext == NULL);
* start a new random sequence in the random() library function.
*/
random_seed = 0;
- gettimeofday(&now, &tz);
- srandom((unsigned int) now.tv_usec);
-
+ 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. (It's OK to mangle
*/
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])));
return internal_forkexec(ac, av, port);
}
+#ifndef WIN32
+
+/*
+ * 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;
+
+ if (!save_backend_variables(¶m, port))
+ return -1; /* log made by save_backend_variables */
+
+ /* 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);
- if (!write_backend_variables(tmpfilename, port))
- return -1; /* log made by write_backend_variables */
+ /* Open file */
+ fp = AllocateFile(tmpfilename, PG_BINARY_W);
+ if (!fp)
+ {
+ /* As per OpenTemporaryFile... */
+ char dirname[MAXPGPATH];
+
+ snprintf(dirname, MAXPGPATH, "%s/%s", DataDir, PG_TEMP_FILES_DIR);
+ mkdir(dirname, S_IRWXU);
+
+ 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);
/* Insert temp file name after -fork argument */
argv[2] = tmpfilename;
-#ifdef WIN32
- pid = win32_forkexec(postgres_exec_path, argv);
-#else
/* Fire off execv in child */
if ((pid = fork()) == 0)
{
if (execv(postgres_exec_path, argv) < 0)
{
ereport(LOG,
- (errmsg("could not exec backend process \"%s\": %m",
+ (errmsg("could not execute server process \"%s\": %m",
postgres_exec_path)));
/* We're already in the child process here, can't return */
exit(1);
}
}
-#endif
- return pid; /* Parent returns pid, or -1 on fork failure */
+ return pid; /* Parent returns pid, or -1 on fork
+ * failure */
+}
+
+#else /* WIN32 */
+
+/*
+ * internal_forkexec win32 implementation
+ *
+ * - 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
+internal_forkexec(int argc, char *argv[], Port *port)
+{
+ 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;
+ }
+
+ 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;
+ }
+
+ /* Insert temp file name after -fork argument */
+ sprintf(paramHandleStr, "%lu", (DWORD)paramHandle);
+ argv[2] = paramHandleStr;
+
+ /* 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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
/* In EXEC_BACKEND case we will not have inherited these settings */
IsPostmasterEnvironment = true;
whereToSendOutput = None;
- pqinitmask();
- PG_SETMASK(&BlockSig);
- /* Setup essential subsystems */
+ /* 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");
- /* Read in file-based context */
- memset(&port, 0, sizeof(Port));
- read_backend_variables(argv[2], &port);
+ /*
+ * 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
+ 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 */
{
/* BackendRun will close sockets */
- /* Attach process to shared segments */
+ /* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
+#ifdef USE_SSL
+ /*
+ * Need to reinitialize the SSL library in the backend,
+ * since the context structures contain function pointers
+ * and cannot be passed through the parameter file.
+ */
+ if (EnableSSL)
+ secure_initialize();
+#endif
+
Assert(argc == 3); /* shouldn't be any more args */
proc_exit(BackendRun(&port));
}
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
- /* Attach process to shared segments */
+ /* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
BootstrapMain(argc - 2, argv + 2);
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 close postmaster sockets here, because we are forking
+ * from pgstat buffer process, which already did it.
*/
/* Do not want to attach to shared memory */
return 1; /* shouldn't get here */
}
-#endif /* EXEC_BACKEND */
+#endif /* EXEC_BACKEND */
/*
* Send SIGUSR1 to archiver process, to wake it up and begin
* archiving next transaction log file.
*/
- kill(PgArchPID, SIGUSR1);
+ kill(PgArchPID, SIGUSR1);
}
- }
+ }
PG_SETMASK(&UnBlockSig);
int cnt = 0;
for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
- {
cnt++;
- }
return cnt;
}
/*
* StartChildProcess -- start a non-backend child process for the postmaster
*
- * xlog determines what kind of child will be started. All child types
+ * xlog determines what kind of child will be started. All child types
* initially go to BootstrapMain, which will handle common setup.
*
* Return value of StartChildProcess is subprocess' PID, or 0 if failed
char *av[10];
int ac = 0;
char xlbuf[32];
+
#ifdef LINUX_PROFILE
struct itimerval prof_itimer;
#endif
pid = postmaster_forkexec(ac, av);
-#else /* !EXEC_BACKEND */
+#else /* !EXEC_BACKEND */
#ifdef LINUX_PROFILE
/* see comments in BackendStartup */
beos_backend_startup();
#endif
- IsUnderPostmaster = true; /* we are a postmaster subprocess now */
+ IsUnderPostmaster = true; /* we are a postmaster subprocess
+ * now */
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
BootstrapMain(ac, av);
ExitPostmaster(0);
}
-
-#endif /* EXEC_BACKEND */
+#endif /* EXEC_BACKEND */
if (pid < 0)
{
break;
case BS_XLOG_BGWRITER:
ereport(LOG,
- (errmsg("could not fork background writer process: %m")));
+ (errmsg("could not fork background writer process: %m")));
break;
default:
ereport(LOG,
}
/*
- * fork failure is fatal during startup, but there's no need
- * to choke immediately if starting other child types 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_STARTUP)
ExitPostmaster(1);
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))
#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 slock_t *ShmemLock;
extern slock_t *ShmemIndexLock;
extern void *ShmemIndexAlloc;
-typedef struct LWLock LWLock;
extern LWLock *LWLockArray;
extern slock_t *ProcStructLock;
extern int pgStatSock;
+extern int pgStatPipe[2];
-#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 write_array_var(var,fp) fwrite((void*)(var),sizeof(var),1,fp)
-#define read_array_var(var,fp) fread((void*)(var),sizeof(var),1,fp)
+#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
+
+/* 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(char *filename, Port *port)
+save_backend_variables(BackendParameters *param, Port *port,
+ HANDLE childProcess, pid_t childPid)
+#endif
{
- static unsigned long tmpBackendFileNum = 0;
- FILE *fp;
- char str_buf[MAXPGPATH];
+ memcpy(¶m->port, port, sizeof(Port));
+ write_inheritable_socket(¶m->portsocket, port->sock, childPid);
- /* Calculate name for temp file in caller's buffer */
- Assert(DataDir);
- snprintf(filename, MAXPGPATH, "%s/%s/%s.backend_var.%d.%lu",
- DataDir, PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
- MyProcPid, ++tmpBackendFileNum);
+ StrNCpy(param->DataDir, DataDir, MAXPGPATH);
- /* Open file */
- fp = AllocateFile(filename, PG_BINARY_W);
- if (!fp)
- {
- /* As per OpenTemporaryFile... */
- char dirname[MAXPGPATH];
+ memcpy(¶m->ListenSocket, &ListenSocket, sizeof(ListenSocket));
- snprintf(dirname, MAXPGPATH, "%s/%s", DataDir, PG_TEMP_FILES_DIR);
- mkdir(dirname, S_IRWXU);
+ param->MyCancelKey = MyCancelKey;
- fp = AllocateFile(filename, PG_BINARY_W);
- if (!fp)
- {
- ereport(LOG,
- (errcode_for_file_access(),
- errmsg("could not create file \"%s\": %m",
- filename)));
- return false;
- }
- }
+ param->UsedShmemSegID = UsedShmemSegID;
+ param->UsedShmemSegAddr = UsedShmemSegAddr;
- /* Write vars */
- 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);
+ param->ShmemLock = ShmemLock;
+ param->ShmemIndexLock = ShmemIndexLock;
+ param->ShmemVariableCache = ShmemVariableCache;
+ param->ShmemIndexAlloc = ShmemIndexAlloc;
+ param->ShmemBackendArray = ShmemBackendArray;
- /*
- * XXX FIXME later: writing these strings as MAXPGPATH bytes always is
- * probably a waste of resources
- */
+ 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);
- StrNCpy(str_buf, DataDir, MAXPGPATH);
- write_array_var(str_buf, fp);
+ param->PostmasterPid = PostmasterPid;
- write_array_var(ListenSocket, fp);
+#ifdef WIN32
+ param->PostmasterHandle = PostmasterHandle;
+ write_duplicated_handle(¶m->initial_signal_pipe,
+ pgwin32_create_signal_listener(childPid),
+ childProcess);
+#endif
- write_var(MyCancelKey, fp);
+ memcpy(¶m->syslogPipe, &syslogPipe, sizeof(syslogPipe));
- write_var(UsedShmemSegID, fp);
- write_var(UsedShmemSegAddr, fp);
+ StrNCpy(param->my_exec_path, my_exec_path, MAXPGPATH);
- write_var(ShmemLock, fp);
- write_var(ShmemIndexLock, fp);
- write_var(ShmemVariableCache, fp);
- write_var(ShmemIndexAlloc, fp);
- write_var(ShmemBackendArray, fp);
+ StrNCpy(param->ExtraOptions, ExtraOptions, MAXPGPATH);
- write_var(LWLockArray, fp);
- write_var(ProcStructLock, fp);
- write_var(pgStatSock, fp);
+ StrNCpy(param->lc_collate, setlocale(LC_COLLATE, NULL), LOCALE_NAME_BUFLEN);
+ StrNCpy(param->lc_ctype, setlocale(LC_CTYPE, NULL), LOCALE_NAME_BUFLEN);
- write_var(debug_flag, fp);
- write_var(PostmasterPid, fp);
-#ifdef WIN32
- write_var(PostmasterHandle, fp);
-#endif
+ return true;
+}
- write_var(syslogPipe[0], fp);
- write_var(syslogPipe[1], fp);
- StrNCpy(str_buf, my_exec_path, MAXPGPATH);
- write_array_var(str_buf, fp);
+#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())));
- write_array_var(ExtraOptions, fp);
+ *dest = hChild;
+}
- StrNCpy(str_buf, setlocale(LC_COLLATE, NULL), MAXPGPATH);
- write_array_var(str_buf, fp);
- StrNCpy(str_buf, setlocale(LC_CTYPE, NULL), MAXPGPATH);
- write_array_var(str_buf, fp);
+/*
+ * 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,
+ (errmsg("could not duplicate socket %d for use in backend: error code %d",
+ src, WSAGetLastError())));
+ }
+}
- /* Release file */
- if (FreeFile(fp))
+/*
+ * Read a duplicate socket structure back, and get the socket descriptor.
+ */
+static void
+read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
+{
+ SOCKET s;
+
+ if (src->origsocket == -1 || src->origsocket == 0)
{
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to file \"%s\": %m", filename)));
- return false;
+ /* Not a real socket! */
+ *dest = src->origsocket;
}
+ 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;
- return true;
+ /*
+ * 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
static void
-read_backend_variables(char *filename, Port *port)
+read_backend_variables(char *id, Port *port)
{
- FILE *fp;
- char str_buf[MAXPGPATH];
+ BackendParameters param;
+
+#ifndef WIN32
+ /* Non-win32 implementation reads from file */
+ FILE *fp;
/* Open file */
- fp = AllocateFile(filename, PG_BINARY_R);
+ fp = AllocateFile(id, PG_BINARY_R);
if (!fp)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not read from backend variables file \"%s\": %m",
- filename)));
+ {
+ write_stderr("could not read from backend variables file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
- /* Read vars */
- 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);
+ 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);
+ }
- read_array_var(str_buf, fp);
- SetDataDir(str_buf);
+ /* Release file */
+ FreeFile(fp);
+ if (unlink(id) != 0)
+ {
+ write_stderr("could not remove file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
+#else
+ /* Win32 version uses mapped file */
+ HANDLE paramHandle;
+ BackendParameters *paramp;
- read_array_var(ListenSocket, fp);
+ 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);
+ }
+
+ memcpy(¶m, paramp, sizeof(BackendParameters));
+
+ if (!UnmapViewOfFile(paramp))
+ {
+ write_stderr("could not unmap view of backend variables: error code %d\n",
+ (int) GetLastError());
+ exit(1);
+ }
+
+ if (!CloseHandle(paramHandle))
+ {
+ write_stderr("could not close handle to backend parameter variables: error code %d\n",
+ (int) GetLastError());
+ exit(1);
+ }
+#endif
+
+ restore_backend_variables(¶m, port);
+}
+
+/* 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(MyCancelKey, fp);
+ SetDataDir(param->DataDir);
- read_var(UsedShmemSegID, fp);
- read_var(UsedShmemSegAddr, fp);
+ memcpy(&ListenSocket, ¶m->ListenSocket, sizeof(ListenSocket));
- read_var(ShmemLock, fp);
- read_var(ShmemIndexLock, fp);
- read_var(ShmemVariableCache, fp);
- read_var(ShmemIndexAlloc, fp);
- read_var(ShmemBackendArray, fp);
+ MyCancelKey = param->MyCancelKey;
- read_var(LWLockArray, fp);
- read_var(ProcStructLock, fp);
- read_var(pgStatSock, fp);
+ 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;
- read_var(debug_flag, fp);
- read_var(PostmasterPid, fp);
#ifdef WIN32
- read_var(PostmasterHandle, fp);
+ PostmasterHandle = param->PostmasterHandle;
+ pgwin32_initial_signal_pipe = param->initial_signal_pipe;
#endif
- read_var(syslogPipe[0], fp);
- read_var(syslogPipe[1], fp);
-
- read_array_var(str_buf, fp);
- StrNCpy(my_exec_path, str_buf, MAXPGPATH);
+ memcpy(&syslogPipe, ¶m->syslogPipe, sizeof(syslogPipe));
- read_array_var(ExtraOptions, fp);
+ StrNCpy(my_exec_path, param->my_exec_path, MAXPGPATH);
- read_array_var(str_buf, fp);
- setlocale(LC_COLLATE, str_buf);
- read_array_var(str_buf, fp);
- setlocale(LC_CTYPE, str_buf);
+ StrNCpy(ExtraOptions, param->ExtraOptions, MAXPGPATH);
- /* Release file */
- FreeFile(fp);
- if (unlink(filename) != 0)
- ereport(WARNING,
- (errcode_for_file_access(),
- errmsg("could not remove file \"%s\": %m", filename)));
+ setlocale(LC_COLLATE, param->lc_collate);
+ setlocale(LC_CTYPE, param->lc_ctype);
}
(int) pid)));
}
-#endif /* EXEC_BACKEND */
+#endif /* EXEC_BACKEND */
#ifdef WIN32
-static pid_t
-win32_forkexec(const char *path, char *argv[])
-{
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- int i;
- int j;
- char cmdLine[MAXPGPATH * 2];
- HANDLE childHandleCopy;
- HANDLE waiterThread;
-
- /* Format the cmd line */
- cmdLine[sizeof(cmdLine)-1] = '\0';
- cmdLine[sizeof(cmdLine)-2] = '\0';
- snprintf(cmdLine, sizeof(cmdLine)-1, "\"%s\"", 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;
- }
-
- 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(LOG, "CreateProcess call failed (%d): %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) == 0)
- ereport(FATAL,
- (errmsg_internal("could not duplicate child handle: %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: %d",
- (int) GetLastError())));
- CloseHandle(waiterThread);
-
- if (IsUnderPostmaster)
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
-
- return pi.dwProcessId;
-}
-
/*
* Note: The following three functions must not be interrupted (eg. by
* signals). As the Postgres Win32 signalling architecture (currently)
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;
+ /*
+ * 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);
for (offset = 0; offset < win32_numChildren; offset += MAXIMUM_WAIT_OBJECTS)
{
- unsigned long num = min(MAXIMUM_WAIT_OBJECTS, win32_numChildren - offset);
+ unsigned long num = Min(MAXIMUM_WAIT_OBJECTS, win32_numChildren - offset);
+
ret = WaitForMultipleObjects(num, &win32_childHNDArray[offset], FALSE, 0);
switch (ret)
{
case WAIT_FAILED:
ereport(LOG,
- (errmsg_internal("failed to wait on %lu of %lu children: %d",
- num, win32_numChildren, (int) GetLastError())));
+ (errmsg_internal("failed to wait on %lu of %lu children: error code %d",
+ num, win32_numChildren, (int) GetLastError())));
return -1;
case WAIT_TIMEOUT:
break;
default:
+
/*
* Get the exit code, and return the PID of, the
* respective process
*/
ereport(FATAL,
(errmsg_internal("failed to get exit code for child %lu",
- win32_childPIDArray[index])));
+ (unsigned long) win32_childPIDArray[index])));
}
*exitstatus = (int) exitCode;
return win32_childPIDArray[index];
if (r == WAIT_OBJECT_0)
pg_queue_signal(SIGCHLD);
else
- write_stderr("ERROR: failed to wait on child process handle: %d\n",
- (int) GetLastError());
+ write_stderr("could not wait on child process handle: error code %d\n",
+ (int) GetLastError());
CloseHandle(procHandle);
return 0;
}
-#endif /* WIN32 */
+#endif /* WIN32 */