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