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