aboutsummaryrefslogtreecommitdiff
path: root/src/backend/postmaster
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-05-28 05:13:32 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-05-28 05:13:32 +0000
commit1a321f26d88e5c64bccba9d36920aede1e201729 (patch)
tree43940a3ed5cc754bff68748502550731b0ad19a0 /src/backend/postmaster
parent37da0ba0e0f2d92857dc62789820d21e177dc00f (diff)
downloadpostgresql-1a321f26d88e5c64bccba9d36920aede1e201729.tar.gz
postgresql-1a321f26d88e5c64bccba9d36920aede1e201729.zip
Code review for EXEC_BACKEND changes. Reduce the number of #ifdefs by
about a third, make it work on non-Windows platforms again. (But perhaps I broke the WIN32 code, since I have no way to test that.) Fold all the paths that fork postmaster child processes to go through the single routine SubPostmasterMain, which takes care of resurrecting the state that would normally be inherited from the postmaster (including GUC variables). Clean up some places where there's no particularly good reason for the EXEC and non-EXEC cases to work differently. Take care of one or two FIXMEs that remained in the code.
Diffstat (limited to 'src/backend/postmaster')
-rw-r--r--src/backend/postmaster/pgstat.c248
-rw-r--r--src/backend/postmaster/postmaster.c923
2 files changed, 598 insertions, 573 deletions
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 70924eb676e..a3a4e57c855 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -9,11 +9,11 @@
* - Add some automatic call for pgstat vacuuming.
*
* - Add a pgstat config column to pg_database, so this
- * entire thing can be enabled/disabled on a per db base.
+ * entire thing can be enabled/disabled on a per db basis.
*
* Copyright (c) 2001-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.71 2004/05/24 02:47:47 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.72 2004/05/28 05:12:58 tgl Exp $
* ----------
*/
#include "postgres.h"
@@ -51,13 +51,6 @@
#include "utils/ps_status.h"
#include "utils/syscache.h"
-#ifdef EXEC_BACKEND
-#include "utils/guc.h"
-#endif
-
-#ifdef WIN32
-extern pid_t win32_forkexec(const char* path, char *argv[]);
-#endif
/* ----------
* GUC parameters
@@ -107,8 +100,8 @@ static HTAB *pgStatBeDead = NULL;
static PgStat_StatBeEntry *pgStatBeTable = NULL;
static int pgStatNumBackends = 0;
-static char pgStat_tmpfname[MAXPGPATH];
static char pgStat_fname[MAXPGPATH];
+static char pgStat_tmpfname[MAXPGPATH];
/* ----------
@@ -116,12 +109,20 @@ static char pgStat_fname[MAXPGPATH];
* ----------
*/
#ifdef EXEC_BACKEND
+
+typedef enum STATS_PROCESS_TYPE
+{
+ STAT_PROC_BUFFER,
+ STAT_PROC_COLLECTOR
+} STATS_PROCESS_TYPE;
+
static pid_t pgstat_forkexec(STATS_PROCESS_TYPE procType);
-static void pgstat_parseArgs(PGSTAT_FORK_ARGS);
+static void pgstat_parseArgs(int argc, char *argv[]);
+
#endif
-NON_EXEC_STATIC void pgstat_main(PGSTAT_FORK_ARGS);
-NON_EXEC_STATIC void pgstat_mainChild(PGSTAT_FORK_ARGS);
-static void pgstat_mainInit(void);
+
+NON_EXEC_STATIC void PgstatBufferMain(int argc, char *argv[]);
+NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]);
static void pgstat_recvbuffer(void);
static void pgstat_die(SIGNAL_ARGS);
@@ -150,18 +151,6 @@ static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len);
* ------------------------------------------------------------
*/
-#ifdef EXEC_BACKEND
-
-void
-pgstat_init_forkexec_backend(void)
-{
- Assert(DataDir != NULL);
- snprintf(pgStat_fname, MAXPGPATH,
- PGSTAT_STAT_FILENAME, DataDir);
-}
-
-#endif
-
/* ----------
* pgstat_init() -
*
@@ -195,12 +184,12 @@ pgstat_init(void)
pgstat_collect_startcollector = true;
/*
- * Initialize the filenames for the status reports.
+ * Initialize the filename for the status reports. (In the EXEC_BACKEND
+ * case, this only sets the value in the postmaster. The collector
+ * subprocess will recompute the value for itself, and individual
+ * backends must do so also if they want to access the file.)
*/
- snprintf(pgStat_tmpfname, MAXPGPATH,
- PGSTAT_STAT_TMPFILE, DataDir, getpid());
- snprintf(pgStat_fname, MAXPGPATH,
- PGSTAT_STAT_FILENAME, DataDir);
+ snprintf(pgStat_fname, MAXPGPATH, PGSTAT_STAT_FILENAME, DataDir);
/*
* If we don't have to start a collector or should reset the collected
@@ -441,112 +430,83 @@ startup_failed:
#ifdef EXEC_BACKEND
-/* ----------
+/*
* pgstat_forkexec() -
*
- * Used to format up the arglist for, then fork and exec, statistics
+ * Format up the arglist for, then fork and exec, statistics
* (buffer and collector) processes
- *
*/
static pid_t
pgstat_forkexec(STATS_PROCESS_TYPE procType)
{
- pid_t pid;
- char *av[15];
+ char *av[12];
int ac = 0, bufc = 0, i;
- char pgstatBuf[12][MAXPGPATH];
+ char pgstatBuf[7][32];
av[ac++] = "postgres";
+
switch (procType)
{
case STAT_PROC_BUFFER:
- av[ac++] = "-statBuf";
+ av[ac++] = "-forkbuf";
break;
case STAT_PROC_COLLECTOR:
- av[ac++] = "-statCol";
+ av[ac++] = "-forkcol";
break;
default:
Assert(false);
}
- /* Sockets + pipes */
- bufc = 0;
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",pgStatSock);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",pgStatPmPipe[0]);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",pgStatPmPipe[1]);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",pgStatCollectorPmPipe[0]);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",pgStatCollectorPmPipe[1]);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",pgStatPipe[0]);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",pgStatPipe[1]);
-
- /* + misc */
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"%d",MaxBackends);
-
- /* + the pstat file names, and postgres pathname */
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"\"%s\"",pgStat_tmpfname);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"\"%s\"",pgStat_fname);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"\"%s\"",postgres_exec_path);
- snprintf(pgstatBuf[bufc++],MAXPGPATH,"\"%s\"",DataDir);
+ av[ac++] = NULL; /* filled in by postmaster_forkexec */
+
+ /* postgres_exec_path is not passed by write_backend_variables */
+ av[ac++] = postgres_exec_path;
+
+ /* Sockets + pipes (those not passed by write_backend_variables) */
+ snprintf(pgstatBuf[bufc++],32,"%d",pgStatPmPipe[0]);
+ snprintf(pgstatBuf[bufc++],32,"%d",pgStatPmPipe[1]);
+ snprintf(pgstatBuf[bufc++],32,"%d",pgStatCollectorPmPipe[0]);
+ snprintf(pgstatBuf[bufc++],32,"%d",pgStatCollectorPmPipe[1]);
+ snprintf(pgstatBuf[bufc++],32,"%d",pgStatPipe[0]);
+ snprintf(pgstatBuf[bufc++],32,"%d",pgStatPipe[1]);
/* Add to the arg list */
Assert(bufc <= lengthof(pgstatBuf));
for (i = 0; i < bufc; i++)
av[ac++] = pgstatBuf[i];
- av[ac++] = NULL;
- Assert(ac <= lengthof(av));
+ av[ac] = NULL;
+ Assert(ac < lengthof(av));
- /* Fire off execv in child */
-#ifdef WIN32
- pid = win32_forkexec(postgres_exec_path, av);
-#else
- if ((pid = fork()) == 0 && (execv(postgres_exec_path, av) == -1))
- /* FIXME: [fork/exec] suggestions for what to do here? Can't call elog... */
- abort();
-#endif
- return pid; /* Parent returns pid */
+ return postmaster_forkexec(ac, av);
}
-/* ----------
+/*
* pgstat_parseArgs() -
*
- * Used to unformat the arglist for exec'ed statistics
+ * Extract data from the arglist for exec'ed statistics
* (buffer and collector) processes
- *
*/
static void
-pgstat_parseArgs(PGSTAT_FORK_ARGS)
+pgstat_parseArgs(int argc, char *argv[])
{
- Assert(argc == 14);
-
- if (find_my_exec(argv[0], my_exec_path) < 0)
- elog(FATAL,
- gettext("%s: could not locate my own executable path"),
- argv[0]);
-
- get_pkglib_path(my_exec_path, pkglib_path);
+ Assert(argc == 10);
- argc = 2;
- pgStatSock = atoi(argv[argc++]);
+ argc = 3;
+ StrNCpy(postgres_exec_path, argv[argc++], MAXPGPATH);
pgStatPmPipe[0] = atoi(argv[argc++]);
pgStatPmPipe[1] = atoi(argv[argc++]);
pgStatCollectorPmPipe[0] = atoi(argv[argc++]);
pgStatCollectorPmPipe[1] = atoi(argv[argc++]);
pgStatPipe[0] = atoi(argv[argc++]);
pgStatPipe[1] = atoi(argv[argc++]);
- MaxBackends = atoi(argv[argc++]);
- StrNCpy(pgStat_tmpfname,argv[argc++],MAXPGPATH);
- StrNCpy(pgStat_fname, argv[argc++],MAXPGPATH);
- StrNCpy(postgres_exec_path, argv[argc++],MAXPGPATH);
- DataDir = strdup(argv[argc++]);
-
- read_nondefault_variables();
}
-#endif
+#endif /* EXEC_BACKEND */
+
/* ----------
* pgstat_start() -
@@ -638,7 +598,7 @@ pgstat_start(void)
/* Drop our connection to postmaster's shared memory, as well */
PGSharedMemoryDetach();
- pgstat_main();
+ PgstatBufferMain(0, NULL);
break;
#endif
@@ -1443,26 +1403,20 @@ pgstat_send(void *msg, int len)
}
-/* ------------------------------------------------------------
- * Local functions implementing the statistics collector itself follow
- *------------------------------------------------------------
+/* ----------
+ * PgstatBufferMain() -
+ *
+ * Start up the statistics buffer process. This is the body of the
+ * postmaster child process.
+ *
+ * The argc/argv parameters are valid only in EXEC_BACKEND case.
+ * ----------
*/
-
-static void
-pgstat_mainInit(void)
+NON_EXEC_STATIC void
+PgstatBufferMain(int argc, char *argv[])
{
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;
-
- /* Setup global context */
- MemoryContextInit(); /* before any elog'ing can occur */
- InitializeGUCOptions();
-#endif
-
MyProcPid = getpid(); /* reset MyProcPid */
/* Lose the postmaster's on-exit routines */
@@ -1485,20 +1439,8 @@ pgstat_mainInit(void)
pqsignal(SIGTTOU, SIG_DFL);
pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL);
-}
-
+ /* unblock will happen in pgstat_recvbuffer */
-/* ----------
- * pgstat_main() -
- *
- * Start up the statistics collector itself. This is the body of the
- * postmaster child process.
- * ----------
- */
-NON_EXEC_STATIC void
-pgstat_main(PGSTAT_FORK_ARGS)
-{
- pgstat_mainInit(); /* Note: for *both* EXEC_BACKEND and regular cases */
#ifdef EXEC_BACKEND
pgstat_parseArgs(argc,argv);
#endif
@@ -1547,7 +1489,7 @@ pgstat_main(PGSTAT_FORK_ARGS)
#ifndef EXEC_BACKEND
case 0:
/* child becomes collector process */
- pgstat_mainChild();
+ PgstatCollectorMain(0, NULL);
break;
#endif
@@ -1560,8 +1502,17 @@ pgstat_main(PGSTAT_FORK_ARGS)
}
+/* ----------
+ * PgstatCollectorMain() -
+ *
+ * Start up the statistics collector itself. This is the body of the
+ * postmaster grandchild process.
+ *
+ * The argc/argv parameters are valid only in EXEC_BACKEND case.
+ * ----------
+ */
NON_EXEC_STATIC void
-pgstat_mainChild(PGSTAT_FORK_ARGS)
+PgstatCollectorMain(int argc, char *argv[])
{
PgStat_Msg msg;
fd_set rfds;
@@ -1574,30 +1525,53 @@ pgstat_mainChild(PGSTAT_FORK_ARGS)
bool need_statwrite;
HASHCTL hash_ctl;
+ MyProcPid = getpid(); /* reset MyProcPid */
+
+ /*
+ * Reset signal handling. With the exception of restoring default
+ * SIGCHLD handling, this is a no-op in the non-EXEC_BACKEND case
+ * because we'll have inherited these settings from the buffer process;
+ * but it's not a no-op for EXEC_BACKEND.
+ */
+ pqsignal(SIGHUP, SIG_IGN);
+ pqsignal(SIGINT, SIG_IGN);
+ pqsignal(SIGTERM, SIG_IGN);
+ pqsignal(SIGQUIT, SIG_IGN);
+ pqsignal(SIGALRM, SIG_IGN);
+ pqsignal(SIGPIPE, SIG_IGN);
+ pqsignal(SIGUSR1, SIG_IGN);
+ pqsignal(SIGUSR2, SIG_IGN);
+ pqsignal(SIGCHLD, SIG_DFL);
+ pqsignal(SIGTTIN, SIG_DFL);
+ pqsignal(SIGTTOU, SIG_DFL);
+ pqsignal(SIGCONT, SIG_DFL);
+ pqsignal(SIGWINCH, SIG_DFL);
+ PG_SETMASK(&UnBlockSig);
+
#ifdef EXEC_BACKEND
- pgstat_mainInit(); /* Note: only in EXEC_BACKEND case */
pgstat_parseArgs(argc,argv);
-#else
- MyProcPid = getpid(); /* reset MyProcPid */
#endif
+ /* Close unwanted files */
closesocket(pgStatPipe[1]);
closesocket(pgStatSock);
pmPipe = pgStatCollectorPmPipe[0];
/*
- * In the child we can have default SIGCHLD handling (in case we want
- * to call system() here...)
- */
- pqsignal(SIGCHLD, SIG_DFL);
-
- /*
* Identify myself via ps
*/
init_ps_display("stats collector process", "", "");
set_ps_display("");
/*
+ * Initialize filenames needed for status reports.
+ */
+ snprintf(pgStat_fname, MAXPGPATH, PGSTAT_STAT_FILENAME, DataDir);
+ /* tmpfname need only be set correctly in this process */
+ snprintf(pgStat_tmpfname, MAXPGPATH, PGSTAT_STAT_TMPFILE,
+ DataDir, getpid());
+
+ /*
* Arrange to write the initial status file right away
*/
gettimeofday(&next_statwrite, NULL);
@@ -2550,6 +2524,18 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
*betab = NULL;
/*
+ * In EXEC_BACKEND case, we won't have inherited pgStat_fname from
+ * postmaster, so compute it first time through.
+ */
+#ifdef EXEC_BACKEND
+ if (pgStat_fname[0] == '\0')
+ {
+ Assert(DataDir != NULL);
+ snprintf(pgStat_fname, MAXPGPATH, PGSTAT_STAT_FILENAME, DataDir);
+ }
+#endif
+
+ /*
* Try to open the status file. If it doesn't exist, the backends
* simply return zero for anything and the collector simply starts
* from scratch with empty counters.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 2679ea6bf9b..07812c7b310 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -7,10 +7,10 @@
* 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.
@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.397 2004/05/27 17:12:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.398 2004/05/28 05:12:58 tgl Exp $
*
* NOTES
*
@@ -113,8 +113,6 @@
#include "pgstat.h"
-#define INVALID_SOCK (-1)
-
#ifdef HAVE_SIGPROCMASK
sigset_t UnBlockSig,
BlockSig,
@@ -177,14 +175,6 @@ static const char *progname = NULL;
#define MAXLISTEN 10
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
*/
@@ -258,7 +248,12 @@ extern int optreset;
/*
* postmaster.c - function prototypes
*/
-static void pmdaemonize(int argc, char *argv[]);
+static void checkDataDir(const char *checkdir);
+#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);
@@ -270,7 +265,6 @@ static void dummy_handler(SIGNAL_ARGS);
static void CleanupProc(int pid, int exitstatus);
static void LogChildExit(int lev, const char *procname,
int pid, int exitstatus);
-static void BackendInit(Port *port);
static int BackendRun(Port *port);
static void ExitPostmaster(int status);
static void usage(const char *);
@@ -286,7 +280,6 @@ static void RandomSalt(char *cryptSalt, char *md5Salt);
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,...)
@@ -296,8 +289,7 @@ __attribute__((format(printf, 1, 2)));
#ifdef EXEC_BACKEND
#ifdef WIN32
-pid_t win32_forkexec(const char *path, char *argv[]);
-
+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);
@@ -308,98 +300,26 @@ static HANDLE *win32_childHNDArray;
static unsigned long win32_numChildren = 0;
#endif
-static pid_t Backend_forkexec(Port *port);
+static pid_t backend_forkexec(Port *port);
+static pid_t internal_forkexec(int argc, char *argv[], 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 read_backend_variables(char *filename, Port *port);
+static bool write_backend_variables(char *filename, Port *port);
static void ShmemBackendArrayAdd(Backend *bn);
static void ShmemBackendArrayRemove(pid_t pid);
-#endif
+
+#endif /* EXEC_BACKEND */
#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 void
-checkDataDir(const char *checkdir)
-{
- 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).")));
-#endif
-
- /* Look for PG_VERSION before looking for pg_control */
- ValidatePgVersion(checkdir);
-
- snprintf(path, sizeof(path), "%s/global/pg_control", checkdir);
-
- 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);
-}
-
-
-#ifdef USE_RENDEZVOUS
-
-/* reg_reply -- empty callback function for DNSServiceRegistrationCreate() */
-static void
-reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
-{
-
-}
-#endif
+/*
+ * Postmaster main entry point
+ */
int
PostmasterMain(int argc, char *argv[])
{
@@ -462,8 +382,7 @@ PostmasterMain(int argc, char *argv[])
IgnoreSystemIndexes(false);
if (find_my_exec(argv[0], my_exec_path) < 0)
- elog(FATAL,
- gettext("%s: could not locate my own executable path"),
+ elog(FATAL, "%s: could not locate my own executable path",
argv[0]);
get_pkglib_path(my_exec_path, pkglib_path);
@@ -700,9 +619,10 @@ PostmasterMain(int argc, char *argv[])
}
#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",
+ (errmsg("%s: could not locate matching postgres executable",
progname)));
#endif
@@ -728,7 +648,7 @@ PostmasterMain(int argc, char *argv[])
* will show the wrong PID.
*/
if (SilentMode)
- pmdaemonize(argc, argv);
+ pmdaemonize();
/*
* Create lockfile for data directory.
@@ -945,8 +865,96 @@ PostmasterMain(int argc, char *argv[])
return 0; /* not reached */
}
+
+/*
+ * Validate the proposed data directory
+ */
+static void
+checkDataDir(const char *checkdir)
+{
+ 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).")));
+#endif
+
+ /* Look for PG_VERSION before looking for pg_control */
+ ValidatePgVersion(checkdir);
+
+ snprintf(path, sizeof(path), "%s/global/pg_control", checkdir);
+
+ 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);
+}
+
+
+#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(int argc, char *argv[])
+pmdaemonize(void)
{
#ifdef WIN32
/* not supported */
@@ -960,7 +968,7 @@ pmdaemonize(int argc, char *argv[])
#endif
#ifdef LINUX_PROFILE
- /* see comments in BackendRun */
+ /* see comments in BackendStartup */
getitimer(ITIMER_PROF, &prof_itimer);
#endif
@@ -1003,7 +1011,6 @@ pmdaemonize(int argc, char *argv[])
}
-
/*
* Print out help message
*/
@@ -1044,6 +1051,10 @@ usage(const char *progname)
"Report bugs to <pgsql-bugs@postgresql.org>.\n"));
}
+
+/*
+ * Main loop of postmaster
+ */
static int
ServerLoop(void)
{
@@ -1194,7 +1205,6 @@ ServerLoop(void)
* 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)
{
@@ -1524,10 +1534,8 @@ processCancelRequest(Port *port, void *pkt)
int backendPID;
long cancelAuthCode;
Backend *bp;
-
#ifndef EXEC_BACKEND
Dlelem *curr;
-
#else
int i;
#endif
@@ -1550,7 +1558,11 @@ processCancelRequest(Port *port, void *pkt)
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 backup array in shared memory.
+ */
#ifndef EXEC_BACKEND
for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
{
@@ -1728,13 +1740,15 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(LOG,
(errmsg("received SIGHUP, reloading configuration files")));
ProcessConfigFile(PGC_SIGHUP);
-#ifdef EXEC_BACKEND
- write_nondefault_variables(PGC_SIGHUP);
-#endif
SignalChildren(SIGHUP);
load_hba();
load_ident();
+#ifdef EXEC_BACKEND
+ /* Update the starting-point file for future children */
+ write_nondefault_variables(PGC_SIGHUP);
+#endif
+
/*
* Tell the background writer to terminate so that we will start a
* new one with a possibly changed config
@@ -1749,7 +1763,6 @@ SIGHUP_handler(SIGNAL_ARGS)
}
-
/*
* pmdie -- signal handler for processing various postmaster signals.
*/
@@ -2249,6 +2262,9 @@ BackendStartup(Port *port)
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
@@ -2260,6 +2276,12 @@ BackendStartup(Port *port)
fflush(stdout);
fflush(stderr);
+#ifdef EXEC_BACKEND
+
+ pid = backend_forkexec(port);
+
+#else /* !EXEC_BACKEND */
+
#ifdef LINUX_PROFILE
/*
@@ -2277,10 +2299,6 @@ BackendStartup(Port *port)
beos_before_backend_startup();
#endif
- port->canAcceptConnections = canAcceptConnections();
-#ifdef EXEC_BACKEND
- pid = Backend_forkexec(port);
-#else
pid = fork();
if (pid == 0) /* child */
@@ -2297,11 +2315,12 @@ BackendStartup(Port *port)
proc_exit(BackendRun(port));
}
-#endif
- /* in parent, error */
+#endif /* EXEC_BACKEND */
+
if (pid < 0)
{
+ /* in parent, fork failed */
int save_errno = errno;
#ifdef __BEOS__
@@ -2316,7 +2335,7 @@ BackendStartup(Port *port)
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)));
@@ -2392,16 +2411,15 @@ split_opts(char **argv, int *argcp, char *s)
/*
- * 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;
@@ -2409,10 +2427,20 @@ BackendInit(Port *port)
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;
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 other sockets
+ */
+ ClosePostmasterPorts(true);
/* We don't want the postmaster's proc_exit() handlers */
on_exit_reset();
@@ -2421,6 +2449,24 @@ BackendInit(Port *port)
* Signal handlers setting is moved to tcop/postgres...
*/
+ /* 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);
@@ -2429,12 +2475,6 @@ BackendInit(Port *port)
port->remote_port = "";
port->commandTag = "";
- /* Save port etc. for ps status */
- MyProcPort = port;
-
- /* Reset MyProcPid to new backend's pid */
- MyProcPid = getpid();
-
/*
* Initialize libpq and enable reporting of ereport errors to the
* client. Must do this now because authentication uses libpq to send
@@ -2490,6 +2530,29 @@ BackendInit(Port *port)
port->remote_port = strdup(remote_port);
/*
+ * In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.c
+ * 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
* indefinitely. PreAuthDelay doesn't count against the time limit.
@@ -2540,37 +2603,6 @@ BackendInit(Port *port)
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);
/* ----------------
@@ -2607,7 +2639,8 @@ BackendRun(Port *port)
/*
* 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);
@@ -2615,12 +2648,6 @@ BackendRun(Port *port)
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.
@@ -2647,9 +2674,7 @@ BackendRun(Port *port)
* username isn't lost either; see ProcessStartupPacket().
*/
MemoryContextSwitchTo(TopMemoryContext);
-#ifndef EXEC_BACKEND
MemoryContextDelete(PostmasterContext);
-#endif
PostmasterContext = NULL;
/*
@@ -2673,111 +2698,176 @@ BackendRun(Port *port)
#ifdef EXEC_BACKEND
-
/*
- * SubPostmasterMain -- prepare the fork/exec'd process to be in an equivalent
- * state (for calling BackendRun) as a forked process.
+ * postmaster_forkexec -- fork and exec a postmaster subprocess
*
- * returns:
- * Shouldn't return at all.
+ * 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.
*/
-void
-SubPostmasterMain(int argc, char *argv[])
+pid_t
+postmaster_forkexec(int argc, char *argv[])
{
- unsigned long backendID;
Port port;
- memset((void *) &port, 0, sizeof(Port));
- Assert(argc == 2);
+ /* This entry point passes dummy values for the Port variables */
+ memset(&port, 0, sizeof(port));
+ return internal_forkexec(argc, argv, &port);
+}
- /* Do this sooner rather than later... */
- IsUnderPostmaster = true; /* we are a postmaster subprocess now */
+/*
+ * backend_forkexec -- fork/exec off a backend process
+ *
+ * returns the pid of the fork/exec'd process, or -1 on failure
+ */
+static pid_t
+backend_forkexec(Port *port)
+{
+ char *av[4];
+ int ac = 0;
- /* In EXEC case we will not have inherited these settings */
- IsPostmasterEnvironment = true;
- whereToSendOutput = None;
+ av[ac++] = "postgres";
+ av[ac++] = "-forkbackend";
+ av[ac++] = NULL; /* filled in by internal_forkexec */
- /* Setup global context */
- MemoryContextInit();
- InitializeGUCOptions();
+ av[ac] = NULL;
+ Assert(ac < lengthof(av));
- /* Parse passed-in context */
- argc = 0;
- backendID = (unsigned long) atol(argv[argc++]);
- DataDir = strdup(argv[argc++]);
+ return internal_forkexec(ac, av, port);
+}
- /* Read in file-based context */
- read_backend_variables(backendID, &port);
- read_nondefault_variables();
+static pid_t
+internal_forkexec(int argc, char *argv[], Port *port)
+{
+ pid_t pid;
+ char tmpfilename[MAXPGPATH];
- /* Remaining initialization */
- pgstat_init_forkexec_backend();
+ if (!write_backend_variables(tmpfilename, port))
+ return -1; /* log made by write_backend_variables */
- /* FIXME: [fork/exec] Ugh */
- load_hba();
- load_ident();
- load_user();
- load_group();
+ /* 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);
- /* Attach process to shared segments */
- CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
+ /* Insert temp file name after -fork argument */
+ argv[2] = tmpfilename;
- /* Run backend */
- proc_exit(BackendRun(&port));
-}
+#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",
+ 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 */
+}
/*
- * Backend_forkexec -- fork/exec off a backend process
+ * 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.
*
- * returns:
- * the pid of the fork/exec'd process
+ * 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.
*/
-static pid_t
-Backend_forkexec(Port *port)
+int
+SubPostmasterMain(int argc, char *argv[])
{
- pid_t pid;
- char *av[5];
- int ac = 0,
- bufc = 0,
- i;
- char buf[2][MAXPGPATH];
+ Port port;
- if (!write_backend_variables(port))
- return -1; /* log made by write_backend_variables */
+ /* Do this sooner rather than later... */
+ IsUnderPostmaster = true; /* we are a postmaster subprocess now */
- av[ac++] = "postgres";
- av[ac++] = "-forkexec";
+ MyProcPid = getpid(); /* reset MyProcPid */
- /* Format up context to pass to exec'd process */
- snprintf(buf[bufc++], MAXPGPATH, "%lu", tmpBackendFileNum);
- snprintf(buf[bufc++], MAXPGPATH, "\"%s\"", DataDir);
+ /* In EXEC_BACKEND case we will not have inherited these settings */
+ IsPostmasterEnvironment = true;
+ whereToSendOutput = None;
+ pqinitmask();
+ PG_SETMASK(&BlockSig);
- /* Add to the arg list */
- Assert(bufc <= lengthof(buf));
- for (i = 0; i < bufc; i++)
- av[ac++] = buf[i];
+ /* Setup essential subsystems */
+ MemoryContextInit();
+ InitializeGUCOptions();
- /* FIXME: [fork/exec] ExtraOptions? */
+ /* Check we got appropriate args */
+ if (argc < 3)
+ elog(FATAL, "invalid subpostmaster invocation");
- av[ac++] = NULL;
- Assert(ac <= lengthof(av));
+ /* Read in file-based context */
+ memset(&port, 0, sizeof(Port));
+ read_backend_variables(argv[2], &port);
+ read_nondefault_variables();
-#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))
+ /* Run backend or appropriate child */
+ if (strcmp(argv[1], "-forkbackend") == 0)
+ {
+ /* BackendRun will close sockets */
+
+ /* Attach process to shared segments */
+ CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
+ 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(true);
+
+ /* Attach process to shared segments */
+ CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
+
+ BootstrapMain(argc - 2, argv + 2);
+ ExitPostmaster(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);
+ ExitPostmaster(0);
+ }
+ if (strcmp(argv[1], "-forkcol") == 0)
+ {
/*
- * FIXME: [fork/exec] suggestions for what to do here? Probably OK
- * to issue error (unlike pgstat case)
+ * Do NOT close postmaster sockets here, because we are forking from
+ * pgstat buffer process, which already did it.
*/
- abort();
-#endif
- return pid; /* Parent returns pid */
+
+ /* Do not want to attach to shared memory */
+
+ PgstatCollectorMain(argc, argv);
+ ExitPostmaster(0);
+ }
+
+ return 1; /* shouldn't get here */
}
-#endif
+
+#endif /* EXEC_BACKEND */
/*
@@ -2984,80 +3074,61 @@ CountChildren(void)
return cnt;
}
+
/*
- * Fire off a subprocess for startup/shutdown/checkpoint/bgwriter.
+ * SSDataBase -- 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 SSDataBase is subprocess' PID, or 0 if failed to start
+ * subprocess (0 is returned only for checkpoint/bgwriter cases).
*/
-NON_EXEC_STATIC void
-SSDataBaseInit(int xlop)
+static pid_t
+SSDataBase(int xlop)
{
- const char *statmsg;
+ Backend *bn;
+ pid_t pid;
+ char *av[10];
+ int ac = 0;
+ char xlbuf[32];
+#ifdef LINUX_PROFILE
+ struct itimerval prof_itimer;
+#endif
- IsUnderPostmaster = true; /* we are a postmaster subprocess now */
+ /*
+ * Set up command-line arguments for subprocess
+ */
+ av[ac++] = "postgres";
#ifdef EXEC_BACKEND
- /* In EXEC case we will not have inherited these settings */
- IsPostmasterEnvironment = true;
- whereToSendOutput = None;
+ av[ac++] = "-forkboot";
+ av[ac++] = NULL; /* filled in by postmaster_forkexec */
#endif
- MyProcPid = getpid(); /* reset MyProcPid */
+ snprintf(xlbuf, sizeof(xlbuf), "-x%d", xlop);
+ av[ac++] = xlbuf;
- /* Lose the postmaster's on-exit routines and port connections */
- on_exit_reset();
+ av[ac++] = "-p";
+ av[ac++] = "template1";
+
+ av[ac] = NULL;
+ Assert(ac < lengthof(av));
/*
- * Identify myself via ps
+ * Flush stdio channels (see comments in BackendStartup)
*/
- 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("");
-}
-
+ fflush(stdout);
+ fflush(stderr);
-static pid_t
-SSDataBase(int xlop)
-{
- pid_t pid;
- Backend *bn;
+#ifdef EXEC_BACKEND
-#ifndef EXEC_BACKEND
-#ifdef LINUX_PROFILE
- struct itimerval prof_itimer;
-#endif
-#else
- char idbuf[32];
- char ddirbuf[MAXPGPATH];
-#endif
+ pid = postmaster_forkexec(ac, av);
- fflush(stdout);
- fflush(stderr);
+#else /* !EXEC_BACKEND */
-#ifndef EXEC_BACKEND
#ifdef LINUX_PROFILE
- /* see comments in BackendRun */
+ /* see comments in BackendStartup */
getitimer(ITIMER_PROF, &prof_itimer);
#endif
@@ -3066,16 +3137,10 @@ SSDataBase(int xlop)
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
@@ -3085,72 +3150,30 @@ SSDataBase(int xlop)
beos_backend_startup();
#endif
+ IsUnderPostmaster = true; /* we are a postmaster subprocess now */
+
/* 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;
-
-#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
+ /* Lose the postmaster's on-exit routines and port connections */
+ on_exit_reset();
- av[ac] = NULL;
-
- Assert(ac < lengthof(av));
-
-#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
}
- /* in parent */
+#endif /* EXEC_BACKEND */
+
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:
@@ -3188,6 +3211,8 @@ SSDataBase(int xlop)
}
/*
+ * in parent, successful fork
+ *
* 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.
@@ -3280,7 +3305,7 @@ postmaster_error(const char *fmt,...)
* functions
*/
#include "storage/spin.h"
-extern XLogwrtResult LogwrtResult;
+
extern slock_t *ShmemLock;
extern slock_t *ShmemIndexLock;
extern void *ShmemIndexAlloc;
@@ -3291,24 +3316,19 @@ extern int pgStatSock;
#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)
static bool
-write_backend_variables(Port *port)
+write_backend_variables(char *filename, Port *port)
{
- char filename[MAXPGPATH];
+ static unsigned long tmpBackendFileNum = 0;
FILE *fp;
+ char str_buf[MAXPGPATH];
- get_tmp_backend_file_name(filename, ++tmpBackendFileNum);
+ /* Calculate name for temp file in caller's buffer */
+ Assert(DataDir);
+ snprintf(filename, MAXPGPATH, "%s/%s/%s.backend_var.%lu",
+ DataDir, PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
+ ++tmpBackendFileNum);
/* Open file */
fp = AllocateFile(filename, PG_BINARY_W);
@@ -3317,33 +3337,38 @@ write_backend_variables(Port *port)
/* As per OpenTemporaryFile... */
char dirname[MAXPGPATH];
- sprintf(dirname, "%s/%s", DataDir, PG_TEMP_FILES_DIR);
+ snprintf(dirname, MAXPGPATH, "%s/%s", DataDir, PG_TEMP_FILES_DIR);
mkdir(dirname, S_IRWXU);
fp = AllocateFile(filename, PG_BINARY_W);
if (!fp)
{
- ereport(ERROR,
+ ereport(LOG,
(errcode_for_file_access(),
- errmsg("could not write to file \"%s\": %m", filename)));
+ errmsg("could not create file \"%s\": %m",
+ filename)));
return false;
}
}
/* Write vars */
- if (port)
- {
- 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);
- }
- write_var(MyCancelKey, fp);
+ 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);
+
+ /*
+ * XXX FIXME later: writing these strings as MAXPGPATH bytes always is
+ * probably a waste of resources
+ */
- write_var(LogwrtResult, fp);
+ StrNCpy(str_buf, DataDir, MAXPGPATH);
+ fwrite((void *) str_buf, MAXPGPATH, 1, fp);
+
+ write_var(MyCancelKey, fp);
write_var(UsedShmemSegID, fp);
write_var(UsedShmemSegAddr, fp);
@@ -3358,12 +3383,18 @@ write_backend_variables(Port *port)
write_var(ProcStructLock, fp);
write_var(pgStatSock, fp);
- write_var(PreAuthDelay, fp);
write_var(debug_flag, fp);
write_var(PostmasterPid, fp);
fwrite((void *) my_exec_path, MAXPGPATH, 1, fp);
+ fwrite((void *) ExtraOptions, sizeof(ExtraOptions), 1, fp);
+
+ StrNCpy(str_buf, setlocale(LC_COLLATE, NULL), MAXPGPATH);
+ fwrite((void *) str_buf, MAXPGPATH, 1, fp);
+ StrNCpy(str_buf, setlocale(LC_CTYPE, NULL), MAXPGPATH);
+ fwrite((void *) str_buf, MAXPGPATH, 1, fp);
+
/* Release file */
if (FreeFile(fp))
{
@@ -3376,38 +3407,33 @@ write_backend_variables(Port *port)
return true;
}
-void
-read_backend_variables(unsigned long id, Port *port)
+static void
+read_backend_variables(char *filename, Port *port)
{
- char filename[MAXPGPATH];
FILE *fp;
-
- get_tmp_backend_file_name(filename, id);
+ char str_buf[MAXPGPATH];
/* Open file */
fp = AllocateFile(filename, PG_BINARY_R);
if (!fp)
- {
- ereport(ERROR,
+ ereport(FATAL,
(errcode_for_file_access(),
- errmsg("could not read from backend_variables file \"%s\": %m", filename)));
- return;
- }
+ errmsg("could not read from backend variables file \"%s\": %m",
+ filename)));
/* Read vars */
- if (port)
- {
- 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);
- }
- read_var(MyCancelKey, fp);
+ 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);
- read_var(LogwrtResult, fp);
+ fread((void *) str_buf, MAXPGPATH, 1, fp);
+ SetDataDir(str_buf);
+
+ read_var(MyCancelKey, fp);
read_var(UsedShmemSegID, fp);
read_var(UsedShmemSegAddr, fp);
@@ -3422,12 +3448,18 @@ read_backend_variables(unsigned long id, Port *port)
read_var(ProcStructLock, fp);
read_var(pgStatSock, fp);
- read_var(PreAuthDelay, fp);
read_var(debug_flag, fp);
read_var(PostmasterPid, fp);
fread((void *) my_exec_path, MAXPGPATH, 1, fp);
+ fread((void *) ExtraOptions, sizeof(ExtraOptions), 1, fp);
+
+ fread((void *) str_buf, MAXPGPATH, 1, fp);
+ setlocale(LC_COLLATE, str_buf);
+ fread((void *) str_buf, MAXPGPATH, 1, fp);
+ setlocale(LC_CTYPE, str_buf);
+
/* Release file */
FreeFile(fp);
if (unlink(filename) != 0)
@@ -3490,52 +3522,54 @@ ShmemBackendArrayRemove(pid_t pid)
(errmsg_internal("unable to find backend entry with pid %d",
pid)));
}
-#endif
+
+#endif /* EXEC_BACKEND */
+
#ifdef WIN32
-pid_t
+static pid_t
win32_forkexec(const char *path, char *argv[])
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
char *p;
int i;
- char cmdLine[MAXPGPATH];
+ int j;
+ char cmdLine[MAXPGPATH * 2];
HANDLE childHandleCopy;
HANDLE waiterThread;
/* Format the cmd line */
- snprintf(cmdLine, sizeof(cmdLine), "\"%s\"", path);
+ cmdline[sizeof(cmdLine)-1] = '\0';
+ cmdline[sizeof(cmdLine)-2] = '\0';
+ snprintf(cmdLine, sizeof(cmdLine)-1, "\"%s\"", path);
i = 0;
while (argv[++i] != NULL)
{
- /* FIXME: [fork/exec] some strlen checks might be prudent here */
- strcat(cmdLine, " ");
- strcat(cmdLine, argv[i]);
+ 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;
}
-
- /*
- * 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());
+ 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,
@@ -3545,11 +3579,15 @@ win32_forkexec(const char *path, char *argv[])
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);
+ (errmsg_internal("failed to duplicate child handle: %d",
+ (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())));
+ (errmsg_internal("failed to create sigchld waiter thread: %d",
+ (int) GetLastError())));
CloseHandle(waiterThread);
if (IsUnderPostmaster)
@@ -3582,7 +3620,7 @@ win32_AddChild(pid_t pid, HANDLE handle)
else
ereport(FATAL,
(errmsg_internal("unable to add child entry with pid %lu",
- pid)));
+ (unsigned long) pid)));
}
static void
@@ -3608,7 +3646,7 @@ win32_RemoveChild(pid_t pid)
ereport(WARNING,
(errmsg_internal("unable to find child entry with pid %lu",
- pid)));
+ (unsigned long) pid)));
}
static pid_t
@@ -3678,9 +3716,10 @@ win32_sigchld_waiter(LPVOID param)
if (r == WAIT_OBJECT_0)
pg_queue_signal(SIGCHLD);
else
- fprintf(stderr, "ERROR: Failed to wait on child process handle: %i\n", (int) GetLastError());
+ fprintf(stderr, "ERROR: Failed to wait on child process handle: %i\n",
+ (int) GetLastError());
CloseHandle(procHandle);
return 0;
}
-#endif
+#endif /* WIN32 */