diff options
Diffstat (limited to 'src/backend/utils/init/miscinit.c')
-rw-r--r-- | src/backend/utils/init/miscinit.c | 1080 |
1 files changed, 0 insertions, 1080 deletions
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c deleted file mode 100644 index b32fdc80335..00000000000 --- a/src/backend/utils/init/miscinit.c +++ /dev/null @@ -1,1080 +0,0 @@ -/*------------------------------------------------------------------------- - * - * miscinit.c - * miscellaneous initialization support stuff - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.93 2002/06/20 20:29:40 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <sys/param.h> -#include <sys/types.h> -#include <signal.h> -#include <sys/stat.h> -#include <sys/file.h> -#include <fcntl.h> -#include <unistd.h> -#include <grp.h> -#include <pwd.h> -#include <stdlib.h> -#include <errno.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include "catalog/catname.h" -#include "catalog/pg_shadow.h" -#include "libpq/libpq-be.h" -#include "miscadmin.h" -#include "storage/ipc.h" -#include "storage/pg_shmem.h" -#include "utils/builtins.h" -#include "utils/guc.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" - - -ProcessingMode Mode = InitProcessing; - -/* Note: we rely on these to initialize as zeroes */ -static char directoryLockFile[MAXPGPATH]; -static char socketLockFile[MAXPGPATH]; - -#ifdef CYR_RECODE -static unsigned char RecodeForwTable[128]; -static unsigned char RecodeBackTable[128]; - -static void GetCharSetByHost(char *TableName, int host, const char *DataDir); -#endif - - -/* ---------------------------------------------------------------- - * ignoring system indexes support stuff - * ---------------------------------------------------------------- - */ - -static bool isIgnoringSystemIndexes = false; - -/* - * IsIgnoringSystemIndexes - * True if ignoring system indexes. - */ -bool -IsIgnoringSystemIndexes() -{ - return isIgnoringSystemIndexes; -} - -/* - * IgnoreSystemIndexes - * Set true or false whether PostgreSQL ignores system indexes. - * - */ -void -IgnoreSystemIndexes(bool mode) -{ - isIgnoringSystemIndexes = mode; -} - -/* ---------------------------------------------------------------- - * database path / name support stuff - * ---------------------------------------------------------------- - */ - -void -SetDatabasePath(const char *path) -{ - if (DatabasePath) - { - free(DatabasePath); - DatabasePath = NULL; - } - /* use strdup since this is done before memory contexts are set up */ - if (path) - { - DatabasePath = strdup(path); - AssertState(DatabasePath); - } -} - -void -SetDatabaseName(const char *name) -{ - if (DatabaseName) - { - free(DatabaseName); - DatabaseName = NULL; - } - /* use strdup since this is done before memory contexts are set up */ - if (name) - { - DatabaseName = strdup(name); - AssertState(DatabaseName); - } -} - -/* - * Set data directory, but make sure it's an absolute path. Use this, - * never set DataDir directly. - */ -void -SetDataDir(const char *dir) -{ - char *new; - int newlen; - - AssertArg(dir); - - /* If presented path is relative, convert to absolute */ - 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); - if (!new) - elog(FATAL, "out of memory"); - sprintf(new, "%s/%s", buf, dir); - free(buf); - } - else - { - new = strdup(dir); - if (!new) - elog(FATAL, "out of memory"); - } - - /* - * Strip any trailing slash. Not strictly necessary, but avoids - * generating funny-looking paths to individual files. - */ - newlen = strlen(new); - if (newlen > 1 && new[newlen-1] == '/') - new[newlen-1] = '\0'; - - if (DataDir) - free(DataDir); - 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) -{ - return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII")); -} - -Datum -pg_client_encoding(PG_FUNCTION_ARGS) -{ - return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII")); -} - -Datum -PG_encoding_to_char(PG_FUNCTION_ARGS) -{ - return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII")); -} - -Datum -PG_char_to_encoding(PG_FUNCTION_ARGS) -{ - PG_RETURN_INT32(0); -} - -Datum -pg_convert(PG_FUNCTION_ARGS) -{ - elog(ERROR, "convert is not supported. To use convert, you need to enable multibyte capability"); - return DirectFunctionCall1(textin, CStringGetDatum("")); -} - -Datum -pg_convert2(PG_FUNCTION_ARGS) -{ - elog(ERROR, "convert is not supported. To use convert, you need to enable multibyte capability"); - return DirectFunctionCall1(textin, CStringGetDatum("")); -} -#endif - -/* ---------------------------------------------------------------- - * CYR_RECODE support - * ---------------------------------------------------------------- - */ - -#ifdef CYR_RECODE - -void -SetCharSet(void) -{ - FILE *file; - char *filename; - char *map_file; - char buf[MAX_TOKEN]; - int i; - unsigned char FromChar, - ToChar; - char ChTable[MAX_TOKEN]; - - for (i = 0; i < 128; i++) - { - RecodeForwTable[i] = i + 128; - RecodeBackTable[i] = i + 128; - } - - if (IsUnderPostmaster) - { - GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir); - filename = ChTable; - } - else - filename = getenv("PG_RECODETABLE"); - - if (filename && *filename != '\0') - { - map_file = palloc(strlen(DataDir) + strlen(filename) + 2); - sprintf(map_file, "%s/%s", DataDir, filename); - file = AllocateFile(map_file, "r"); - pfree(map_file); - if (file == NULL) - return; - - while (!feof(file)) - { - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - FromChar = strtoul(buf, 0, 0); - /* Read the ToChar */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - ToChar = strtoul(buf, 0, 0); - RecodeForwTable[FromChar - 128] = ToChar; - RecodeBackTable[ToChar - 128] = FromChar; - - /* read to EOL */ - while (!feof(file) && buf[0]) - { - next_token(file, buf, sizeof(buf)); - elog(LOG, "SetCharSet: unknown tag %s in file %s", - buf, filename); - } - } - } - } - FreeFile(file); - } -} - - -char * -convertstr(unsigned char *buff, int len, int dest) -{ - int i; - char *ch = buff; - - for (i = 0; i < len; i++, buff++) - { - if (*buff > 127) - { - if (dest) - *buff = RecodeForwTable[*buff - 128]; - else - *buff = RecodeBackTable[*buff - 128]; - } - } - return ch; -} - -#define CHARSET_FILE "charset.conf" -#define MAX_CHARSETS 10 -#define KEY_HOST 1 -#define KEY_BASE 2 -#define KEY_TABLE 3 - -struct CharsetItem -{ - char Orig[MAX_TOKEN]; - char Dest[MAX_TOKEN]; - char Table[MAX_TOKEN]; -}; - - -static bool -CharSetInRange(char *buf, int host) -{ - int valid, - i, - FromAddr, - ToAddr, - tmp; - struct in_addr file_ip_addr; - char *p; - unsigned int one = 0x80000000, - NetMask = 0; - unsigned char mask; - - p = strchr(buf, '/'); - if (p) - { - *p++ = '\0'; - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - mask = strtoul(p, 0, 0); - FromAddr = ntohl(file_ip_addr.s_addr); - ToAddr = ntohl(file_ip_addr.s_addr); - for (i = 0; i < mask; i++) - { - NetMask |= one; - one >>= 1; - } - FromAddr &= NetMask; - ToAddr = ToAddr | ~NetMask; - tmp = ntohl(host); - return ((unsigned) tmp >= (unsigned) FromAddr && - (unsigned) tmp <= (unsigned) ToAddr); - } - } - else - { - p = strchr(buf, '-'); - if (p) - { - *p++ = '\0'; - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - FromAddr = ntohl(file_ip_addr.s_addr); - valid = inet_aton(p, &file_ip_addr); - if (valid) - { - ToAddr = ntohl(file_ip_addr.s_addr); - tmp = ntohl(host); - return ((unsigned) tmp >= (unsigned) FromAddr && - (unsigned) tmp <= (unsigned) ToAddr); - } - } - } - else - { - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - FromAddr = file_ip_addr.s_addr; - return (unsigned) FromAddr == (unsigned) host; - } - } - } - return false; -} - - -static void -GetCharSetByHost(char *TableName, int host, const char *DataDir) -{ - FILE *file; - char buf[MAX_TOKEN], - BaseCharset[MAX_TOKEN], - OrigCharset[MAX_TOKEN], - DestCharset[MAX_TOKEN], - HostCharset[MAX_TOKEN], - *map_file; - int key, - ChIndex = 0, - i, - bufsize; - struct CharsetItem *ChArray[MAX_CHARSETS]; - - *TableName = '\0'; - bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char); - map_file = (char *) palloc(bufsize); - snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE); - file = AllocateFile(map_file, "r"); - pfree(map_file); - if (file == NULL) - { - /* XXX should we log a complaint? */ - return; - } - - while (!feof(file)) - { - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - key = 0; - if (strcasecmp(buf, "HostCharset") == 0) - key = KEY_HOST; - else if (strcasecmp(buf, "BaseCharset") == 0) - key = KEY_BASE; - else if (strcasecmp(buf, "RecodeTable") == 0) - key = KEY_TABLE; - else - elog(LOG, "GetCharSetByHost: unknown tag %s in file %s", - buf, CHARSET_FILE); - - switch (key) - { - case KEY_HOST: - /* Read the host */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - if (CharSetInRange(buf, host)) - { - /* Read the charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - strcpy(HostCharset, buf); - } - } - break; - case KEY_BASE: - /* Read the base charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - strcpy(BaseCharset, buf); - break; - case KEY_TABLE: - /* Read the original charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(OrigCharset, buf); - /* Read the destination charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(DestCharset, buf); - /* Read the table filename */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - ChArray[ChIndex] = - (struct CharsetItem *) palloc(sizeof(struct CharsetItem)); - strcpy(ChArray[ChIndex]->Orig, OrigCharset); - strcpy(ChArray[ChIndex]->Dest, DestCharset); - strcpy(ChArray[ChIndex]->Table, buf); - ChIndex++; - } - } - } - break; - } - - /* read to EOL */ - while (!feof(file) && buf[0]) - { - next_token(file, buf, sizeof(buf)); - elog(LOG, "GetCharSetByHost: unknown tag %s in file %s", - buf, CHARSET_FILE); - } - } - } - FreeFile(file); - - for (i = 0; i < ChIndex; i++) - { - if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 && - strcasecmp(HostCharset, ChArray[i]->Dest) == 0) - strncpy(TableName, ChArray[i]->Table, 79); - pfree(ChArray[i]); - } -} - -#endif /* CYR_RECODE */ - - - -/* ---------------------------------------------------------------- - * User ID things - * - * The authenticated user is determined at connection start and never - * changes. The session user can be changed only by SET SESSION - * AUTHORIZATION. The current user may change when "setuid" functions - * are implemented. Conceptually there is a stack, whose bottom - * is the session user. You are yourself responsible to save and - * restore the current user id if you need to change it. - * ---------------------------------------------------------------- - */ -static Oid AuthenticatedUserId = InvalidOid; -static Oid SessionUserId = InvalidOid; -static Oid CurrentUserId = InvalidOid; - -static bool AuthenticatedUserIsSuperuser = false; - -/* - * This function is relevant for all privilege checks. - */ -Oid -GetUserId(void) -{ - AssertState(OidIsValid(CurrentUserId)); - return CurrentUserId; -} - - -void -SetUserId(Oid newid) -{ - AssertArg(OidIsValid(newid)); - CurrentUserId = newid; -} - - -/* - * This value is only relevant for informational purposes. - */ -Oid -GetSessionUserId(void) -{ - AssertState(OidIsValid(SessionUserId)); - return SessionUserId; -} - - -void -SetSessionUserId(Oid newid) -{ - AssertArg(OidIsValid(newid)); - SessionUserId = newid; - /* Current user defaults to session user. */ - if (!OidIsValid(CurrentUserId)) - CurrentUserId = newid; -} - - -void -InitializeSessionUserId(const char *username) -{ - HeapTuple userTup; - Datum datum; - bool isnull; - Oid usesysid; - - /* - * Don't do scans if we're bootstrapping, none of the system catalogs - * exist yet, and they should be owned by postgres anyway. - */ - AssertState(!IsBootstrapProcessingMode()); - - /* call only once */ - AssertState(!OidIsValid(AuthenticatedUserId)); - - userTup = SearchSysCache(SHADOWNAME, - PointerGetDatum(username), - 0, 0, 0); - if (!HeapTupleIsValid(userTup)) - elog(FATAL, "user \"%s\" does not exist", username); - - usesysid = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid; - - AuthenticatedUserId = usesysid; - AuthenticatedUserIsSuperuser = ((Form_pg_shadow) GETSTRUCT(userTup))->usesuper; - - SetSessionUserId(usesysid); /* sets CurrentUserId too */ - - /* Record username as a config option too */ - SetConfigOption("session_authorization", username, - PGC_BACKEND, PGC_S_OVERRIDE); - - /* - * Set up user-specific configuration variables. This is a good - * place to do it so we don't have to read pg_shadow twice during - * session startup. - */ - datum = SysCacheGetAttr(SHADOWNAME, userTup, - Anum_pg_shadow_useconfig, &isnull); - if (!isnull) - { - ArrayType *a = DatumGetArrayTypeP(datum); - - ProcessGUCArray(a, PGC_S_USER); - } - - ReleaseSysCache(userTup); -} - - -void -InitializeSessionUserIdStandalone(void) -{ - /* This function should only be called in a single-user backend. */ - AssertState(!IsUnderPostmaster); - - /* call only once */ - AssertState(!OidIsValid(AuthenticatedUserId)); - - AuthenticatedUserId = BOOTSTRAP_USESYSID; - AuthenticatedUserIsSuperuser = true; - - SetSessionUserId(BOOTSTRAP_USESYSID); -} - - -/* - * Change session auth ID while running - * - * Only a superuser may set auth ID to something other than himself. - */ -void -SetSessionAuthorization(Oid userid) -{ - /* Must have authenticated already, else can't make permission check */ - AssertState(OidIsValid(AuthenticatedUserId)); - - if (userid != AuthenticatedUserId && - !AuthenticatedUserIsSuperuser) - elog(ERROR, "permission denied"); - - SetSessionUserId(userid); - SetUserId(userid); -} - - -/* - * Get user name from user id - */ -char * -GetUserNameFromId(Oid userid) -{ - HeapTuple tuple; - char *result; - - tuple = SearchSysCache(SHADOWSYSID, - ObjectIdGetDatum(userid), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "invalid user id %u", (unsigned) userid); - - result = pstrdup(NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename)); - - ReleaseSysCache(tuple); - return result; -} - - - -/*------------------------------------------------------------------------- - * 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). - * Both kinds of files contain the same info: - * - * Owning process' PID - * Data directory path - * - * 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). - * - * A data-directory lockfile can optionally contain a third line, containing - * the key and ID for the shared memory block used by this postmaster. - * - * On successful lockfile creation, a proc_exit callback to remove the - * lockfile is automatically created. - *------------------------------------------------------------------------- - */ - -/* - * proc_exit callback to remove a lockfile. - */ -static void -UnlinkLockFile(int status, Datum filename) -{ - unlink((char *) DatumGetPointer(filename)); - /* Should we complain if the unlink fails? */ -} - -/* - * Create a lockfile, if possible - * - * Call CreateLockFile with the name of the lockfile to be created. - * Returns true if successful, false if not (with a message on stderr). - * - * amPostmaster is used to determine how to encode the output PID. - * isDDLock and refName are used to determine what error message to produce. - */ -static bool -CreateLockFile(const char *filename, bool amPostmaster, - bool isDDLock, const char *refName) -{ - int fd; - char buffer[MAXPGPATH + 100]; - int ntries; - int len; - int encoded_pid; - pid_t other_pid; - pid_t my_pid = getpid(); - - /* - * We need a loop here because of race conditions. But don't loop - * forever (for example, a non-writable $PGDATA directory might cause - * a failure that won't go away). 100 tries seems like plenty. - */ - for (ntries = 0;; ntries++) - { - /* - * 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) || ntries > 100) - elog(FATAL, "Can't create lock file %s: %m", filename); - - /* - * 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(filename, O_RDONLY, 0600); - if (fd < 0) - { - 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); - - buffer[len] = '\0'; - encoded_pid = atoi(buffer); - - /* if pid < 0, the pid is for postgres, not postmaster */ - other_pid = (pid_t) (encoded_pid < 0 ? -encoded_pid : encoded_pid); - - if (other_pid <= 0) - elog(FATAL, "Bogus data in lock file %s", filename); - - /* - * Check to see if the other process still exists - * - * Normally kill() will fail with ESRCH if the given PID doesn't - * exist. BeOS returns EINVAL for some silly reason, however. - */ - if (other_pid != my_pid) - { - if (kill(other_pid, 0) == 0 || - (errno != ESRCH -#ifdef __BEOS__ - && errno != EINVAL -#endif - )) - { - /* lockfile belongs to a live process */ - fprintf(stderr, "Lock file \"%s\" already exists.\n", - filename); - if (isDDLock) - fprintf(stderr, - "Is another %s (pid %d) running in \"%s\"?\n", - (encoded_pid < 0 ? "postgres" : "postmaster"), - (int) other_pid, refName); - else - fprintf(stderr, - "Is another %s (pid %d) using \"%s\"?\n", - (encoded_pid < 0 ? "postgres" : "postmaster"), - (int) other_pid, refName); - return false; - } - } - - /* - * No, the creating process did not exist. However, it could be - * that the postmaster crashed (or more likely was kill -9'd by a - * clueless admin) but has left orphan backends behind. Check for - * this by looking to see if there is an associated shmem segment - * that is still in use. - */ - if (isDDLock) - { - char *ptr; - unsigned long id1, - id2; - - ptr = strchr(buffer, '\n'); - if (ptr != NULL && - (ptr = strchr(ptr + 1, '\n')) != NULL) - { - ptr++; - if (sscanf(ptr, "%lu %lu", &id1, &id2) == 2) - { - if (PGSharedMemoryIsInUse(id1, id2)) - { - fprintf(stderr, - "Found a pre-existing shared memory block (key %lu, id %lu) still in use.\n" - "If you're sure there are no old backends still running,\n" - "remove the shared memory block with ipcrm(1), or just\n" - "delete \"%s\".\n", - id1, id2, filename); - return false; - } - } - } - } - - /* - * Looks like nobody's home. 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); - } - - /* - * Successfully created the file, now fill it. - */ - snprintf(buffer, sizeof(buffer), "%d\n%s\n", - amPostmaster ? (int) my_pid : -((int) my_pid), - DataDir); - errno = 0; - if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) - { - int save_errno = errno; - - close(fd); - unlink(filename); - /* if write didn't set errno, assume problem is no disk space */ - errno = save_errno ? save_errno : ENOSPC; - elog(FATAL, "Can't write lock file %s: %m", filename); - } - close(fd); - - /* - * Arrange for automatic removal of lockfile at proc_exit. - */ - on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename))); - - return true; /* Success! */ -} - -bool -CreateDataDirLockFile(const char *datadir, bool amPostmaster) -{ - char lockfile[MAXPGPATH]; - - snprintf(lockfile, sizeof(lockfile), "%s/postmaster.pid", datadir); - if (!CreateLockFile(lockfile, amPostmaster, true, datadir)) - return false; - /* Save name of lockfile for RecordSharedMemoryInLockFile */ - strcpy(directoryLockFile, lockfile); - return true; -} - -bool -CreateSocketLockFile(const char *socketfile, bool amPostmaster) -{ - char lockfile[MAXPGPATH]; - - snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile); - if (!CreateLockFile(lockfile, amPostmaster, false, socketfile)) - return false; - /* Save name of lockfile for TouchSocketLockFile */ - strcpy(socketLockFile, lockfile); - return true; -} - -/* - * Re-read the socket lock file. This should be called every so often - * to ensure that the lock file has a recent access date. That saves it - * from being removed by overenthusiastic /tmp-directory-cleaner daemons. - * (Another reason we should never have put the socket file in /tmp...) - */ -void -TouchSocketLockFile(void) -{ - int fd; - char buffer[1]; - - /* Do nothing if we did not create a socket... */ - if (socketLockFile[0] != '\0') - { - /* XXX any need to complain about errors here? */ - fd = open(socketLockFile, O_RDONLY | PG_BINARY, 0); - if (fd >= 0) - { - read(fd, buffer, sizeof(buffer)); - close(fd); - } - } -} - -/* - * Append information about a shared memory segment to the data directory - * lock file (if we have created one). - * - * This may be called multiple times in the life of a postmaster, if we - * delete and recreate shmem due to backend crash. Therefore, be prepared - * to overwrite existing information. (As of 7.1, a postmaster only creates - * one shm seg at a time; but for the purposes here, if we did have more than - * one then any one of them would do anyway.) - */ -void -RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2) -{ - int fd; - int len; - char *ptr; - char buffer[BLCKSZ]; - - /* - * Do nothing if we did not create a lockfile (probably because we are - * running standalone). - */ - if (directoryLockFile[0] == '\0') - return; - - fd = open(directoryLockFile, O_RDWR | PG_BINARY, 0); - if (fd < 0) - { - elog(LOG, "Failed to rewrite %s: %m", directoryLockFile); - return; - } - len = read(fd, buffer, sizeof(buffer) - 100); - if (len <= 0) - { - elog(LOG, "Failed to read %s: %m", directoryLockFile); - close(fd); - return; - } - buffer[len] = '\0'; - - /* - * Skip over first two lines (PID and path). - */ - ptr = strchr(buffer, '\n'); - if (ptr == NULL || - (ptr = strchr(ptr + 1, '\n')) == NULL) - { - elog(LOG, "Bogus data in %s", directoryLockFile); - close(fd); - return; - } - ptr++; - - /* - * Append key information. Format to try to keep it the same length - * always (trailing junk won't hurt, but might confuse humans). - */ - sprintf(ptr, "%9lu %9lu\n", id1, id2); - - /* - * And rewrite the data. Since we write in a single kernel call, this - * update should appear atomic to onlookers. - */ - len = strlen(buffer); - errno = 0; - if (lseek(fd, (off_t) 0, SEEK_SET) != 0 || - (int) write(fd, buffer, len) != len) - { - /* if write didn't set errno, assume problem is no disk space */ - if (errno == 0) - errno = ENOSPC; - elog(LOG, "Failed to write %s: %m", directoryLockFile); - close(fd); - return; - } - close(fd); -} - - -/*------------------------------------------------------------------------- - * Version checking support - *------------------------------------------------------------------------- - */ - -/* - * Determine whether the PG_VERSION file in directory `path' indicates - * a data version compatible with the version of this program. - * - * If compatible, return. Otherwise, elog(FATAL). - */ -void -ValidatePgVersion(const char *path) -{ - char full_path[MAXPGPATH]; - FILE *file; - int ret; - long file_major, - file_minor; - long my_major = 0, - my_minor = 0; - char *endptr; - const char *version_string = PG_VERSION; - - my_major = strtol(version_string, &endptr, 10); - if (*endptr == '.') - my_minor = strtol(endptr + 1, NULL, 10); - - snprintf(full_path, MAXPGPATH, "%s/PG_VERSION", path); - - file = AllocateFile(full_path, "r"); - if (!file) - { - if (errno == ENOENT) - elog(FATAL, "File %s is missing. This is not a valid data directory.", full_path); - else - elog(FATAL, "cannot open %s: %m", full_path); - } - - ret = fscanf(file, "%ld.%ld", &file_major, &file_minor); - if (ret != 2) - elog(FATAL, "File %s does not contain valid data. You need to initdb.", full_path); - - FreeFile(file); - - if (my_major != file_major || my_minor != file_minor) - elog(FATAL, "The data directory was initialized by PostgreSQL version %ld.%ld, " - "which is not compatible with this version %s.", - file_major, file_minor, version_string); -} |