mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
Support having multiple Unix-domain sockets per postmaster.
Replace unix_socket_directory with unix_socket_directories, which is a list of socket directories, and adjust postmaster's code to allow zero or more Unix-domain sockets to be created. This is mostly a straightforward change, but since the Unix sockets ought to be created after the TCP/IP sockets for safety reasons (better chance of detecting a port number conflict), AddToDataDirLockFile needs to be fixed to support out-of-order updates of data directory lockfile lines. That's a change that had been foreseen to be necessary someday anyway. Honza Horak, reviewed and revised by Tom Lane
This commit is contained in:
@@ -2446,6 +2446,116 @@ SplitIdentifierString(char *rawstring, char separator,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SplitDirectoriesString --- parse a string containing directory names
|
||||
*
|
||||
* This is similar to SplitIdentifierString, except that the parsing
|
||||
* rules are meant to handle pathnames instead of identifiers: there is
|
||||
* no downcasing, the max length is MAXPGPATH-1, and we apply
|
||||
* canonicalize_path() to each extracted string. Because of the last,
|
||||
* the returned strings are separately palloc'd rather than being
|
||||
* pointers into rawstring --- but we still scribble on rawstring.
|
||||
*
|
||||
* Inputs:
|
||||
* rawstring: the input string; must be modifiable!
|
||||
* separator: the separator punctuation expected between directories
|
||||
* (typically ',' or ';'). Whitespace may also appear around
|
||||
* directories.
|
||||
* Outputs:
|
||||
* namelist: filled with a palloc'd list of directory names.
|
||||
* Caller should list_free_deep() this even on error return.
|
||||
*
|
||||
* Returns TRUE if okay, FALSE if there is a syntax error in the string.
|
||||
*
|
||||
* Note that an empty string is considered okay here.
|
||||
*/
|
||||
bool
|
||||
SplitDirectoriesString(char *rawstring, char separator,
|
||||
List **namelist)
|
||||
{
|
||||
char *nextp = rawstring;
|
||||
bool done = false;
|
||||
|
||||
*namelist = NIL;
|
||||
|
||||
while (isspace((unsigned char) *nextp))
|
||||
nextp++; /* skip leading whitespace */
|
||||
|
||||
if (*nextp == '\0')
|
||||
return true; /* allow empty string */
|
||||
|
||||
/* At the top of the loop, we are at start of a new directory. */
|
||||
do
|
||||
{
|
||||
char *curname;
|
||||
char *endp;
|
||||
|
||||
if (*nextp == '\"')
|
||||
{
|
||||
/* Quoted name --- collapse quote-quote pairs */
|
||||
curname = nextp + 1;
|
||||
for (;;)
|
||||
{
|
||||
endp = strchr(nextp + 1, '\"');
|
||||
if (endp == NULL)
|
||||
return false; /* mismatched quotes */
|
||||
if (endp[1] != '\"')
|
||||
break; /* found end of quoted name */
|
||||
/* Collapse adjacent quotes into one quote, and look again */
|
||||
memmove(endp, endp + 1, strlen(endp));
|
||||
nextp = endp;
|
||||
}
|
||||
/* endp now points at the terminating quote */
|
||||
nextp = endp + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unquoted name --- extends to separator or whitespace */
|
||||
curname = nextp;
|
||||
while (*nextp && *nextp != separator &&
|
||||
!isspace((unsigned char) *nextp))
|
||||
nextp++;
|
||||
endp = nextp;
|
||||
if (curname == nextp)
|
||||
return false; /* empty unquoted name not allowed */
|
||||
}
|
||||
|
||||
while (isspace((unsigned char) *nextp))
|
||||
nextp++; /* skip trailing whitespace */
|
||||
|
||||
if (*nextp == separator)
|
||||
{
|
||||
nextp++;
|
||||
while (isspace((unsigned char) *nextp))
|
||||
nextp++; /* skip leading whitespace for next */
|
||||
/* we expect another name, so done remains false */
|
||||
}
|
||||
else if (*nextp == '\0')
|
||||
done = true;
|
||||
else
|
||||
return false; /* invalid syntax */
|
||||
|
||||
/* Now safe to overwrite separator with a null */
|
||||
*endp = '\0';
|
||||
|
||||
/* Truncate path if it's overlength */
|
||||
if (strlen(curname) >= MAXPGPATH)
|
||||
curname[MAXPGPATH - 1] = '\0';
|
||||
|
||||
/*
|
||||
* Finished isolating current name --- add it to list
|
||||
*/
|
||||
curname = pstrdup(curname);
|
||||
canonicalize_path(curname);
|
||||
*namelist = lappend(*namelist, curname);
|
||||
|
||||
/* Loop back if we didn't reach end of string */
|
||||
} while (!done);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Comparison Functions used for bytea
|
||||
*
|
||||
|
@@ -49,8 +49,8 @@
|
||||
|
||||
ProcessingMode Mode = InitProcessing;
|
||||
|
||||
/* Note: we rely on this to initialize as zeroes */
|
||||
static char socketLockFile[MAXPGPATH];
|
||||
/* List of lock files to be removed at proc exit */
|
||||
static List *lock_files = NIL;
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@@ -628,7 +628,7 @@ GetUserNameFromId(Oid roleid)
|
||||
* Interlock-file support
|
||||
*
|
||||
* These routines are used to create both a data-directory lockfile
|
||||
* ($DATADIR/postmaster.pid) and a Unix-socket-file lockfile ($SOCKFILE.lock).
|
||||
* ($DATADIR/postmaster.pid) and Unix-socket-file lockfiles ($SOCKFILE.lock).
|
||||
* Both kinds of files contain the same info initially, although we can add
|
||||
* more information to a data-directory lockfile after it's created, using
|
||||
* AddToDataDirLockFile(). See miscadmin.h for documentation of the contents
|
||||
@@ -640,32 +640,35 @@ GetUserNameFromId(Oid roleid)
|
||||
*/
|
||||
|
||||
/*
|
||||
* proc_exit callback to remove a lockfile.
|
||||
* proc_exit callback to remove lockfiles.
|
||||
*/
|
||||
static void
|
||||
UnlinkLockFile(int status, Datum filename)
|
||||
UnlinkLockFiles(int status, Datum arg)
|
||||
{
|
||||
char *fname = (char *) DatumGetPointer(filename);
|
||||
ListCell *l;
|
||||
|
||||
if (fname != NULL)
|
||||
foreach(l, lock_files)
|
||||
{
|
||||
if (unlink(fname) != 0)
|
||||
{
|
||||
/* Should we complain if the unlink fails? */
|
||||
}
|
||||
free(fname);
|
||||
char *curfile = (char *) lfirst(l);
|
||||
|
||||
unlink(curfile);
|
||||
/* Should we complain if the unlink fails? */
|
||||
}
|
||||
/* Since we're about to exit, no need to reclaim storage */
|
||||
lock_files = NIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a lockfile.
|
||||
*
|
||||
* filename is the name of the lockfile to create.
|
||||
* filename is the path name of the lockfile to create.
|
||||
* amPostmaster is used to determine how to encode the output PID.
|
||||
* socketDir is the Unix socket directory path to include (possibly empty).
|
||||
* isDDLock and refName are used to determine what error message to produce.
|
||||
*/
|
||||
static void
|
||||
CreateLockFile(const char *filename, bool amPostmaster,
|
||||
const char *socketDir,
|
||||
bool isDDLock, const char *refName)
|
||||
{
|
||||
int fd;
|
||||
@@ -891,12 +894,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
|
||||
DataDir,
|
||||
(long) MyStartTime,
|
||||
PostPortNumber,
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
(*UnixSocketDir != '\0') ? UnixSocketDir : DEFAULT_PGSOCKET_DIR
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
socketDir);
|
||||
|
||||
errno = 0;
|
||||
if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
|
||||
@@ -934,9 +932,14 @@ CreateLockFile(const char *filename, bool amPostmaster,
|
||||
}
|
||||
|
||||
/*
|
||||
* Arrange for automatic removal of lockfile at proc_exit.
|
||||
* Arrange to unlink the lock file(s) at proc_exit. If this is the
|
||||
* first one, set up the on_proc_exit function to do it; then add this
|
||||
* lock file to the list of files to unlink.
|
||||
*/
|
||||
on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename)));
|
||||
if (lock_files == NIL)
|
||||
on_proc_exit(UnlinkLockFiles, 0);
|
||||
|
||||
lock_files = lappend(lock_files, pstrdup(filename));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -945,41 +948,50 @@ CreateLockFile(const char *filename, bool amPostmaster,
|
||||
* When this is called, we must have already switched the working
|
||||
* directory to DataDir, so we can just use a relative path. This
|
||||
* helps ensure that we are locking the directory we should be.
|
||||
*
|
||||
* Note that the socket directory path line is initially written as empty.
|
||||
* postmaster.c will rewrite it upon creating the first Unix socket.
|
||||
*/
|
||||
void
|
||||
CreateDataDirLockFile(bool amPostmaster)
|
||||
{
|
||||
CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, true, DataDir);
|
||||
CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, "", true, DataDir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a lockfile for the specified Unix socket file.
|
||||
*/
|
||||
void
|
||||
CreateSocketLockFile(const char *socketfile, bool amPostmaster)
|
||||
CreateSocketLockFile(const char *socketfile, bool amPostmaster,
|
||||
const char *socketDir)
|
||||
{
|
||||
char lockfile[MAXPGPATH];
|
||||
|
||||
snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile);
|
||||
CreateLockFile(lockfile, amPostmaster, false, socketfile);
|
||||
/* Save name of lockfile for TouchSocketLockFile */
|
||||
strcpy(socketLockFile, lockfile);
|
||||
CreateLockFile(lockfile, amPostmaster, socketDir, false, socketfile);
|
||||
}
|
||||
|
||||
/*
|
||||
* TouchSocketLockFile -- mark socket lock file as recently accessed
|
||||
* TouchSocketLockFiles -- mark socket lock files as recently accessed
|
||||
*
|
||||
* This routine should be called every so often to ensure that the lock file
|
||||
* has a recent mod or access date. That saves it
|
||||
* This routine should be called every so often to ensure that the socket
|
||||
* lock files have a recent mod or access date. That saves them
|
||||
* from being removed by overenthusiastic /tmp-directory-cleaner daemons.
|
||||
* (Another reason we should never have put the socket file in /tmp...)
|
||||
*/
|
||||
void
|
||||
TouchSocketLockFile(void)
|
||||
TouchSocketLockFiles(void)
|
||||
{
|
||||
/* Do nothing if we did not create a socket... */
|
||||
if (socketLockFile[0] != '\0')
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, lock_files)
|
||||
{
|
||||
char *socketLockFile = (char *) lfirst(l);
|
||||
|
||||
/* No need to touch the data directory lock file, we trust */
|
||||
if (strcmp(socketLockFile, DIRECTORY_LOCK_FILE) == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* utime() is POSIX standard, utimes() is a common alternative; if we
|
||||
* have neither, fall back to actually reading the file (which only
|
||||
@@ -1011,8 +1023,10 @@ TouchSocketLockFile(void)
|
||||
* Add (or replace) a line in the data directory lock file.
|
||||
* The given string should not include a trailing newline.
|
||||
*
|
||||
* Caution: this erases all following lines. In current usage that is OK
|
||||
* because lines are added in order. We could improve it if needed.
|
||||
* Note: because we don't truncate the file, if we were to rewrite a line
|
||||
* with less data than it had before, there would be garbage after the last
|
||||
* line. We don't ever actually do that, so not worth adding another kernel
|
||||
* call to cover the possibility.
|
||||
*/
|
||||
void
|
||||
AddToDataDirLockFile(int target_line, const char *str)
|
||||
@@ -1020,8 +1034,10 @@ AddToDataDirLockFile(int target_line, const char *str)
|
||||
int fd;
|
||||
int len;
|
||||
int lineno;
|
||||
char *ptr;
|
||||
char buffer[BLCKSZ];
|
||||
char *srcptr;
|
||||
char *destptr;
|
||||
char srcbuffer[BLCKSZ];
|
||||
char destbuffer[BLCKSZ];
|
||||
|
||||
fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
|
||||
if (fd < 0)
|
||||
@@ -1032,7 +1048,7 @@ AddToDataDirLockFile(int target_line, const char *str)
|
||||
DIRECTORY_LOCK_FILE)));
|
||||
return;
|
||||
}
|
||||
len = read(fd, buffer, sizeof(buffer) - 1);
|
||||
len = read(fd, srcbuffer, sizeof(srcbuffer) - 1);
|
||||
if (len < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
@@ -1042,36 +1058,50 @@ AddToDataDirLockFile(int target_line, const char *str)
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
buffer[len] = '\0';
|
||||
srcbuffer[len] = '\0';
|
||||
|
||||
/*
|
||||
* Skip over lines we are not supposed to rewrite.
|
||||
* Advance over lines we are not supposed to rewrite, then copy them
|
||||
* to destbuffer.
|
||||
*/
|
||||
ptr = buffer;
|
||||
srcptr = srcbuffer;
|
||||
for (lineno = 1; lineno < target_line; lineno++)
|
||||
{
|
||||
if ((ptr = strchr(ptr, '\n')) == NULL)
|
||||
if ((srcptr = strchr(srcptr, '\n')) == NULL)
|
||||
{
|
||||
elog(LOG, "bogus data in \"%s\"", DIRECTORY_LOCK_FILE);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
ptr++;
|
||||
srcptr++;
|
||||
}
|
||||
memcpy(destbuffer, srcbuffer, srcptr - srcbuffer);
|
||||
destptr = destbuffer + (srcptr - srcbuffer);
|
||||
|
||||
/*
|
||||
* Write or rewrite the target line.
|
||||
*/
|
||||
snprintf(ptr, buffer + sizeof(buffer) - ptr, "%s\n", str);
|
||||
snprintf(destptr, destbuffer + sizeof(destbuffer) - destptr, "%s\n", str);
|
||||
destptr += strlen(destptr);
|
||||
|
||||
/*
|
||||
* If there are more lines in the old file, append them to destbuffer.
|
||||
*/
|
||||
if ((srcptr = strchr(srcptr, '\n')) != NULL)
|
||||
{
|
||||
srcptr++;
|
||||
snprintf(destptr, destbuffer + sizeof(destbuffer) - destptr, "%s",
|
||||
srcptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* And rewrite the data. Since we write in a single kernel call, this
|
||||
* update should appear atomic to onlookers.
|
||||
*/
|
||||
len = strlen(buffer);
|
||||
len = strlen(destbuffer);
|
||||
errno = 0;
|
||||
if (lseek(fd, (off_t) 0, SEEK_SET) != 0 ||
|
||||
(int) write(fd, buffer, len) != len)
|
||||
(int) write(fd, destbuffer, len) != len)
|
||||
{
|
||||
/* if write didn't set errno, assume problem is no disk space */
|
||||
if (errno == 0)
|
||||
|
@@ -2895,14 +2895,18 @@ static struct config_string ConfigureNamesString[] =
|
||||
},
|
||||
|
||||
{
|
||||
{"unix_socket_directory", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
|
||||
gettext_noop("Sets the directory where the Unix-domain socket will be created."),
|
||||
{"unix_socket_directories", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
|
||||
gettext_noop("Sets the directories where Unix-domain sockets will be created."),
|
||||
NULL,
|
||||
GUC_SUPERUSER_ONLY
|
||||
},
|
||||
&UnixSocketDir,
|
||||
&Unix_socket_directories,
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
DEFAULT_PGSOCKET_DIR,
|
||||
#else
|
||||
"",
|
||||
check_canonical_path, NULL, NULL
|
||||
#endif
|
||||
NULL, NULL, NULL
|
||||
},
|
||||
|
||||
{
|
||||
|
@@ -65,7 +65,8 @@
|
||||
# Note: Increasing max_connections costs ~400 bytes of shared memory per
|
||||
# connection slot, plus lock space (see max_locks_per_transaction).
|
||||
#superuser_reserved_connections = 3 # (change requires restart)
|
||||
#unix_socket_directory = '' # (change requires restart)
|
||||
#unix_socket_directories = '/tmp' # comma-separated list of directories
|
||||
# (change requires restart)
|
||||
#unix_socket_group = '' # (change requires restart)
|
||||
#unix_socket_permissions = 0777 # begin with 0 to use octal notation
|
||||
# (change requires restart)
|
||||
|
Reference in New Issue
Block a user