1
0
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:
Bruce Momjian
2003-12-20 17:31:21 +00:00
parent 1ee0ddf91d
commit d75b2ec4eb
23 changed files with 490 additions and 216 deletions

View File

@@ -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