aboutsummaryrefslogtreecommitdiff
path: root/src/backend/postmaster/postmaster.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/postmaster/postmaster.c')
-rw-r--r--src/backend/postmaster/postmaster.c923
1 files changed, 481 insertions, 442 deletions
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 */