diff options
Diffstat (limited to 'src/backend/utils/init/miscinit.c')
-rw-r--r-- | src/backend/utils/init/miscinit.c | 398 |
1 files changed, 212 insertions, 186 deletions
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); |