]> granicus.if.org Git - postgresql/blob - src/backend/utils/init/miscinit.c
User and database-specific session defaults for run-time configuration
[postgresql] / src / backend / utils / init / miscinit.c
1 /*-------------------------------------------------------------------------
2  *
3  * miscinit.c
4  *        miscellaneous initialization support stuff
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.83 2002/03/01 22:45:15 petere Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <signal.h>
20 #include <sys/stat.h>
21 #include <sys/file.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <grp.h>
25 #include <pwd.h>
26 #include <stdlib.h>
27 #include <errno.h>
28
29 #include "catalog/catname.h"
30 #include "catalog/pg_shadow.h"
31 #include "libpq/libpq-be.h"
32 #include "miscadmin.h"
33 #include "utils/builtins.h"
34 #include "utils/guc.h"
35 #include "utils/lsyscache.h"
36 #include "utils/syscache.h"
37
38
39 #ifdef CYR_RECODE
40 unsigned char RecodeForwTable[128];
41 unsigned char RecodeBackTable[128];
42 #endif
43
44 ProcessingMode Mode = InitProcessing;
45
46 /* Note: we rely on these to initialize as zeroes */
47 static char directoryLockFile[MAXPGPATH];
48 static char socketLockFile[MAXPGPATH];
49
50
51 /* ----------------------------------------------------------------
52  *              ignoring system indexes support stuff
53  * ----------------------------------------------------------------
54  */
55
56 static bool isIgnoringSystemIndexes = false;
57
58 /*
59  * IsIgnoringSystemIndexes
60  *              True if ignoring system indexes.
61  */
62 bool
63 IsIgnoringSystemIndexes()
64 {
65         return isIgnoringSystemIndexes;
66 }
67
68 /*
69  * IgnoreSystemIndexes
70  *      Set true or false whether PostgreSQL ignores system indexes.
71  *
72  */
73 void
74 IgnoreSystemIndexes(bool mode)
75 {
76         isIgnoringSystemIndexes = mode;
77 }
78
79 /* ----------------------------------------------------------------
80  *                              database path / name support stuff
81  * ----------------------------------------------------------------
82  */
83
84 void
85 SetDatabasePath(const char *path)
86 {
87         if (DatabasePath)
88         {
89                 free(DatabasePath);
90                 DatabasePath = NULL;
91         }
92         /* use strdup since this is done before memory contexts are set up */
93         if (path)
94         {
95                 DatabasePath = strdup(path);
96                 AssertState(DatabasePath);
97         }
98 }
99
100 void
101 SetDatabaseName(const char *name)
102 {
103         if (DatabaseName)
104         {
105                 free(DatabaseName);
106                 DatabaseName = NULL;
107         }
108         /* use strdup since this is done before memory contexts are set up */
109         if (name)
110         {
111                 DatabaseName = strdup(name);
112                 AssertState(DatabaseName);
113         }
114 }
115
116 /*
117  * Set data directory, but make sure it's an absolute path.  Use this,
118  * never set DataDir directly.
119  */
120 void
121 SetDataDir(const char *dir)
122 {
123         char       *new;
124
125         AssertArg(dir);
126
127         if (dir[0] != '/')
128         {
129                 char       *buf;
130                 size_t          buflen;
131
132                 buflen = MAXPGPATH;
133                 for (;;)
134                 {
135                         buf = malloc(buflen);
136                         if (!buf)
137                                 elog(FATAL, "out of memory");
138
139                         if (getcwd(buf, buflen))
140                                 break;
141                         else if (errno == ERANGE)
142                         {
143                                 free(buf);
144                                 buflen *= 2;
145                                 continue;
146                         }
147                         else
148                         {
149                                 free(buf);
150                                 elog(FATAL, "cannot get current working directory: %m");
151                         }
152                 }
153
154                 new = malloc(strlen(buf) + 1 + strlen(dir) + 1);
155                 if (!new)
156                         elog(FATAL, "out of memory");
157                 sprintf(new, "%s/%s", buf, dir);
158                 free(buf);
159         }
160         else
161         {
162                 new = strdup(dir);
163                 if (!new)
164                         elog(FATAL, "out of memory");
165         }
166
167         if (DataDir)
168                 free(DataDir);
169         DataDir = new;
170 }
171
172
173 /* ----------------------------------------------------------------
174  *                              MULTIBYTE stub code
175  *
176  * Even if MULTIBYTE is not enabled, these functions are necessary
177  * since pg_proc.h has references to them.
178  * ----------------------------------------------------------------
179  */
180
181 #ifndef MULTIBYTE
182
183 Datum
184 getdatabaseencoding(PG_FUNCTION_ARGS)
185 {
186         return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII"));
187 }
188
189 Datum
190 pg_client_encoding(PG_FUNCTION_ARGS)
191 {
192         return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII"));
193 }
194
195 Datum
196 PG_encoding_to_char(PG_FUNCTION_ARGS)
197 {
198         return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII"));
199 }
200
201 Datum
202 PG_char_to_encoding(PG_FUNCTION_ARGS)
203 {
204         PG_RETURN_INT32(0);
205 }
206
207 Datum
208 pg_convert(PG_FUNCTION_ARGS)
209 {
210         elog(ERROR, "convert is not supported. To use convert, you need to enable multibyte capability");
211         return DirectFunctionCall1(textin, CStringGetDatum(""));
212 }
213
214 Datum
215 pg_convert2(PG_FUNCTION_ARGS)
216 {
217         elog(ERROR, "convert is not supported. To use convert, you need to enable multibyte capability");
218         return DirectFunctionCall1(textin, CStringGetDatum(""));
219 }
220 #endif
221
222 /* ----------------------------------------------------------------
223  *                              CYR_RECODE support
224  * ----------------------------------------------------------------
225  */
226
227 #ifdef CYR_RECODE
228
229 #define MAX_TOKEN       80
230
231 /*
232  * Some standard C libraries, including GNU, have an isblank() function.
233  * Others, including Solaris, do not.  So we have our own.
234  */
235 static bool
236 isblank(const char c)
237 {
238         return c == ' ' || c == '\t';
239 }
240
241
242 /*
243  *      Grab one token out of fp.  Tokens are strings of non-blank
244  *      characters bounded by blank characters, beginning of line, and end
245  *      of line.        Blank means space or tab.  Return the token as *buf.
246  *      Leave file positioned to character immediately after the token or
247  *      EOF, whichever comes first.  If no more tokens on line, return null
248  *      string as *buf and position file to beginning of next line or EOF,
249  *      whichever comes first.
250  */
251 static void
252 next_token(FILE *fp, char *buf, const int bufsz)
253 {
254         int                     c;
255         char       *eb = buf + (bufsz - 1);
256
257         /* Move over initial token-delimiting blanks */
258         while ((c = getc(fp)) != EOF && isblank(c))
259                 ;
260
261         if (c != EOF && c != '\n')
262         {
263                 /*
264                  * build a token in buf of next characters up to EOF, eol, or
265                  * blank.  If the token gets too long, we still parse it
266                  * correctly, but the excess characters are not stored into *buf.
267                  */
268                 while (c != EOF && c != '\n' && !isblank(c))
269                 {
270                         if (buf < eb)
271                                 *buf++ = c;
272                         c = getc(fp);
273                 }
274
275                 /*
276                  * Put back the char right after the token (critical in case it is
277                  * eol, since we need to detect end-of-line at next call).
278                  */
279                 if (c != EOF)
280                         ungetc(c, fp);
281         }
282         *buf = '\0';
283 }
284
285
286 static void
287 read_through_eol(FILE *file)
288 {
289         int                     c;
290
291         while ((c = getc(file)) != EOF && c != '\n')
292                 ;
293 }
294
295
296 void
297 SetCharSet(void)
298 {
299         FILE       *file;
300         char       *p;
301         char       *map_file;
302         char            buf[MAX_TOKEN];
303         int                     i,
304                                 c;
305         unsigned char FromChar,
306                                 ToChar;
307         char            ChTable[80];
308
309         for (i = 0; i < 128; i++)
310         {
311                 RecodeForwTable[i] = i + 128;
312                 RecodeBackTable[i] = i + 128;
313         }
314
315         if (IsUnderPostmaster)
316         {
317                 GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
318                 p = ChTable;
319         }
320         else
321                 p = getenv("PG_RECODETABLE");
322
323         if (p && *p != '\0')
324         {
325                 map_file = palloc(strlen(DataDir) + strlen(p) + 2);
326                 sprintf(map_file, "%s/%s", DataDir, p);
327                 file = AllocateFile(map_file, PG_BINARY_R);
328                 pfree(map_file);
329                 if (file == NULL)
330                         return;
331                 while ((c = getc(file)) != EOF)
332                 {
333                         if (c == '#')
334                                 read_through_eol(file);
335                         else
336                         {
337                                 /* Read the FromChar */
338                                 ungetc(c, file);
339                                 next_token(file, buf, sizeof(buf));
340                                 if (buf[0] != '\0')
341                                 {
342                                         FromChar = strtoul(buf, 0, 0);
343                                         /* Read the ToChar */
344                                         next_token(file, buf, sizeof(buf));
345                                         if (buf[0] != '\0')
346                                         {
347                                                 ToChar = strtoul(buf, 0, 0);
348                                                 RecodeForwTable[FromChar - 128] = ToChar;
349                                                 RecodeBackTable[ToChar - 128] = FromChar;
350                                                 read_through_eol(file);
351                                         }
352                                 }
353                         }
354                 }
355                 FreeFile(file);
356         }
357 }
358
359 char *
360 convertstr(unsigned char *buff, int len, int dest)
361 {
362         int                     i;
363         char       *ch = buff;
364
365         for (i = 0; i < len; i++, buff++)
366         {
367                 if (*buff > 127)
368                 {
369                         if (dest)
370                                 *buff = RecodeForwTable[*buff - 128];
371                         else
372                                 *buff = RecodeBackTable[*buff - 128];
373                 }
374         }
375         return ch;
376 }
377 #endif
378
379
380
381 /* ----------------------------------------------------------------
382  *      User ID things
383  *
384  * The session user is determined at connection start and never
385  * changes.  The current user may change when "setuid" functions
386  * are implemented.  Conceptually there is a stack, whose bottom
387  * is the session user.  You are yourself responsible to save and
388  * restore the current user id if you need to change it.
389  * ----------------------------------------------------------------
390  */
391 static Oid      CurrentUserId = InvalidOid;
392 static Oid      SessionUserId = InvalidOid;
393
394 static bool AuthenticatedUserIsSuperuser = false;
395
396 /*
397  * This function is relevant for all privilege checks.
398  */
399 Oid
400 GetUserId(void)
401 {
402         AssertState(OidIsValid(CurrentUserId));
403         return CurrentUserId;
404 }
405
406
407 void
408 SetUserId(Oid newid)
409 {
410         AssertArg(OidIsValid(newid));
411         CurrentUserId = newid;
412 }
413
414
415 /*
416  * This value is only relevant for informational purposes.
417  */
418 Oid
419 GetSessionUserId(void)
420 {
421         AssertState(OidIsValid(SessionUserId));
422         return SessionUserId;
423 }
424
425
426 void
427 SetSessionUserId(Oid newid)
428 {
429         AssertArg(OidIsValid(newid));
430         SessionUserId = newid;
431         /* Current user defaults to session user. */
432         if (!OidIsValid(CurrentUserId))
433                 CurrentUserId = newid;
434 }
435
436
437 void
438 InitializeSessionUserId(const char *username)
439 {
440         HeapTuple       userTup;
441         Datum           datum;
442         bool            isnull;
443
444         /*
445          * Don't do scans if we're bootstrapping, none of the system catalogs
446          * exist yet, and they should be owned by postgres anyway.
447          */
448         AssertState(!IsBootstrapProcessingMode());
449
450         /* call only once */
451         AssertState(!OidIsValid(SessionUserId));
452
453         userTup = SearchSysCache(SHADOWNAME,
454                                                          PointerGetDatum(username),
455                                                          0, 0, 0);
456         if (!HeapTupleIsValid(userTup))
457                 elog(FATAL, "user \"%s\" does not exist", username);
458
459         SetSessionUserId(((Form_pg_shadow) GETSTRUCT(userTup))->usesysid);
460
461         AuthenticatedUserIsSuperuser = ((Form_pg_shadow) GETSTRUCT(userTup))->usesuper;
462
463         /*
464          * Set up user-specific configuration variables.  This is a good
465          * place to do it so we don't have to read pg_shadow twice during
466          * session startup.
467          */
468         datum = SysCacheGetAttr(SHADOWNAME, userTup,
469                                                         Anum_pg_shadow_useconfig, &isnull);
470         if (!isnull)
471         {
472                 ArrayType *a;
473
474                 a = (ArrayType *) pg_detoast_datum((struct varlena *)datum);
475                 ProcessGUCArray(a, PGC_S_USER);
476         }
477
478         ReleaseSysCache(userTup);
479 }
480
481
482 void
483 InitializeSessionUserIdStandalone(void)
484 {
485         /* This function should only be called in a single-user backend. */
486         AssertState(!IsUnderPostmaster);
487
488         /* call only once */
489         AssertState(!OidIsValid(SessionUserId));
490
491         SetSessionUserId(BOOTSTRAP_USESYSID);
492         AuthenticatedUserIsSuperuser = true;
493 }
494
495
496 /*
497  * Change session auth ID while running
498  */
499 void
500 SetSessionAuthorization(const char *username)
501 {
502         int32           userid;
503
504         if (!AuthenticatedUserIsSuperuser)
505                 elog(ERROR, "permission denied");
506
507         userid = get_usesysid(username);
508
509         SetSessionUserId(userid);
510         SetUserId(userid);
511 }
512
513
514 /*
515  * Get user name from user id
516  */
517 char *
518 GetUserName(Oid userid)
519 {
520         HeapTuple       tuple;
521         char       *result;
522
523         tuple = SearchSysCache(SHADOWSYSID,
524                                                    ObjectIdGetDatum(userid),
525                                                    0, 0, 0);
526         if (!HeapTupleIsValid(tuple))
527                 elog(ERROR, "invalid user id %u", (unsigned) userid);
528
529         result = pstrdup(NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename));
530
531         ReleaseSysCache(tuple);
532         return result;
533 }
534
535
536
537 /*-------------------------------------------------------------------------
538  *                              Interlock-file support
539  *
540  * These routines are used to create both a data-directory lockfile
541  * ($DATADIR/postmaster.pid) and a Unix-socket-file lockfile ($SOCKFILE.lock).
542  * Both kinds of files contain the same info:
543  *
544  *              Owning process' PID
545  *              Data directory path
546  *
547  * By convention, the owning process' PID is negated if it is a standalone
548  * backend rather than a postmaster.  This is just for informational purposes.
549  * The path is also just for informational purposes (so that a socket lockfile
550  * can be more easily traced to the associated postmaster).
551  *
552  * A data-directory lockfile can optionally contain a third line, containing
553  * the key and ID for the shared memory block used by this postmaster.
554  *
555  * On successful lockfile creation, a proc_exit callback to remove the
556  * lockfile is automatically created.
557  *-------------------------------------------------------------------------
558  */
559
560 /*
561  * proc_exit callback to remove a lockfile.
562  */
563 static void
564 UnlinkLockFile(int status, Datum filename)
565 {
566         unlink((char *) DatumGetPointer(filename));
567         /* Should we complain if the unlink fails? */
568 }
569
570 /*
571  * Create a lockfile, if possible
572  *
573  * Call CreateLockFile with the name of the lockfile to be created.
574  * Returns true if successful, false if not (with a message on stderr).
575  *
576  * amPostmaster is used to determine how to encode the output PID.
577  * isDDLock and refName are used to determine what error message to produce.
578  */
579 static bool
580 CreateLockFile(const char *filename, bool amPostmaster,
581                            bool isDDLock, const char *refName)
582 {
583         int                     fd;
584         char            buffer[MAXPGPATH + 100];
585         int                     ntries;
586         int                     len;
587         int                     encoded_pid;
588         pid_t           other_pid;
589         pid_t           my_pid = getpid();
590
591         /*
592          * We need a loop here because of race conditions.      But don't loop
593          * forever (for example, a non-writable $PGDATA directory might cause
594          * a failure that won't go away).  100 tries seems like plenty.
595          */
596         for (ntries = 0;; ntries++)
597         {
598                 /*
599                  * Try to create the lock file --- O_EXCL makes this atomic.
600                  */
601                 fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
602                 if (fd >= 0)
603                         break;                          /* Success; exit the retry loop */
604
605                 /*
606                  * Couldn't create the pid file. Probably it already exists.
607                  */
608                 if ((errno != EEXIST && errno != EACCES) || ntries > 100)
609                         elog(FATAL, "Can't create lock file %s: %m", filename);
610
611                 /*
612                  * Read the file to get the old owner's PID.  Note race condition
613                  * here: file might have been deleted since we tried to create it.
614                  */
615                 fd = open(filename, O_RDONLY, 0600);
616                 if (fd < 0)
617                 {
618                         if (errno == ENOENT)
619                                 continue;               /* race condition; try again */
620                         elog(FATAL, "Can't read lock file %s: %m", filename);
621                 }
622                 if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0)
623                         elog(FATAL, "Can't read lock file %s: %m", filename);
624                 close(fd);
625
626                 buffer[len] = '\0';
627                 encoded_pid = atoi(buffer);
628
629                 /* if pid < 0, the pid is for postgres, not postmaster */
630                 other_pid = (pid_t) (encoded_pid < 0 ? -encoded_pid : encoded_pid);
631
632                 if (other_pid <= 0)
633                         elog(FATAL, "Bogus data in lock file %s", filename);
634
635                 /*
636                  * Check to see if the other process still exists
637                  *
638                  * Normally kill() will fail with ESRCH if the given PID doesn't
639                  * exist.  BeOS returns EINVAL for some silly reason, however.
640                  */
641                 if (other_pid != my_pid)
642                 {
643                         if (kill(other_pid, 0) == 0 ||
644                                 (errno != ESRCH
645 #ifdef __BEOS__
646                                  && errno != EINVAL
647 #endif
648                                  ))
649                         {
650                                 /* lockfile belongs to a live process */
651                                 fprintf(stderr, "Lock file \"%s\" already exists.\n",
652                                                 filename);
653                                 if (isDDLock)
654                                         fprintf(stderr,
655                                                         "Is another %s (pid %d) running in \"%s\"?\n",
656                                                         (encoded_pid < 0 ? "postgres" : "postmaster"),
657                                                         (int) other_pid, refName);
658                                 else
659                                         fprintf(stderr,
660                                                         "Is another %s (pid %d) using \"%s\"?\n",
661                                                         (encoded_pid < 0 ? "postgres" : "postmaster"),
662                                                         (int) other_pid, refName);
663                                 return false;
664                         }
665                 }
666
667                 /*
668                  * No, the creating process did not exist.      However, it could be
669                  * that the postmaster crashed (or more likely was kill -9'd by a
670                  * clueless admin) but has left orphan backends behind.  Check for
671                  * this by looking to see if there is an associated shmem segment
672                  * that is still in use.
673                  */
674                 if (isDDLock)
675                 {
676                         char       *ptr;
677                         unsigned long shmKey,
678                                                 shmId;
679
680                         ptr = strchr(buffer, '\n');
681                         if (ptr != NULL &&
682                                 (ptr = strchr(ptr + 1, '\n')) != NULL)
683                         {
684                                 ptr++;
685                                 if (sscanf(ptr, "%lu %lu", &shmKey, &shmId) == 2)
686                                 {
687                                         if (SharedMemoryIsInUse((IpcMemoryKey) shmKey,
688                                                                                         (IpcMemoryId) shmId))
689                                         {
690                                                 fprintf(stderr,
691                                                                 "Found a pre-existing shared memory block (ID %d) still in use.\n"
692                                                                 "If you're sure there are no old backends still running,\n"
693                                                                 "remove the shared memory block with ipcrm(1), or just\n"
694                                                                 "delete \"%s\".\n",
695                                                                 (int) shmId, filename);
696                                                 return false;
697                                         }
698                                 }
699                         }
700                 }
701
702                 /*
703                  * Looks like nobody's home.  Unlink the file and try again to
704                  * create it.  Need a loop because of possible race condition
705                  * against other would-be creators.
706                  */
707                 if (unlink(filename) < 0)
708                         elog(FATAL, "Can't remove old lock file %s: %m"
709                                  "\n\tThe file seems accidentally left, but I couldn't remove it."
710                                  "\n\tPlease remove the file by hand and try again.",
711                                  filename);
712         }
713
714         /*
715          * Successfully created the file, now fill it.
716          */
717         snprintf(buffer, sizeof(buffer), "%d\n%s\n",
718                          amPostmaster ? (int) my_pid : -((int) my_pid),
719                          DataDir);
720         errno = 0;
721         if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
722         {
723                 int                     save_errno = errno;
724
725                 close(fd);
726                 unlink(filename);
727                 /* if write didn't set errno, assume problem is no disk space */
728                 errno = save_errno ? save_errno : ENOSPC;
729                 elog(FATAL, "Can't write lock file %s: %m", filename);
730         }
731         close(fd);
732
733         /*
734          * Arrange for automatic removal of lockfile at proc_exit.
735          */
736         on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename)));
737
738         return true;                            /* Success! */
739 }
740
741 bool
742 CreateDataDirLockFile(const char *datadir, bool amPostmaster)
743 {
744         char            lockfile[MAXPGPATH];
745
746         snprintf(lockfile, sizeof(lockfile), "%s/postmaster.pid", datadir);
747         if (!CreateLockFile(lockfile, amPostmaster, true, datadir))
748                 return false;
749         /* Save name of lockfile for RecordSharedMemoryInLockFile */
750         strcpy(directoryLockFile, lockfile);
751         return true;
752 }
753
754 bool
755 CreateSocketLockFile(const char *socketfile, bool amPostmaster)
756 {
757         char            lockfile[MAXPGPATH];
758
759         snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile);
760         if (!CreateLockFile(lockfile, amPostmaster, false, socketfile))
761                 return false;
762         /* Save name of lockfile for TouchSocketLockFile */
763         strcpy(socketLockFile, lockfile);
764         return true;
765 }
766
767 /*
768  * Re-read the socket lock file.  This should be called every so often
769  * to ensure that the lock file has a recent access date.  That saves it
770  * from being removed by overenthusiastic /tmp-directory-cleaner daemons.
771  * (Another reason we should never have put the socket file in /tmp...)
772  */
773 void
774 TouchSocketLockFile(void)
775 {
776         int                     fd;
777         char            buffer[1];
778
779         /* Do nothing if we did not create a socket... */
780         if (socketLockFile[0] != '\0')
781         {
782                 /* XXX any need to complain about errors here? */
783                 fd = open(socketLockFile, O_RDONLY | PG_BINARY, 0);
784                 if (fd >= 0)
785                 {
786                         read(fd, buffer, sizeof(buffer));
787                         close(fd);
788                 }
789         }
790 }
791
792 /*
793  * Append information about a shared memory segment to the data directory
794  * lock file (if we have created one).
795  *
796  * This may be called multiple times in the life of a postmaster, if we
797  * delete and recreate shmem due to backend crash.      Therefore, be prepared
798  * to overwrite existing information.  (As of 7.1, a postmaster only creates
799  * one shm seg anyway; but for the purposes here, if we did have more than
800  * one then any one of them would do anyway.)
801  */
802 void
803 RecordSharedMemoryInLockFile(IpcMemoryKey shmKey, IpcMemoryId shmId)
804 {
805         int                     fd;
806         int                     len;
807         char       *ptr;
808         char            buffer[BLCKSZ];
809
810         /*
811          * Do nothing if we did not create a lockfile (probably because we are
812          * running standalone).
813          */
814         if (directoryLockFile[0] == '\0')
815                 return;
816
817         fd = open(directoryLockFile, O_RDWR | PG_BINARY, 0);
818         if (fd < 0)
819         {
820                 elog(DEBUG, "Failed to rewrite %s: %m", directoryLockFile);
821                 return;
822         }
823         len = read(fd, buffer, sizeof(buffer) - 100);
824         if (len <= 0)
825         {
826                 elog(DEBUG, "Failed to read %s: %m", directoryLockFile);
827                 close(fd);
828                 return;
829         }
830         buffer[len] = '\0';
831
832         /*
833          * Skip over first two lines (PID and path).
834          */
835         ptr = strchr(buffer, '\n');
836         if (ptr == NULL ||
837                 (ptr = strchr(ptr + 1, '\n')) == NULL)
838         {
839                 elog(DEBUG, "Bogus data in %s", directoryLockFile);
840                 close(fd);
841                 return;
842         }
843         ptr++;
844
845         /*
846          * Append shm key and ID.  Format to try to keep it the same length
847          * always (trailing junk won't hurt, but might confuse humans).
848          */
849         sprintf(ptr, "%9lu %9lu\n",
850                         (unsigned long) shmKey, (unsigned long) shmId);
851
852         /*
853          * And rewrite the data.  Since we write in a single kernel call, this
854          * update should appear atomic to onlookers.
855          */
856         len = strlen(buffer);
857         errno = 0;
858         if (lseek(fd, (off_t) 0, SEEK_SET) != 0 ||
859                 (int) write(fd, buffer, len) != len)
860         {
861                 /* if write didn't set errno, assume problem is no disk space */
862                 if (errno == 0)
863                         errno = ENOSPC;
864                 elog(DEBUG, "Failed to write %s: %m", directoryLockFile);
865                 close(fd);
866                 return;
867         }
868         close(fd);
869 }
870
871
872 /*-------------------------------------------------------------------------
873  *                              Version checking support
874  *-------------------------------------------------------------------------
875  */
876
877 /*
878  * Determine whether the PG_VERSION file in directory `path' indicates
879  * a data version compatible with the version of this program.
880  *
881  * If compatible, return. Otherwise, elog(FATAL).
882  */
883 void
884 ValidatePgVersion(const char *path)
885 {
886         char            full_path[MAXPGPATH];
887         FILE       *file;
888         int                     ret;
889         long            file_major,
890                                 file_minor;
891         long            my_major = 0,
892                                 my_minor = 0;
893         char       *endptr;
894         const char *version_string = PG_VERSION;
895
896         my_major = strtol(version_string, &endptr, 10);
897         if (*endptr == '.')
898                 my_minor = strtol(endptr + 1, NULL, 10);
899
900         snprintf(full_path, MAXPGPATH, "%s/PG_VERSION", path);
901
902         file = AllocateFile(full_path, "r");
903         if (!file)
904         {
905                 if (errno == ENOENT)
906                         elog(FATAL, "File %s is missing. This is not a valid data directory.", full_path);
907                 else
908                         elog(FATAL, "cannot open %s: %m", full_path);
909         }
910
911         ret = fscanf(file, "%ld.%ld", &file_major, &file_minor);
912         if (ret != 2)
913                 elog(FATAL, "File %s does not contain valid data. You need to initdb.", full_path);
914
915         FreeFile(file);
916
917         if (my_major != file_major || my_minor != file_minor)
918                 elog(FATAL, "The data directory was initialized by PostgreSQL version %ld.%ld, "
919                          "which is not compatible with this version %s.",
920                          file_major, file_minor, version_string);
921 }