mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
This patch is the next step towards (re)allowing fork/exec.
Claudio Natoli
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.351 2003/12/01 22:15:37 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.352 2003/12/20 17:31:21 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
@@ -273,6 +273,7 @@ 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);
|
||||
NON_EXEC_STATIC bool BackendInit(Port *port);
|
||||
static int BackendFork(Port *port);
|
||||
static void ExitPostmaster(int status);
|
||||
static void usage(const char *);
|
||||
@@ -282,10 +283,6 @@ static int ProcessStartupPacket(Port *port, bool SSLdone);
|
||||
static void processCancelRequest(Port *port, void *pkt);
|
||||
static int initMasks(fd_set *rmask);
|
||||
static void report_fork_failure_to_client(Port *port, int errnum);
|
||||
enum CAC_state
|
||||
{
|
||||
CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY
|
||||
};
|
||||
static enum CAC_state canAcceptConnections(void);
|
||||
static long PostmasterRandom(void);
|
||||
static void RandomSalt(char *cryptSalt, char *md5Salt);
|
||||
@@ -298,6 +295,11 @@ postmaster_error(const char *fmt,...)
|
||||
/* This lets gcc check the format string for consistency. */
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
static void
|
||||
write_backend_variables(pid_t pid, Port *port);
|
||||
#endif
|
||||
|
||||
#define StartupDataBase() SSDataBase(BS_XLOG_STARTUP)
|
||||
#define CheckPointDataBase() SSDataBase(BS_XLOG_CHECKPOINT)
|
||||
#define StartBackgroundWriter() SSDataBase(BS_XLOG_BGWRITER)
|
||||
@@ -1185,7 +1187,6 @@ initMasks(fd_set *rmask)
|
||||
static int
|
||||
ProcessStartupPacket(Port *port, bool SSLdone)
|
||||
{
|
||||
enum CAC_state cac;
|
||||
int32 len;
|
||||
void *buf;
|
||||
ProtocolVersion proto;
|
||||
@@ -1244,7 +1245,11 @@ ProcessStartupPacket(Port *port, bool SSLdone)
|
||||
|
||||
if (proto == CANCEL_REQUEST_CODE)
|
||||
{
|
||||
#ifdef EXEC_BACKEND
|
||||
abort(); /* FIXME: [fork/exec] Whoops. Not handled... yet */
|
||||
#else
|
||||
processCancelRequest(port, buf);
|
||||
#endif
|
||||
return 127; /* XXX */
|
||||
}
|
||||
|
||||
@@ -1435,9 +1440,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
|
||||
* so now instead of wasting cycles on an authentication exchange.
|
||||
* (This also allows a pg_ping utility to be written.)
|
||||
*/
|
||||
cac = canAcceptConnections();
|
||||
|
||||
switch (cac)
|
||||
switch (port->canAcceptConnections)
|
||||
{
|
||||
case CAC_STARTUP:
|
||||
ereport(FATAL,
|
||||
@@ -1499,8 +1502,10 @@ processCancelRequest(Port *port, void *pkt)
|
||||
backendPID)));
|
||||
return;
|
||||
}
|
||||
else if (ExecBackend)
|
||||
#ifdef EXEC_BACKEND
|
||||
else
|
||||
AttachSharedMemoryAndSemaphores();
|
||||
#endif
|
||||
|
||||
/* See if we have a matching backend */
|
||||
|
||||
@@ -2341,40 +2346,25 @@ split_opts(char **argv, int *argcp, char *s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BackendFork -- perform authentication, and if successful, set up the
|
||||
* backend's argument list and invoke backend main().
|
||||
*
|
||||
* This used to perform an execv() but we no longer exec the backend;
|
||||
* it's the same executable as the postmaster.
|
||||
* BackendInit/Fork -- perform authentication [BackendInit], and if successful,
|
||||
* set up the backend's argument list [BackendFork] and invoke
|
||||
* backend main() [or exec in EXEC_BACKEND case]
|
||||
*
|
||||
* returns:
|
||||
* Shouldn't return at all.
|
||||
* If PostgresMain() fails, return status.
|
||||
*/
|
||||
static int
|
||||
BackendFork(Port *port)
|
||||
NON_EXEC_STATIC bool
|
||||
BackendInit(Port *port)
|
||||
{
|
||||
char **av;
|
||||
int maxac;
|
||||
int ac;
|
||||
char debugbuf[32];
|
||||
char protobuf[32];
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
char pbuf[NAMEDATALEN + 256];
|
||||
#endif
|
||||
int i;
|
||||
int status;
|
||||
struct timeval now;
|
||||
struct timezone tz;
|
||||
char remote_host[NI_MAXHOST];
|
||||
char remote_port[NI_MAXSERV];
|
||||
|
||||
/*
|
||||
* Let's clean up ourselves as the postmaster child
|
||||
*/
|
||||
|
||||
IsUnderPostmaster = true; /* we are a postmaster subprocess now */
|
||||
|
||||
ClientAuthInProgress = true; /* limit visibility of log messages */
|
||||
@@ -2386,9 +2376,6 @@ BackendFork(Port *port)
|
||||
* Signal handlers setting is moved to tcop/postgres...
|
||||
*/
|
||||
|
||||
/* Close the postmaster's other sockets */
|
||||
ClosePostmasterPorts(true);
|
||||
|
||||
/* Save port etc. for ps status */
|
||||
MyProcPort = port;
|
||||
|
||||
@@ -2444,16 +2431,6 @@ BackendFork(Port *port)
|
||||
StrNCpy(remote_host, tmphost, sizeof(remote_host));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
sleep(PreAuthDelay);
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -2469,7 +2446,7 @@ BackendFork(Port *port)
|
||||
status = ProcessStartupPacket(port, false);
|
||||
|
||||
if (status != STATUS_OK)
|
||||
return 0; /* cancel request processed, or error */
|
||||
return false; /* cancel request processed, or error */
|
||||
|
||||
/*
|
||||
* Now that we have the user and database name, we can set the process
|
||||
@@ -2506,6 +2483,50 @@ BackendFork(Port *port)
|
||||
gettimeofday(&now, &tz);
|
||||
srandom((unsigned int) now.tv_usec);
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
ClientAuthInProgress = false; /* client_min_messages is active
|
||||
* now */
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
BackendFork(Port *port)
|
||||
{
|
||||
char **av;
|
||||
int maxac;
|
||||
int ac;
|
||||
char debugbuf[32];
|
||||
#ifndef EXEC_BACKEND
|
||||
char protobuf[32];
|
||||
#endif
|
||||
int i;
|
||||
char tmpExtraOptions[MAXPGPATH];
|
||||
|
||||
/*
|
||||
* 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)
|
||||
sleep(PreAuthDelay);
|
||||
|
||||
port->canAcceptConnections = canAcceptConnections();
|
||||
|
||||
#ifndef EXEC_BACKEND
|
||||
if (!BackendInit(port))
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
/* ----------------
|
||||
* Now, build the argv vector that will be given to PostgresMain.
|
||||
*
|
||||
@@ -2540,30 +2561,38 @@ BackendFork(Port *port)
|
||||
|
||||
/*
|
||||
* Pass any backend switches specified with -o in the postmaster's own
|
||||
* command line. We assume these are secure. (It's OK to mangle
|
||||
* ExtraOptions since we are now in the child process; this won't
|
||||
* change the postmaster's copy.)
|
||||
* command line. We assume these are secure.
|
||||
* [Note: now makes a copy to protect against future fork/exec changes]
|
||||
*/
|
||||
split_opts(av, &ac, ExtraOptions);
|
||||
strcpy(tmpExtraOptions,ExtraOptions);
|
||||
split_opts(av, &ac, tmpExtraOptions);
|
||||
|
||||
#ifndef EXEC_BACKEND
|
||||
/* Tell the backend what protocol the frontend is using. */
|
||||
snprintf(protobuf, sizeof(protobuf), "-v%u", port->proto);
|
||||
av[ac++] = protobuf;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tell the backend it is being called from the postmaster, and which
|
||||
* database to use. -p marks the end of secure switches.
|
||||
*/
|
||||
av[ac++] = "-p";
|
||||
#ifdef EXEC_BACKEND
|
||||
Assert(UsedShmemSegID != 0 && UsedShmemSegAddr != NULL);
|
||||
/* database name at the end because it might contain commas */
|
||||
snprintf(pbuf, sizeof(pbuf), "%d,%d,%lu,%p,%s",
|
||||
port->sock, canAcceptConnections(),
|
||||
UsedShmemSegID, UsedShmemSegAddr,
|
||||
port->database_name);
|
||||
av[ac++] = pbuf;
|
||||
write_backend_variables(getpid(),port);
|
||||
|
||||
/* pass data dir before end of secure switches (-p) */
|
||||
av[ac++] = "-D";
|
||||
av[ac++] = DataDir;
|
||||
|
||||
/*
|
||||
* This is totally bogus. We need to pass an arg to -p, but we'll
|
||||
* actually get the dbname by ProcessStartupPacket in the exec'd
|
||||
* process
|
||||
*/
|
||||
av[ac++] = "-p";
|
||||
av[ac++] = "FORK_EXEC";
|
||||
#else
|
||||
av[ac++] = "-p";
|
||||
av[ac++] = port->database_name;
|
||||
#endif
|
||||
|
||||
@@ -2571,6 +2600,10 @@ BackendFork(Port *port)
|
||||
* Pass the (insecure) option switches from the connection request.
|
||||
* (It's OK to mangle port->cmdline_options now.)
|
||||
*/
|
||||
/* FIXME: [fork/exec] Hmmm.. we won't see these until after we BackendInit.
|
||||
* Should we add code to BackendInit to add these (somehow!) into
|
||||
* the PostgresMain argument list in the EXEC_BACKEND case?
|
||||
*/
|
||||
if (port->cmdline_options)
|
||||
split_opts(av, &ac, port->cmdline_options);
|
||||
|
||||
@@ -2594,17 +2627,21 @@ BackendFork(Port *port)
|
||||
*/
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal("%s child[%d]: starting with (",
|
||||
progname, MyProcPid)));
|
||||
progname, getpid())));
|
||||
for (i = 0; i < ac; ++i)
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal("\t%s", av[i])));
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal(")")));
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
return execv(pg_pathname,av);
|
||||
#else
|
||||
ClientAuthInProgress = false; /* client_min_messages is active
|
||||
* now */
|
||||
|
||||
return (PostgresMain(ac, av, port->user_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3051,3 +3088,136 @@ postmaster_error(const char *fmt,...)
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
|
||||
/*
|
||||
* The following need to be available to the read/write_backend_variables
|
||||
* functions
|
||||
*/
|
||||
extern XLogRecPtr RedoRecPtr;
|
||||
extern XLogwrtResult LogwrtResult;
|
||||
extern slock_t *ShmemLock;
|
||||
extern slock_t *ShmemIndexLock;
|
||||
extern void *ShmemIndexAlloc;
|
||||
typedef struct LWLock LWLock;
|
||||
extern LWLock *LWLockArray;
|
||||
extern slock_t *ProcStructLock;
|
||||
extern 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.%d", \
|
||||
DataDir, \
|
||||
PG_TEMP_FILES_DIR, \
|
||||
PG_TEMP_FILE_PREFIX, \
|
||||
(id)); \
|
||||
} while (0)
|
||||
|
||||
static void
|
||||
write_backend_variables(pid_t pid, Port *port)
|
||||
{
|
||||
char filename[MAXPGPATH];
|
||||
FILE *fp;
|
||||
get_tmp_backend_file_name(filename,pid);
|
||||
|
||||
/* Open file */
|
||||
fp = AllocateFile(filename, PG_BINARY_W);
|
||||
if (!fp)
|
||||
{
|
||||
/* As per OpenTemporaryFile... */
|
||||
char dirname[MAXPGPATH];
|
||||
sprintf(dirname,"%s/%s",DataDir,PG_TEMP_FILES_DIR);
|
||||
mkdir(dirname, S_IRWXU);
|
||||
|
||||
fp = AllocateFile(filename, PG_BINARY_W);
|
||||
if (!fp)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not write to file \"%s\": %m", filename)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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(MyCancelKey,fp);
|
||||
|
||||
write_var(RedoRecPtr,fp);
|
||||
write_var(LogwrtResult,fp);
|
||||
|
||||
write_var(UsedShmemSegID,fp);
|
||||
write_var(UsedShmemSegAddr,fp);
|
||||
|
||||
write_var(ShmemLock,fp);
|
||||
write_var(ShmemIndexLock,fp);
|
||||
write_var(ShmemVariableCache,fp);
|
||||
write_var(ShmemIndexAlloc,fp);
|
||||
|
||||
write_var(LWLockArray,fp);
|
||||
write_var(ProcStructLock,fp);
|
||||
write_var(pgStatSock,fp);
|
||||
|
||||
/* Release file */
|
||||
FreeFile(fp);
|
||||
}
|
||||
|
||||
void
|
||||
read_backend_variables(pid_t pid, Port *port)
|
||||
{
|
||||
char filename[MAXPGPATH];
|
||||
FILE *fp;
|
||||
get_tmp_backend_file_name(filename,pid);
|
||||
|
||||
/* Open file */
|
||||
fp = AllocateFile(filename, PG_BINARY_R);
|
||||
if (!fp)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read from backend_variables file \"%s\": %m", filename)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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(MyCancelKey,fp);
|
||||
|
||||
read_var(RedoRecPtr,fp);
|
||||
read_var(LogwrtResult,fp);
|
||||
|
||||
read_var(UsedShmemSegID,fp);
|
||||
read_var(UsedShmemSegAddr,fp);
|
||||
|
||||
read_var(ShmemLock,fp);
|
||||
read_var(ShmemIndexLock,fp);
|
||||
read_var(ShmemVariableCache,fp);
|
||||
read_var(ShmemIndexAlloc,fp);
|
||||
|
||||
read_var(LWLockArray,fp);
|
||||
read_var(ProcStructLock,fp);
|
||||
read_var(pgStatSock,fp);
|
||||
|
||||
/* Release file */
|
||||
FreeFile(fp);
|
||||
if (unlink(filename) != 0)
|
||||
ereport(WARNING,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not remove file \"%s\": %m", filename)));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user