diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/libpq/pqcomm.c | 75 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 236 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 16 | ||||
-rw-r--r-- | src/backend/utils/init/miscinit.c | 398 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 4 | ||||
-rwxr-xr-x | src/bin/pg_ctl/pg_ctl.sh | 12 | ||||
-rw-r--r-- | src/include/config.h.in | 5 | ||||
-rw-r--r-- | src/include/miscadmin.h | 17 |
8 files changed, 373 insertions, 390 deletions
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index d680cfb7d51..35d6802a5a5 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -29,7 +29,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pqcomm.c,v 1.113 2000/11/21 23:03:53 petere Exp $ + * $Id: pqcomm.c,v 1.114 2000/11/29 20:59:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -169,7 +169,7 @@ StreamDoUnlink(void) /* * StreamServerPort -- open a sock stream "listening" port. * - * This initializes the Postmaster's connection-accepting port fdP. + * This initializes the Postmaster's connection-accepting port *fdP. * * RETURNS: STATUS_OK or STATUS_ERROR */ @@ -183,9 +183,6 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber, err; size_t len = 0; int one = 1; -#ifdef HAVE_FCNTL_SETLK - int lock_fd; -#endif Assert(family == AF_INET || family == AF_UNIX); @@ -223,22 +220,15 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber, len = UNIXSOCK_LEN(saddr.un); strcpy(sock_path, saddr.un.sun_path); /* - * If the socket exists but nobody has an advisory lock on it we - * can safely delete the file. + * Grab an interlock file associated with the socket file. */ -#ifdef HAVE_FCNTL_SETLK - if ((lock_fd = open(sock_path, O_WRONLY | O_NONBLOCK | PG_BINARY, 0666)) >= 0) - { - struct flock lck; - - lck.l_whence = SEEK_SET; - lck.l_start = lck.l_len = 0; - lck.l_type = F_WRLCK; - if (fcntl(lock_fd, F_SETLK, &lck) != -1) - unlink(sock_path); - close(lock_fd); - } -#endif /* HAVE_FCNTL_SETLK */ + if (! CreateSocketLockFile(sock_path, true)) + return STATUS_ERROR; + /* + * Once we have the interlock, we can safely delete any pre-existing + * socket file to avoid failure at bind() time. + */ + unlink(sock_path); } #endif /* HAVE_UNIX_SOCKETS */ @@ -274,8 +264,8 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber, { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "FATAL: StreamServerPort: bind() failed: %s\n" - "\tIs another postmaster already running on that port?\n", - strerror(errno)); + "\tIs another postmaster already running on port %d?\n", + strerror(errno), (int) portNumber); if (family == AF_UNIX) snprintf(PQerrormsg + strlen(PQerrormsg), PQERRORMSG_LENGTH - strlen(PQerrormsg), @@ -293,41 +283,14 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber, #ifdef HAVE_UNIX_SOCKETS if (family == AF_UNIX) { + /* Arrange to unlink the socket file at exit */ on_proc_exit(StreamDoUnlink, 0); /* - * Open the socket file and get an advisory lock on it. The - * lock_fd is left open to keep the lock. + * Fix socket ownership/permission if requested. Note we must + * do this before we listen() to avoid a window where unwanted + * connections could get accepted. */ -#ifdef HAVE_FCNTL_SETLK - if ((lock_fd = open(sock_path, O_WRONLY | O_NONBLOCK | PG_BINARY, 0666)) >= 0) - { - struct flock lck; - - lck.l_whence = SEEK_SET; - lck.l_start = lck.l_len = 0; - lck.l_type = F_WRLCK; - if (fcntl(lock_fd, F_SETLK, &lck) != 0) - elog(DEBUG, "flock error on %s: %s", sock_path, strerror(errno)); - } -#endif /* HAVE_FCNTL_SETLK */ - } -#endif /* HAVE_UNIX_SOCKETS */ - - listen(fd, SOMAXCONN); - - /* - * MS: I took this code from Dillon's version. It makes the listening - * port non-blocking. That is not necessary (and may tickle kernel - * bugs). - * - * fcntl(fd, F_SETFD, 1); fcntl(fd, F_SETFL, FNDELAY); - */ - - *fdP = fd; - - if (family == AF_UNIX) - { Assert(Unix_socket_group); if (Unix_socket_group[0] != '\0') { @@ -379,6 +342,12 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber, return STATUS_ERROR; } } +#endif /* HAVE_UNIX_SOCKETS */ + + listen(fd, SOMAXCONN); + + *fdP = fd; + return STATUS_OK; } diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 9fa4ebb3689..80ff7bac3ea 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.194 2000/11/28 23:27:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.195 2000/11/29 20:59:52 tgl Exp $ * * NOTES * @@ -104,8 +104,6 @@ typedef struct bkend long cancel_key; /* cancel key for cancels for this backend */ } Backend; -Port *MyBackendPort = NULL; - /* list of active backends. For garbage collection only now. */ static Dllist *BackendList; @@ -114,8 +112,8 @@ static Dllist *PortList; /* The socket number we are listening for connections on */ int PostPortNumber; -char * UnixSocketDir; -char * Virtual_host; +char *UnixSocketDir; +char *VirtualHost; /* * MaxBackends is the actual limit on the number of backends we will @@ -216,6 +214,7 @@ static int BackendStartup(Port *port); static int readStartupPacket(void *arg, PacketLen len, void *pkt); static int processCancelRequest(Port *port, PacketLen len, void *pkt); static int initMasks(fd_set *rmask, fd_set *wmask); +static char *canAcceptConnections(void); static long PostmasterRandom(void); static void RandomSalt(char *salt); static void SignalChildren(SIGNAL_ARGS); @@ -252,7 +251,7 @@ checkDataDir(const char *checkdir) "database system either by specifying the -D invocation " "option or by setting the PGDATA environment variable.\n\n", progname); - exit(2); + ExitPostmaster(2); } snprintf(path, sizeof(path), "%s%cglobal%cpg_control", @@ -265,7 +264,7 @@ checkDataDir(const char *checkdir) "\n\tExpected to find it in the PGDATA directory \"%s\"," "\n\tbut unable to open file \"%s\": %s\n\n", progname, checkdir, path, strerror(errno)); - exit(2); + ExitPostmaster(2); } FreeFile(fp); @@ -299,12 +298,12 @@ PostmasterMain(int argc, char *argv[]) if (strcmp(argv[1], "--help")==0 || strcmp(argv[1], "-?")==0) { usage(progname); - exit(0); + ExitPostmaster(0); } if (strcmp(argv[1], "--version")==0 || strcmp(argv[1], "-V")==0) { puts("postmaster (PostgreSQL) " PG_VERSION); - exit(0); + ExitPostmaster(0); } } @@ -367,7 +366,7 @@ PostmasterMain(int argc, char *argv[]) case '?': fprintf(stderr, "Try '%s --help' for more information.\n", progname); - exit(1); + ExitPostmaster(1); } } @@ -378,7 +377,7 @@ PostmasterMain(int argc, char *argv[]) { fprintf(stderr, "%s: invalid argument -- %s\n", progname, argv[optind]); fprintf(stderr, "Try '%s --help' for more information.\n", progname); - exit(1); + ExitPostmaster(1); } checkDataDir(potential_DataDir); /* issues error messages */ @@ -427,7 +426,7 @@ PostmasterMain(int argc, char *argv[]) enableFsync = false; break; case 'h': - Virtual_host = optarg; + VirtualHost = optarg; break; case 'i': NetServer = true; @@ -525,7 +524,7 @@ PostmasterMain(int argc, char *argv[]) default: /* shouldn't get here */ fprintf(stderr, "Try '%s --help' for more information.\n", progname); - exit(1); + ExitPostmaster(1); } } @@ -542,7 +541,7 @@ PostmasterMain(int argc, char *argv[]) */ fprintf(stderr, "%s: The number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16.\n", progname); - exit(1); + ExitPostmaster(1); } if (DebugLvl > 2) @@ -559,6 +558,27 @@ PostmasterMain(int argc, char *argv[]) } /* + * Fork away from controlling terminal, if -S specified. + * + * Must do this before we grab any interlock files, else the interlocks + * will show the wrong PID. + */ + if (SilentMode) + pmdaemonize(argc, argv); + + /* + * Create lockfile for data directory. + * + * We want to do this before we try to grab the input sockets, because + * the data directory interlock is more reliable than the socket-file + * interlock (thanks to whoever decided to put socket files in /tmp :-(). + * For the same reason, it's best to grab the TCP socket before the + * Unix socket. + */ + if (! CreateDataDirLockFile(DataDir, true)) + ExitPostmaster(1); + + /* * Establish input sockets. */ #ifdef USE_SSL @@ -566,7 +586,7 @@ PostmasterMain(int argc, char *argv[]) { fprintf(stderr, "%s: For SSL, TCP/IP connections must be enabled. See -? for help.\n", progname); - exit(1); + ExitPostmaster(1); } if (EnableSSL) InitSSL(); @@ -574,7 +594,7 @@ PostmasterMain(int argc, char *argv[]) if (NetServer) { - status = StreamServerPort(AF_INET, Virtual_host, + status = StreamServerPort(AF_INET, VirtualHost, (unsigned short) PostPortNumber, UnixSocketDir, &ServerSock_INET); if (status != STATUS_OK) @@ -586,7 +606,7 @@ PostmasterMain(int argc, char *argv[]) } #ifdef HAVE_UNIX_SOCKETS - status = StreamServerPort(AF_UNIX, Virtual_host, + status = StreamServerPort(AF_UNIX, VirtualHost, (unsigned short) PostPortNumber, UnixSocketDir, &ServerSock_UNIX); if (status != STATUS_OK) @@ -599,7 +619,9 @@ PostmasterMain(int argc, char *argv[]) XLOGPathInit(); - /* set up shared memory and semaphores */ + /* + * Set up shared memory and semaphores. + */ reset_shared(PostPortNumber); /* @@ -609,35 +631,12 @@ PostmasterMain(int argc, char *argv[]) BackendList = DLNewList(); PortList = DLNewList(); - if (SilentMode) - pmdaemonize(argc, argv); - else - { - - /* - * create pid file. if the file has already existed, exits. - */ - SetPidFname(DataDir); - if (SetPidFile(getpid()) == 0) - { - if (!CreateOptsFile(argc, argv)) - { - UnlinkPidFile(); - ExitPostmaster(1); - return 0; /* not reached */ - } - } - else - { - ExitPostmaster(1); - return 0; /* not reached */ - } - - /* - * register clean up proc - */ - on_proc_exit(UnlinkPidFile, 0); - } + /* + * Record postmaster options. We delay this till now to avoid recording + * bogus options (eg, NBuffers too high for available memory). + */ + if (!CreateOptsFile(argc, argv)) + ExitPostmaster(1); /* * Set up signal handlers for the postmaster process. @@ -658,11 +657,18 @@ PostmasterMain(int argc, char *argv[]) pqsignal(SIGTTOU, SIG_IGN); /* ignored */ pqsignal(SIGWINCH, dumpstatus); /* dump port status */ + /* + * We're ready to rock and roll... + */ StartupPID = StartupDataBase(); status = ServerLoop(); + /* + * ServerLoop probably shouldn't ever return, but if it does, close down. + */ ExitPostmaster(status != STATUS_OK); + return 0; /* not reached */ } @@ -672,8 +678,6 @@ pmdaemonize(int argc, char *argv[]) int i; pid_t pid; - SetPidFname(DataDir); - pid = fork(); if (pid == -1) { @@ -683,36 +687,8 @@ pmdaemonize(int argc, char *argv[]) } else if (pid) { /* parent */ - - /* - * create pid file. if the file has already existed, exits. - */ - if (SetPidFile(pid) == 0) - { - if (!CreateOptsFile(argc, argv)) - { - - /* - * Failed to create opts file. kill the child and exit - * now. - */ - UnlinkPidFile(); - kill(pid, SIGTERM); - ExitPostmaster(1); - return; /* not reached */ - } - _exit(0); - } - else - { - - /* - * Failed to create pid file. kill the child and exit now. - */ - kill(pid, SIGTERM); - ExitPostmaster(1); - return; /* not reached */ - } + /* Parent should just exit, without doing any atexit cleanup */ + _exit(0); } /* GH: If there's no setsid(), we hopefully don't need silent mode. @@ -723,7 +699,7 @@ pmdaemonize(int argc, char *argv[]) { fprintf(stderr, "%s: ", progname); perror("cannot disassociate from controlling TTY"); - exit(1); + ExitPostmaster(1); } #endif i = open(NULL_DEV, O_RDWR | PG_BINARY); @@ -731,11 +707,6 @@ pmdaemonize(int argc, char *argv[]) dup2(i, 1); dup2(i, 2); close(i); - - /* - * register clean up proc - */ - on_proc_exit(UnlinkPidFile, 0); } @@ -960,24 +931,19 @@ ServerLoop(void) if (status == STATUS_OK && port->pktInfo.state == Idle) { - /* - * Can't start backend if max backend count is exceeded. + * Can we accept a connection now? * - * The same when data base is in startup/shutdown mode. + * Even though readStartupPacket() already checked, + * we have to check again in case conditions changed + * while negotiating authentication. */ - if (Shutdown > NoShutdown) - PacketSendError(&port->pktInfo, - "The Data Base System is shutting down"); - else if (StartupPID) - PacketSendError(&port->pktInfo, - "The Data Base System is starting up"); - else if (FatalError) - PacketSendError(&port->pktInfo, - "The Data Base System is in recovery mode"); - else if (CountChildren() >= MaxBackends) - PacketSendError(&port->pktInfo, - "Sorry, too many clients already"); + char *rejectMsg = canAcceptConnections(); + + if (rejectMsg != NULL) + { + PacketSendError(&port->pktInfo, rejectMsg); + } else { @@ -1066,6 +1032,7 @@ readStartupPacket(void *arg, PacketLen len, void *pkt) { Port *port; StartupPacket *si; + char *rejectMsg; port = (Port *) arg; si = (StartupPacket *) pkt; @@ -1158,6 +1125,19 @@ readStartupPacket(void *arg, PacketLen len, void *pkt) return STATUS_OK; /* don't close the connection yet */ } + /* + * If we're going to reject the connection due to database state, + * say so now instead of wasting cycles on an authentication exchange. + * (This also allows a pg_ping utility to be written.) + */ + rejectMsg = canAcceptConnections(); + + if (rejectMsg != NULL) + { + PacketSendError(&port->pktInfo, rejectMsg); + return STATUS_OK; /* don't close the connection yet */ + } + /* Start the authentication itself. */ be_recvauth(port); @@ -1226,6 +1206,28 @@ processCancelRequest(Port *port, PacketLen len, void *pkt) return STATUS_ERROR; } +/* + * canAcceptConnections --- check to see if database state allows connections. + * + * If we are open for business, return NULL, otherwise return an error message + * string suitable for rejecting a connection request. + */ +static char * +canAcceptConnections(void) +{ + /* Can't start backends when in startup/shutdown/recovery state. */ + if (Shutdown > NoShutdown) + return "The Data Base System is shutting down"; + if (StartupPID) + return "The Data Base System is starting up"; + if (FatalError) + return "The Data Base System is in recovery mode"; + /* Can't start backend if max backend count is exceeded. */ + if (CountChildren() >= MaxBackends) + return "Sorry, too many clients already"; + + return NULL; +} /* * ConnCreate -- create a local connection data structure @@ -1423,7 +1425,7 @@ pmdie(SIGNAL_ARGS) } /* exit postmaster */ - proc_exit(0); + ExitPostmaster(0); } /* @@ -1465,9 +1467,9 @@ reaper(SIGNAL_ARGS) { fprintf(stderr, "Shutdown failed - abort\n"); fflush(stderr); - proc_exit(1); + ExitPostmaster(1); } - proc_exit(0); + ExitPostmaster(0); } if (StartupPID > 0) { @@ -1477,7 +1479,7 @@ reaper(SIGNAL_ARGS) { fprintf(stderr, "Startup failed - abort\n"); fflush(stderr); - proc_exit(1); + ExitPostmaster(1); } StartupPID = 0; FatalError = false; @@ -1507,7 +1509,7 @@ reaper(SIGNAL_ARGS) { /* - * Wait for all children exit then StartupDataBase. + * Wait for all children exit, then reset shmem and StartupDataBase. */ if (DLGetHead(BackendList)) return; @@ -1518,8 +1520,10 @@ reaper(SIGNAL_ARGS) "Reinitializing shared memory and semaphores\n", ctime(&tnow)); fflush(stderr); + shmem_exit(0); reset_shared(PostPortNumber); + StartupPID = StartupDataBase(); return; } @@ -1744,10 +1748,10 @@ BackendStartup(Port *port) { fprintf(stderr, "%s child[%d]: BackendStartup: backend startup failed\n", progname, (int) getpid()); - exit(1); + ExitPostmaster(1); } else - exit(0); + ExitPostmaster(0); } /* in parent */ @@ -1943,9 +1947,9 @@ DoBackend(Port *port) /* * Release postmaster's working memory context so that backend can - * recycle the space. Note we couldn't do it earlier than here, - * because port pointer is pointing into that space! But now we - * have copied all the interesting info into safe local storage. + * recycle the space. Note this does not trash *MyProcPort, because + * ConnCreate() allocated that space with malloc() ... else we'd need + * to copy the Port data here. */ MemoryContextSwitchTo(TopMemoryContext); MemoryContextDelete(PostmasterContext); @@ -1968,6 +1972,8 @@ DoBackend(Port *port) /* * ExitPostmaster -- cleanup + * + * Do NOT call exit() directly --- always go through here! */ static void ExitPostmaster(int status) @@ -2099,24 +2105,24 @@ InitSSL(void) if (!SSL_context) { fprintf(stderr, "Failed to create SSL context: %s\n", ERR_reason_error_string(ERR_get_error())); - exit(1); + ExitPostmaster(1); } snprintf(fnbuf, sizeof(fnbuf), "%s/server.crt", DataDir); if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) { fprintf(stderr, "Failed to load server certificate (%s): %s\n", fnbuf, ERR_reason_error_string(ERR_get_error())); - exit(1); + ExitPostmaster(1); } snprintf(fnbuf, sizeof(fnbuf), "%s/server.key", DataDir); if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) { fprintf(stderr, "Failed to load private key file (%s): %s\n", fnbuf, ERR_reason_error_string(ERR_get_error())); - exit(1); + ExitPostmaster(1); } if (!SSL_CTX_check_private_key(SSL_context)) { fprintf(stderr, "Check of private key failed: %s\n", ERR_reason_error_string(ERR_get_error())); - exit(1); + ExitPostmaster(1); } } @@ -2178,7 +2184,7 @@ SSDataBase(int xlop) PG_SETMASK(&BlockSig); BootstrapMain(ac, av); - exit(0); + ExitPostmaster(0); } /* in parent */ diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 0f8dec00d89..7d143a9590a 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.191 2000/11/25 20:33:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.192 2000/11/29 20:59:52 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -1514,16 +1514,10 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha } /* - * Try to create pid file. + * Create lockfile for data directory. */ - SetPidFname(DataDir); - if (SetPidFile(-getpid())) - proc_exit(0); - - /* - * Register clean up proc. - */ - on_proc_exit(UnlinkPidFile, 0); + if (! CreateDataDirLockFile(DataDir, false)) + proc_exit(1); XLOGPathInit(); BaseInit(); @@ -1635,7 +1629,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.191 $ $Date: 2000/11/25 20:33:52 $\n"); + puts("$Revision: 1.192 $ $Date: 2000/11/29 20:59:52 $\n"); } /* diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 919a9dd220f..98af889946f 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * miscinit.c - * miscellanious initialization support stuff + * miscellaneous initialization support stuff * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.58 2000/11/17 01:24:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.59 2000/11/29 20:59:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,8 +32,6 @@ #include "utils/builtins.h" #include "utils/syscache.h" -static char *GetPidFname(void); - #ifdef CYR_RECODE unsigned char RecodeForwTable[128]; @@ -43,6 +41,7 @@ unsigned char RecodeBackTable[128]; ProcessingMode Mode = InitProcessing; + /* ---------------------------------------------------------------- * ignoring system indexes support stuff * ---------------------------------------------------------------- @@ -99,11 +98,71 @@ SetDatabaseName(const char *name) } } -#ifndef MULTIBYTE -/* even if MULTIBYTE is not enabled, these functions are necessary +/* + * Set data directory, but make sure it's an absolute path. Use this, + * never set DataDir directly. + */ +void +SetDataDir(const char *dir) +{ + char *new; + + AssertArg(dir); + if (DataDir) + free(DataDir); + + if (dir[0] != '/') + { + char *buf; + size_t buflen; + + buflen = MAXPGPATH; + for (;;) + { + buf = malloc(buflen); + if (!buf) + elog(FATAL, "out of memory"); + + if (getcwd(buf, buflen)) + break; + else if (errno == ERANGE) + { + free(buf); + buflen *= 2; + continue; + } + else + { + free(buf); + elog(FATAL, "cannot get current working directory: %m"); + } + } + + new = malloc(strlen(buf) + 1 + strlen(dir) + 1); + sprintf(new, "%s/%s", buf, dir); + free(buf); + } + else + { + new = strdup(dir); + } + + if (!new) + elog(FATAL, "out of memory"); + DataDir = new; +} + + +/* ---------------------------------------------------------------- + * MULTIBYTE stub code + * + * Even if MULTIBYTE is not enabled, these functions are necessary * since pg_proc.h has references to them. + * ---------------------------------------------------------------- */ +#ifndef MULTIBYTE + Datum getdatabaseencoding(PG_FUNCTION_ARGS) { @@ -124,7 +183,13 @@ PG_char_to_encoding(PG_FUNCTION_ARGS) #endif +/* ---------------------------------------------------------------- + * CYR_RECODE support + * ---------------------------------------------------------------- + */ + #ifdef CYR_RECODE + #define MAX_TOKEN 80 /* Some standard C libraries, including GNU, have an isblank() function. @@ -376,228 +441,189 @@ GetUserName(Oid userid) /*------------------------------------------------------------------------- - * Set data directory, but make sure it's an absolute path. Use this, - * never set DataDir directly. - *------------------------------------------------------------------------- - */ -void -SetDataDir(const char *dir) -{ - char *new; - - AssertArg(dir); - if (DataDir) - free(DataDir); - - if (dir[0] != '/') - { - char *buf; - size_t buflen; - - buflen = MAXPGPATH; - for (;;) - { - buf = malloc(buflen); - if (!buf) - elog(FATAL, "out of memory"); - - if (getcwd(buf, buflen)) - break; - else if (errno == ERANGE) - { - free(buf); - buflen *= 2; - continue; - } - else - { - free(buf); - elog(FATAL, "cannot get current working directory: %m"); - } - } - - new = malloc(strlen(buf) + 1 + strlen(dir) + 1); - sprintf(new, "%s/%s", buf, dir); - } - else - { - new = strdup(dir); - } - - if (!new) - elog(FATAL, "out of memory"); - DataDir = new; -} - - - -/*------------------------------------------------------------------------- + * Interlock-file support * - * postmaster pid file stuffs. $DATADIR/postmaster.pid is created when: + * These routines are used to create both a data-directory lockfile + * ($DATADIR/postmaster.pid) and a Unix-socket-file lockfile ($SOCKFILE.lock). + * Both kinds of files contain the same info: * - * (1) postmaster starts. In this case pid > 0. - * (2) postgres starts in standalone mode. In this case - * pid < 0 + * Owning process' PID + * Data directory path * - * to gain an interlock. - * - * SetPidFname(datadir) - * Remember the the pid file name. This is neccesary - * UnlinkPidFile() is called from proc_exit(). - * - * GetPidFname(datadir) - * Get the pid file name. SetPidFname() should be called - * before GetPidFname() gets called. - * - * UnlinkPidFile() - * This is called from proc_exit() and unlink the pid file. - * - * SetPidFile(pid_t pid) - * Create the pid file. On failure, it checks if the process - * actually exists or not. SetPidFname() should be called - * in prior to calling SetPidFile(). + * By convention, the owning process' PID is negated if it is a standalone + * backend rather than a postmaster. This is just for informational purposes. + * The path is also just for informational purposes (so that a socket lockfile + * can be more easily traced to the associated postmaster). * + * On successful lockfile creation, a proc_exit callback to remove the + * lockfile is automatically created. *------------------------------------------------------------------------- */ /* - * Path to pid file. proc_exit() remember it to unlink the file. - */ -static char PidFile[MAXPGPATH]; - -/* - * Remove the pid file. This function is called from proc_exit. - */ -void -UnlinkPidFile(void) -{ - unlink(PidFile); -} - -/* - * Set path to the pid file - */ -void -SetPidFname(char *datadir) -{ - snprintf(PidFile, sizeof(PidFile), "%s/%s", datadir, PIDFNAME); -} - -/* - * Get path to the pid file + * proc_exit callback to remove a lockfile. */ -static char * -GetPidFname(void) +static void +UnlinkLockFile(int status, Datum filename) { - return (PidFile); + unlink((char *) DatumGetPointer(filename)); + /* Should we complain if the unlink fails? */ } /* - * Create the pid file + * Create a lockfile, if possible + * + * Call CreateLockFile with the name of the lockfile to be created. If + * successful, it returns zero. On detecting a collision, it returns + * the PID or negated PID of the lockfile owner --- the caller is responsible + * for producing an appropriate error message. */ -int -SetPidFile(pid_t pid) +static int +CreateLockFile(const char *filename, bool amPostmaster) { int fd; - char *pidfile; - char pidstr[32]; + char buffer[MAXPGPATH + 32]; int len; - pid_t post_pid; - int is_postgres = 0; + int encoded_pid; + pid_t other_pid; + pid_t my_pid = getpid(); /* - * Creating pid file + * We need a loop here because of race conditions. */ - pidfile = GetPidFname(); - fd = open(pidfile, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd < 0) + for (;;) { + /* + * Try to create the lock file --- O_EXCL makes this atomic. + */ + fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) + break; /* Success; exit the retry loop */ + /* + * Couldn't create the pid file. Probably it already exists. + */ + if (errno != EEXIST && errno != EACCES) + elog(FATAL, "Can't create lock file %s: %m", filename); /* - * Couldn't create the pid file. Probably it already exists. Read - * the file to see if the process actually exists + * Read the file to get the old owner's PID. Note race condition + * here: file might have been deleted since we tried to create it. */ - fd = open(pidfile, O_RDONLY, 0600); + fd = open(filename, O_RDONLY, 0600); if (fd < 0) { - fprintf(stderr, "Can't open pid file: %s\n", pidfile); - fprintf(stderr, "Please check the permission and try again.\n"); - return (-1); - } - if ((len = read(fd, pidstr, sizeof(pidstr) - 1)) < 0) - { - fprintf(stderr, "Can't read pid file: %s\n", pidfile); - fprintf(stderr, "Please check the permission and try again.\n"); - close(fd); - return (-1); + if (errno == ENOENT) + continue; /* race condition; try again */ + elog(FATAL, "Can't read lock file %s: %m", filename); } + if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0) + elog(FATAL, "Can't read lock file %s: %m", filename); close(fd); - /* - * Check to see if the process actually exists - */ - pidstr[len] = '\0'; - post_pid = (pid_t) atoi(pidstr); + buffer[len] = '\0'; + encoded_pid = atoi(buffer); - /* if pid < 0, the pid is for postgres, not postmatser */ - if (post_pid < 0) - { - is_postgres++; - post_pid = -post_pid; - } + /* if pid < 0, the pid is for postgres, not postmaster */ + other_pid = (pid_t) (encoded_pid < 0 ? -encoded_pid : encoded_pid); - if (post_pid == 0 || (post_pid > 0 && kill(post_pid, 0) < 0)) - { + if (other_pid <= 0) + elog(FATAL, "Bogus data in lock file %s", filename); - /* - * No, the process did not exist. Unlink the file and try to - * create it - */ - if (unlink(pidfile) < 0) - { - fprintf(stderr, "Can't remove pid file: %s\n", pidfile); - fprintf(stderr, "The file seems accidently left, but I couldn't remove it.\n"); - fprintf(stderr, "Please remove the file by hand and try again.\n"); - return (-1); - } - fd = open(pidfile, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd < 0) - { - fprintf(stderr, "Can't create pid file: %s\n", pidfile); - fprintf(stderr, "Please check the permission and try again.\n"); - return (-1); - } - } - else + /* + * Check to see if the other process still exists + */ + if (other_pid != my_pid) { - - /* - * Another postmaster is running - */ - fprintf(stderr, "Can't create pid file: %s\n", pidfile); - if (is_postgres) - fprintf(stderr, "Is another postgres (pid: %d) running?\n", (int) post_pid); - else - fprintf(stderr, "Is another postmaster (pid: %s) running?\n", pidstr); - return (-1); + if (kill(other_pid, 0) == 0 || + errno != ESRCH) + return encoded_pid; /* lockfile belongs to a live process */ } + + /* + * No, the process did not exist. Unlink the file and try again to + * create it. Need a loop because of possible race condition against + * other would-be creators. + */ + if (unlink(filename) < 0) + elog(FATAL, "Can't remove old lock file %s: %m" + "\n\tThe file seems accidentally left, but I couldn't remove it." + "\n\tPlease remove the file by hand and try again.", + filename); } - sprintf(pidstr, "%d", (int) pid); - if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) + /* + * Successfully created the file, now fill it. + */ + snprintf(buffer, sizeof(buffer), "%d\n%s\n", + amPostmaster ? (int) my_pid : - ((int) my_pid), + DataDir); + if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) { - fprintf(stderr, "Write to pid file failed\n"); - fprintf(stderr, "Please check the permission and try again.\n"); + int save_errno = errno; + close(fd); - unlink(pidfile); - return (-1); + unlink(filename); + errno = save_errno; + elog(FATAL, "Can't write lock file %s: %m", filename); } close(fd); - return (0); + /* + * Arrange for automatic removal of lockfile at proc_exit. + */ + on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename))); + + return 0; /* Success! */ +} + +bool +CreateDataDirLockFile(const char *datadir, bool amPostmaster) +{ + char lockfile[MAXPGPATH]; + int encoded_pid; + + snprintf(lockfile, sizeof(lockfile), "%s/postmaster.pid", datadir); + encoded_pid = CreateLockFile(lockfile, amPostmaster); + if (encoded_pid != 0) + { + fprintf(stderr, "Lock file \"%s\" already exists.\n", lockfile); + if (encoded_pid < 0) + fprintf(stderr, "Is another postgres (pid %d) running in \"%s\"?\n", + -encoded_pid, datadir); + else + fprintf(stderr, "Is another postmaster (pid %d) running in \"%s\"?\n", + encoded_pid, datadir); + return false; + } + return true; } +bool +CreateSocketLockFile(const char *socketfile, bool amPostmaster) +{ + char lockfile[MAXPGPATH]; + int encoded_pid; + + snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile); + encoded_pid = CreateLockFile(lockfile, amPostmaster); + if (encoded_pid != 0) + { + fprintf(stderr, "Lock file \"%s\" already exists.\n", lockfile); + if (encoded_pid < 0) + fprintf(stderr, "Is another postgres (pid %d) using \"%s\"?\n", + -encoded_pid, socketfile); + else + fprintf(stderr, "Is another postmaster (pid %d) using \"%s\"?\n", + encoded_pid, socketfile); + return false; + } + return true; +} +/*------------------------------------------------------------------------- + * Version checking support + *------------------------------------------------------------------------- + */ /* * Determine whether the PG_VERSION file in directory `path' indicates @@ -628,12 +654,12 @@ ValidatePgVersion(const char *path) if (errno == ENOENT) elog(FATAL, "File %s is missing. This is not a valid data directory.", full_path); else - elog(FATAL, "cannot open %s: %s", full_path, strerror(errno)); + elog(FATAL, "cannot open %s: %m", full_path); } ret = fscanf(file, "%ld.%ld", &file_major, &file_minor); if (ret == EOF) - elog(FATAL, "cannot read %s: %s", full_path, strerror(errno)); + elog(FATAL, "cannot read %s: %m", full_path); else if (ret != 2) elog(FATAL, "`%s' does not have a valid format. You need to initdb.", full_path); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 7813b6d10ba..398fb25bf68 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -4,7 +4,7 @@ * Support for grand unified configuration scheme, including SET * command, configuration file, and command line options. * - * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.23 2000/11/25 04:13:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.24 2000/11/29 20:59:53 tgl Exp $ * * Copyright 2000 by PostgreSQL Global Development Group * Written by Peter Eisentraut <peter_e@gmx.net>. @@ -319,7 +319,7 @@ ConfigureNamesString[] = {"unix_socket_directory", PGC_POSTMASTER, &UnixSocketDir, "", NULL}, - {"virtual_host", PGC_POSTMASTER, &Virtual_host, + {"virtual_host", PGC_POSTMASTER, &VirtualHost, "", NULL}, {NULL, 0, NULL, NULL, NULL} diff --git a/src/bin/pg_ctl/pg_ctl.sh b/src/bin/pg_ctl/pg_ctl.sh index ae7a1c19a02..6802269befb 100755 --- a/src/bin/pg_ctl/pg_ctl.sh +++ b/src/bin/pg_ctl/pg_ctl.sh @@ -8,7 +8,7 @@ # # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/bin/pg_ctl/Attic/pg_ctl.sh,v 1.15 2000/11/27 02:50:17 tgl Exp $ +# $Header: /cvsroot/pgsql/src/bin/pg_ctl/Attic/pg_ctl.sh,v 1.16 2000/11/29 20:59:53 tgl Exp $ # #------------------------------------------------------------------------- @@ -187,7 +187,7 @@ PIDFILE=$PGDATA/postmaster.pid if [ $op = "status" ];then if [ -f $PIDFILE ];then - PID=`cat $PIDFILE` + PID=`head -1 $PIDFILE` if [ $PID -lt 0 ];then PID=`expr 0 - $PID` echo "$CMDNAME: postgres is running (pid: $PID)" @@ -205,7 +205,7 @@ fi if [ $op = "stop" -o $op = "restart" ];then if [ -f $PIDFILE ];then - PID=`cat $PIDFILE` + PID=`head -1 $PIDFILE` if [ $PID -lt 0 ];then PID=`expr 0 - $PID` echo "$CMDNAME: Cannot restart postmaster. postgres is running (pid: $PID)" @@ -213,7 +213,7 @@ if [ $op = "stop" -o $op = "restart" ];then exit 1 fi - kill $sig `cat $PIDFILE` + kill $sig $PID # wait for postmaster shutting down if [ "$wait" = 1 -o $op = "restart" ];then @@ -253,7 +253,7 @@ fi if [ $op = "start" -o $op = "restart" ];then if [ -f $PIDFILE ];then echo "$CMDNAME: It seems another postmaster is running. Trying to start postmaster anyway." - pid=`cat $PIDFILE` + pid=`head -1 $PIDFILE` fi # no -o given @@ -275,7 +275,7 @@ if [ $op = "start" -o $op = "restart" ];then fi if [ -f $PIDFILE ];then - if [ "`cat $PIDFILE`" = "$pid" ];then + if [ "`head -1 $PIDFILE`" = "$pid" ];then echo "$CMDNAME: Cannot start postmaster. Is another postmaster is running?" exit 1 fi diff --git a/src/include/config.h.in b/src/include/config.h.in index ab699b30f3c..1f6939dd960 100644 --- a/src/include/config.h.in +++ b/src/include/config.h.in @@ -8,7 +8,7 @@ * or in config.h afterwards. Of course, if you edit config.h, then your * changes will be overwritten the next time you run configure. * - * $Id: config.h.in,v 1.149 2000/11/20 16:52:54 petere Exp $ + * $Id: config.h.in,v 1.150 2000/11/29 20:59:54 tgl Exp $ */ #ifndef CONFIG_H @@ -567,9 +567,6 @@ extern void srandom(unsigned int seed); /* Set to 1 if you have struct sockaddr_un */ #undef HAVE_STRUCT_SOCKADDR_UN -/* Set to 1 if you have F_SETLK option for fcntl() */ -#undef HAVE_FCNTL_SETLK - /* Set to 1 if type "long int" works and is 64 bits */ #undef HAVE_LONG_INT_64 diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index cff6439dde3..f41a01822ac 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: miscadmin.h,v 1.74 2000/11/25 22:34:13 momjian Exp $ + * $Id: miscadmin.h,v 1.75 2000/11/29 20:59:54 tgl Exp $ * * NOTES * some of the information in this file will be moved to @@ -114,7 +114,7 @@ extern int PostPortNumber; extern int Unix_socket_permissions; extern char *Unix_socket_group; extern char *UnixSocketDir; -extern char *Virtual_host; +extern char *VirtualHost; /***************************************************************************** @@ -227,17 +227,8 @@ extern bool IsIgnoringSystemIndexes(void); extern bool IsCacheInitialized(void); extern void SetWaitingForLock(bool); -/* - * "postmaster.pid" is a file containing postmaster's pid, being - * created uder $PGDATA upon postmaster's starting up. When postmaster - * shuts down, it will be unlinked. -*/ -#define PIDFNAME "postmaster.pid" - -extern void SetPidFname(char *datadir); -extern void UnlinkPidFile(void); -extern int SetPidFile(pid_t pid); - +extern bool CreateDataDirLockFile(const char *datadir, bool amPostmaster); +extern bool CreateSocketLockFile(const char *socketfile, bool amPostmaster); extern void ValidatePgVersion(const char *path); |