]> granicus.if.org Git - postgresql/blob - src/backend/utils/init/miscinit.c
Support toasting of shared system relations, and provide toast tables for
[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.87 2002/04/27 21:24:34 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/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         int                     newlen;
125
126         AssertArg(dir);
127
128         /* If presented path is relative, convert to absolute */
129         if (dir[0] != '/')
130         {
131                 char       *buf;
132                 size_t          buflen;
133
134                 buflen = MAXPGPATH;
135                 for (;;)
136                 {
137                         buf = malloc(buflen);
138                         if (!buf)
139                                 elog(FATAL, "out of memory");
140
141                         if (getcwd(buf, buflen))
142                                 break;
143                         else if (errno == ERANGE)
144                         {
145                                 free(buf);
146                                 buflen *= 2;
147                                 continue;
148                         }
149                         else
150                         {
151                                 free(buf);
152                                 elog(FATAL, "cannot get current working directory: %m");
153                         }
154                 }
155
156                 new = malloc(strlen(buf) + 1 + strlen(dir) + 1);
157                 if (!new)
158                         elog(FATAL, "out of memory");
159                 sprintf(new, "%s/%s", buf, dir);
160                 free(buf);
161         }
162         else
163         {
164                 new = strdup(dir);
165                 if (!new)
166                         elog(FATAL, "out of memory");
167         }
168
169         /*
170          * Strip any trailing slash.  Not strictly necessary, but avoids
171          * generating funny-looking paths to individual files.
172          */
173         newlen = strlen(new);
174         if (newlen > 1 && new[newlen-1] == '/')
175                 new[newlen-1] = '\0';
176
177         if (DataDir)
178                 free(DataDir);
179         DataDir = new;
180 }
181
182
183 /* ----------------------------------------------------------------
184  *                              MULTIBYTE stub code
185  *
186  * Even if MULTIBYTE is not enabled, these functions are necessary
187  * since pg_proc.h has references to them.
188  * ----------------------------------------------------------------
189  */
190
191 #ifndef MULTIBYTE
192
193 Datum
194 getdatabaseencoding(PG_FUNCTION_ARGS)
195 {
196         return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII"));
197 }
198
199 Datum
200 pg_client_encoding(PG_FUNCTION_ARGS)
201 {
202         return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII"));
203 }
204
205 Datum
206 PG_encoding_to_char(PG_FUNCTION_ARGS)
207 {
208         return DirectFunctionCall1(namein, CStringGetDatum("SQL_ASCII"));
209 }
210
211 Datum
212 PG_char_to_encoding(PG_FUNCTION_ARGS)
213 {
214         PG_RETURN_INT32(0);
215 }
216
217 Datum
218 pg_convert(PG_FUNCTION_ARGS)
219 {
220         elog(ERROR, "convert is not supported. To use convert, you need to enable multibyte capability");
221         return DirectFunctionCall1(textin, CStringGetDatum(""));
222 }
223
224 Datum
225 pg_convert2(PG_FUNCTION_ARGS)
226 {
227         elog(ERROR, "convert is not supported. To use convert, you need to enable multibyte capability");
228         return DirectFunctionCall1(textin, CStringGetDatum(""));
229 }
230 #endif
231
232 /* ----------------------------------------------------------------
233  *                              CYR_RECODE support
234  * ----------------------------------------------------------------
235  */
236
237 #ifdef CYR_RECODE
238
239 SetCharSet(void)
240 {
241         FILE       *file;
242         char       *filename;
243         char       *map_file;
244         char            buf[MAX_TOKEN];
245         int                     i,
246                                 c;
247         unsigned char FromChar,
248                                 ToChar;
249         char            ChTable[MAX_TOKEN];
250
251         for (i = 0; i < 128; i++)
252         {
253                 RecodeForwTable[i] = i + 128;
254                 RecodeBackTable[i] = i + 128;
255         }
256
257         if (IsUnderPostmaster)
258         {
259                 GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
260                 filename = ChTable;
261         }
262         else
263                 filename = getenv("PG_RECODETABLE");
264
265         if (filename && *filename != '\0')
266         {
267                 map_file = palloc(strlen(DataDir) + strlen(filename) + 2);
268                 sprintf(map_file, "%s/%s", DataDir, filename);
269                 file = AllocateFile(map_file, "r");
270                 pfree(map_file);
271                 if (file == NULL)
272                         return;
273
274                 while (!feof(file))
275                 {
276                         next_token(file, buf, sizeof(buf));
277                         if (buf[0] != '\0')
278                         {
279                                 FromChar = strtoul(buf, 0, 0);
280                                 /* Read the ToChar */
281                                 next_token(file, buf, sizeof(buf));
282                                 if (buf[0] != '\0')
283                                 {
284                                         ToChar = strtoul(buf, 0, 0);
285                                         RecodeForwTable[FromChar - 128] = ToChar;
286                                         RecodeBackTable[ToChar - 128] = FromChar;
287
288                                         /* read to EOL */
289                                         while (!feof(file) && buf[0])
290                                         {
291                                                 next_token(file, buf, sizeof(buf));
292                                                 elog(LOG, "SetCharSet: unknown tag %s in file %s"
293                                                         buf, filename);
294                                         }
295                                 }
296                         }
297                 }
298                 FreeFile(file);
299         }
300 }
301
302
303 char *
304 convertstr(unsigned char *buff, int len, int dest)
305 {
306         int                     i;
307         char       *ch = buff;
308
309         for (i = 0; i < len; i++, buff++)
310         {
311                 if (*buff > 127)
312                 {
313                         if (dest)
314                                 *buff = RecodeForwTable[*buff - 128];
315                         else
316                                 *buff = RecodeBackTable[*buff - 128];
317                 }
318         }
319         return ch;
320 }
321
322 #define CHARSET_FILE "charset.conf"
323 #define MAX_CHARSETS   10
324 #define KEY_HOST           1
325 #define KEY_BASE           2
326 #define KEY_TABLE          3
327
328 struct CharsetItem
329 {
330         char            Orig[MAX_TOKEN];
331         char            Dest[MAX_TOKEN];
332         char            Table[MAX_TOKEN];
333 };
334
335
336 static bool
337 CharSetInRange(char *buf, int host)
338 {
339         int                     valid,
340                                 i,
341                                 FromAddr,
342                                 ToAddr,
343                                 tmp;
344         struct in_addr file_ip_addr;
345         char       *p;
346         unsigned int one = 0x80000000,
347                                 NetMask = 0;
348         unsigned char mask;
349
350         p = strchr(buf, '/');
351         if (p)
352         {
353                 *p++ = '\0';
354                 valid = inet_aton(buf, &file_ip_addr);
355                 if (valid)
356                 {
357                         mask = strtoul(p, 0, 0);
358                         FromAddr = ntohl(file_ip_addr.s_addr);
359                         ToAddr = ntohl(file_ip_addr.s_addr);
360                         for (i = 0; i < mask; i++)
361                         {
362                                 NetMask |= one;
363                                 one >>= 1;
364                         }
365                         FromAddr &= NetMask;
366                         ToAddr = ToAddr | ~NetMask;
367                         tmp = ntohl(host);
368                         return ((unsigned) tmp >= (unsigned) FromAddr &&
369                                         (unsigned) tmp <= (unsigned) ToAddr);
370                 }
371         }
372         else
373         {
374                 p = strchr(buf, '-');
375                 if (p)
376                 {
377                         *p++ = '\0';
378                         valid = inet_aton(buf, &file_ip_addr);
379                         if (valid)
380                         {
381                                 FromAddr = ntohl(file_ip_addr.s_addr);
382                                 valid = inet_aton(p, &file_ip_addr);
383                                 if (valid)
384                                 {
385                                         ToAddr = ntohl(file_ip_addr.s_addr);
386                                         tmp = ntohl(host);
387                                         return ((unsigned) tmp >= (unsigned) FromAddr &&
388                                                         (unsigned) tmp <= (unsigned) ToAddr);
389                                 }
390                         }
391                 }
392                 else
393                 {
394                         valid = inet_aton(buf, &file_ip_addr);
395                         if (valid)
396                         {
397                                 FromAddr = file_ip_addr.s_addr;
398                                 return (unsigned) FromAddr == (unsigned) host;
399                         }
400                 }
401         }
402         return false;
403 }
404
405
406 static void
407 GetCharSetByHost(char *TableName, int host, const char *DataDir)
408 {
409         FILE       *file;
410         char            buf[MAX_TOKEN],
411                                 BaseCharset[MAX_TOKEN],
412                                 OrigCharset[MAX_TOKEN],
413                                 DestCharset[MAX_TOKEN],
414                                 HostCharset[MAX_TOKEN],
415                            *map_file;
416         int                     key,
417                                 ChIndex = 0,
418                                 c,
419                                 i,
420                                 bufsize;
421         struct CharsetItem *ChArray[MAX_CHARSETS];
422
423         *TableName = '\0';
424         bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char);
425         map_file = (char *) palloc(bufsize);
426         snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE);
427         file = AllocateFile(map_file, "r");
428         pfree(map_file);
429         if (file == NULL)
430         {
431                 /* XXX should we log a complaint? */
432                 return;
433         }
434
435         while (!feof(file))
436         {
437                 next_token(file, buf, sizeof(buf));
438                 if (buf[0] != '\0')
439                 {
440                         key = 0;
441                         if (strcasecmp(buf, "HostCharset") == 0)
442                                 key = KEY_HOST;
443                         else if (strcasecmp(buf, "BaseCharset") == 0)
444                                 key = KEY_BASE;
445                         else if (strcasecmp(buf, "RecodeTable") == 0)
446                                 key = KEY_TABLE;
447                         else
448                                 elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
449                                         buf, CHARSET_FILE);
450
451                         switch (key)
452                         {
453                                 case KEY_HOST:
454                                         /* Read the host */
455                                         next_token(file, buf, sizeof(buf));
456                                         if (buf[0] != '\0')
457                                         {
458                                                 if (CharSetInRange(buf, host))
459                                                 {
460                                                         /* Read the charset */
461                                                         next_token(file, buf, sizeof(buf));
462                                                         if (buf[0] != '\0')
463                                                                 strcpy(HostCharset, buf);
464                                                 }
465                                         }
466                                         break;
467                                 case KEY_BASE:
468                                         /* Read the base charset */
469                                         next_token(file, buf, sizeof(buf));
470                                         if (buf[0] != '\0')
471                                                 strcpy(BaseCharset, buf);
472                                         break;
473                                 case KEY_TABLE:
474                                         /* Read the original charset */
475                                         next_token(file, buf, sizeof(buf));
476                                         if (buf[0] != '\0')
477                                         {
478                                                 strcpy(OrigCharset, buf);
479                                                 /* Read the destination charset */
480                                                 next_token(file, buf, sizeof(buf));
481                                                 if (buf[0] != '\0')
482                                                 {
483                                                         strcpy(DestCharset, buf);
484                                                         /* Read the table filename */
485                                                         next_token(file, buf, sizeof(buf));
486                                                         if (buf[0] != '\0')
487                                                         {
488                                                                 ChArray[ChIndex] =
489                                                                         (struct CharsetItem *) palloc(sizeof(struct CharsetItem));
490                                                                 strcpy(ChArray[ChIndex]->Orig, OrigCharset);
491                                                                 strcpy(ChArray[ChIndex]->Dest, DestCharset);
492                                                                 strcpy(ChArray[ChIndex]->Table, buf);
493                                                                 ChIndex++;
494                                                         }
495                                                 }
496                                         }
497                                         break;
498                         }
499
500                         /* read to EOL */
501                         while (!feof(file) && buf[0])
502                         {
503                                 next_token(file, buf, sizeof(buf));
504                                 elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
505                                         buf, CHARSET_FILE);
506                         }
507                 }
508         }
509         FreeFile(file);
510
511         for (i = 0; i < ChIndex; i++)
512         {
513                 if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 &&
514                         strcasecmp(HostCharset, ChArray[i]->Dest) == 0)
515                         strncpy(TableName, ChArray[i]->Table, 79);
516                 pfree(ChArray[i]);
517         }
518 }
519
520 #endif   /* CYR_RECODE */
521
522
523
524 /* ----------------------------------------------------------------
525  *      User ID things
526  *
527  * The session user is determined at connection start and never
528  * changes.  The current user may change when "setuid" functions
529  * are implemented.  Conceptually there is a stack, whose bottom
530  * is the session user.  You are yourself responsible to save and
531  * restore the current user id if you need to change it.
532  * ----------------------------------------------------------------
533  */
534 static Oid      CurrentUserId = InvalidOid;
535 static Oid      SessionUserId = InvalidOid;
536
537 static bool AuthenticatedUserIsSuperuser = false;
538
539 /*
540  * This function is relevant for all privilege checks.
541  */
542 Oid
543 GetUserId(void)
544 {
545         AssertState(OidIsValid(CurrentUserId));
546         return CurrentUserId;
547 }
548
549
550 void
551 SetUserId(Oid newid)
552 {
553         AssertArg(OidIsValid(newid));
554         CurrentUserId = newid;
555 }
556
557
558 /*
559  * This value is only relevant for informational purposes.
560  */
561 Oid
562 GetSessionUserId(void)
563 {
564         AssertState(OidIsValid(SessionUserId));
565         return SessionUserId;
566 }
567
568
569 void
570 SetSessionUserId(Oid newid)
571 {
572         AssertArg(OidIsValid(newid));
573         SessionUserId = newid;
574         /* Current user defaults to session user. */
575         if (!OidIsValid(CurrentUserId))
576                 CurrentUserId = newid;
577 }
578
579
580 void
581 InitializeSessionUserId(const char *username)
582 {
583         HeapTuple       userTup;
584         Datum           datum;
585         bool            isnull;
586
587         /*
588          * Don't do scans if we're bootstrapping, none of the system catalogs
589          * exist yet, and they should be owned by postgres anyway.
590          */
591         AssertState(!IsBootstrapProcessingMode());
592
593         /* call only once */
594         AssertState(!OidIsValid(SessionUserId));
595
596         userTup = SearchSysCache(SHADOWNAME,
597                                                          PointerGetDatum(username),
598                                                          0, 0, 0);
599         if (!HeapTupleIsValid(userTup))
600                 elog(FATAL, "user \"%s\" does not exist", username);
601
602         SetSessionUserId(((Form_pg_shadow) GETSTRUCT(userTup))->usesysid);
603
604         AuthenticatedUserIsSuperuser = ((Form_pg_shadow) GETSTRUCT(userTup))->usesuper;
605
606         /*
607          * Set up user-specific configuration variables.  This is a good
608          * place to do it so we don't have to read pg_shadow twice during
609          * session startup.
610          */
611         datum = SysCacheGetAttr(SHADOWNAME, userTup,
612                                                         Anum_pg_shadow_useconfig, &isnull);
613         if (!isnull)
614         {
615                 ArrayType *a = DatumGetArrayTypeP(datum);
616
617                 ProcessGUCArray(a, PGC_S_USER);
618         }
619
620         ReleaseSysCache(userTup);
621 }
622
623
624 void
625 InitializeSessionUserIdStandalone(void)
626 {
627         /* This function should only be called in a single-user backend. */
628         AssertState(!IsUnderPostmaster);
629
630         /* call only once */
631         AssertState(!OidIsValid(SessionUserId));
632
633         SetSessionUserId(BOOTSTRAP_USESYSID);
634         AuthenticatedUserIsSuperuser = true;
635 }
636
637
638 /*
639  * Change session auth ID while running
640  */
641 void
642 SetSessionAuthorization(const char *username)
643 {
644         int32           userid;
645
646         if (!AuthenticatedUserIsSuperuser)
647                 elog(ERROR, "permission denied");
648
649         userid = get_usesysid(username);
650
651         SetSessionUserId(userid);
652         SetUserId(userid);
653 }
654
655
656 /*
657  * Get user name from user id
658  */
659 char *
660 GetUserName(Oid userid)
661 {
662         HeapTuple       tuple;
663         char       *result;
664
665         tuple = SearchSysCache(SHADOWSYSID,
666                                                    ObjectIdGetDatum(userid),
667                                                    0, 0, 0);
668         if (!HeapTupleIsValid(tuple))
669                 elog(ERROR, "invalid user id %u", (unsigned) userid);
670
671         result = pstrdup(NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename));
672
673         ReleaseSysCache(tuple);
674         return result;
675 }
676
677
678
679 /*-------------------------------------------------------------------------
680  *                              Interlock-file support
681  *
682  * These routines are used to create both a data-directory lockfile
683  * ($DATADIR/postmaster.pid) and a Unix-socket-file lockfile ($SOCKFILE.lock).
684  * Both kinds of files contain the same info:
685  *
686  *              Owning process' PID
687  *              Data directory path
688  *
689  * By convention, the owning process' PID is negated if it is a standalone
690  * backend rather than a postmaster.  This is just for informational purposes.
691  * The path is also just for informational purposes (so that a socket lockfile
692  * can be more easily traced to the associated postmaster).
693  *
694  * A data-directory lockfile can optionally contain a third line, containing
695  * the key and ID for the shared memory block used by this postmaster.
696  *
697  * On successful lockfile creation, a proc_exit callback to remove the
698  * lockfile is automatically created.
699  *-------------------------------------------------------------------------
700  */
701
702 /*
703  * proc_exit callback to remove a lockfile.
704  */
705 static void
706 UnlinkLockFile(int status, Datum filename)
707 {
708         unlink((char *) DatumGetPointer(filename));
709         /* Should we complain if the unlink fails? */
710 }
711
712 /*
713  * Create a lockfile, if possible
714  *
715  * Call CreateLockFile with the name of the lockfile to be created.
716  * Returns true if successful, false if not (with a message on stderr).
717  *
718  * amPostmaster is used to determine how to encode the output PID.
719  * isDDLock and refName are used to determine what error message to produce.
720  */
721 static bool
722 CreateLockFile(const char *filename, bool amPostmaster,
723                            bool isDDLock, const char *refName)
724 {
725         int                     fd;
726         char            buffer[MAXPGPATH + 100];
727         int                     ntries;
728         int                     len;
729         int                     encoded_pid;
730         pid_t           other_pid;
731         pid_t           my_pid = getpid();
732
733         /*
734          * We need a loop here because of race conditions.      But don't loop
735          * forever (for example, a non-writable $PGDATA directory might cause
736          * a failure that won't go away).  100 tries seems like plenty.
737          */
738         for (ntries = 0;; ntries++)
739         {
740                 /*
741                  * Try to create the lock file --- O_EXCL makes this atomic.
742                  */
743                 fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
744                 if (fd >= 0)
745                         break;                          /* Success; exit the retry loop */
746
747                 /*
748                  * Couldn't create the pid file. Probably it already exists.
749                  */
750                 if ((errno != EEXIST && errno != EACCES) || ntries > 100)
751                         elog(FATAL, "Can't create lock file %s: %m", filename);
752
753                 /*
754                  * Read the file to get the old owner's PID.  Note race condition
755                  * here: file might have been deleted since we tried to create it.
756                  */
757                 fd = open(filename, O_RDONLY, 0600);
758                 if (fd < 0)
759                 {
760                         if (errno == ENOENT)
761                                 continue;               /* race condition; try again */
762                         elog(FATAL, "Can't read lock file %s: %m", filename);
763                 }
764                 if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0)
765                         elog(FATAL, "Can't read lock file %s: %m", filename);
766                 close(fd);
767
768                 buffer[len] = '\0';
769                 encoded_pid = atoi(buffer);
770
771                 /* if pid < 0, the pid is for postgres, not postmaster */
772                 other_pid = (pid_t) (encoded_pid < 0 ? -encoded_pid : encoded_pid);
773
774                 if (other_pid <= 0)
775                         elog(FATAL, "Bogus data in lock file %s", filename);
776
777                 /*
778                  * Check to see if the other process still exists
779                  *
780                  * Normally kill() will fail with ESRCH if the given PID doesn't
781                  * exist.  BeOS returns EINVAL for some silly reason, however.
782                  */
783                 if (other_pid != my_pid)
784                 {
785                         if (kill(other_pid, 0) == 0 ||
786                                 (errno != ESRCH
787 #ifdef __BEOS__
788                                  && errno != EINVAL
789 #endif
790                                  ))
791                         {
792                                 /* lockfile belongs to a live process */
793                                 fprintf(stderr, "Lock file \"%s\" already exists.\n",
794                                                 filename);
795                                 if (isDDLock)
796                                         fprintf(stderr,
797                                                         "Is another %s (pid %d) running in \"%s\"?\n",
798                                                         (encoded_pid < 0 ? "postgres" : "postmaster"),
799                                                         (int) other_pid, refName);
800                                 else
801                                         fprintf(stderr,
802                                                         "Is another %s (pid %d) using \"%s\"?\n",
803                                                         (encoded_pid < 0 ? "postgres" : "postmaster"),
804                                                         (int) other_pid, refName);
805                                 return false;
806                         }
807                 }
808
809                 /*
810                  * No, the creating process did not exist.      However, it could be
811                  * that the postmaster crashed (or more likely was kill -9'd by a
812                  * clueless admin) but has left orphan backends behind.  Check for
813                  * this by looking to see if there is an associated shmem segment
814                  * that is still in use.
815                  */
816                 if (isDDLock)
817                 {
818                         char       *ptr;
819                         unsigned long shmKey,
820                                                 shmId;
821
822                         ptr = strchr(buffer, '\n');
823                         if (ptr != NULL &&
824                                 (ptr = strchr(ptr + 1, '\n')) != NULL)
825                         {
826                                 ptr++;
827                                 if (sscanf(ptr, "%lu %lu", &shmKey, &shmId) == 2)
828                                 {
829                                         if (SharedMemoryIsInUse((IpcMemoryKey) shmKey,
830                                                                                         (IpcMemoryId) shmId))
831                                         {
832                                                 fprintf(stderr,
833                                                                 "Found a pre-existing shared memory block (ID %d) still in use.\n"
834                                                                 "If you're sure there are no old backends still running,\n"
835                                                                 "remove the shared memory block with ipcrm(1), or just\n"
836                                                                 "delete \"%s\".\n",
837                                                                 (int) shmId, filename);
838                                                 return false;
839                                         }
840                                 }
841                         }
842                 }
843
844                 /*
845                  * Looks like nobody's home.  Unlink the file and try again to
846                  * create it.  Need a loop because of possible race condition
847                  * against other would-be creators.
848                  */
849                 if (unlink(filename) < 0)
850                         elog(FATAL, "Can't remove old lock file %s: %m"
851                                  "\n\tThe file seems accidentally left, but I couldn't remove it."
852                                  "\n\tPlease remove the file by hand and try again.",
853                                  filename);
854         }
855
856         /*
857          * Successfully created the file, now fill it.
858          */
859         snprintf(buffer, sizeof(buffer), "%d\n%s\n",
860                          amPostmaster ? (int) my_pid : -((int) my_pid),
861                          DataDir);
862         errno = 0;
863         if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
864         {
865                 int                     save_errno = errno;
866
867                 close(fd);
868                 unlink(filename);
869                 /* if write didn't set errno, assume problem is no disk space */
870                 errno = save_errno ? save_errno : ENOSPC;
871                 elog(FATAL, "Can't write lock file %s: %m", filename);
872         }
873         close(fd);
874
875         /*
876          * Arrange for automatic removal of lockfile at proc_exit.
877          */
878         on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename)));
879
880         return true;                            /* Success! */
881 }
882
883 bool
884 CreateDataDirLockFile(const char *datadir, bool amPostmaster)
885 {
886         char            lockfile[MAXPGPATH];
887
888         snprintf(lockfile, sizeof(lockfile), "%s/postmaster.pid", datadir);
889         if (!CreateLockFile(lockfile, amPostmaster, true, datadir))
890                 return false;
891         /* Save name of lockfile for RecordSharedMemoryInLockFile */
892         strcpy(directoryLockFile, lockfile);
893         return true;
894 }
895
896 bool
897 CreateSocketLockFile(const char *socketfile, bool amPostmaster)
898 {
899         char            lockfile[MAXPGPATH];
900
901         snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile);
902         if (!CreateLockFile(lockfile, amPostmaster, false, socketfile))
903                 return false;
904         /* Save name of lockfile for TouchSocketLockFile */
905         strcpy(socketLockFile, lockfile);
906         return true;
907 }
908
909 /*
910  * Re-read the socket lock file.  This should be called every so often
911  * to ensure that the lock file has a recent access date.  That saves it
912  * from being removed by overenthusiastic /tmp-directory-cleaner daemons.
913  * (Another reason we should never have put the socket file in /tmp...)
914  */
915 void
916 TouchSocketLockFile(void)
917 {
918         int                     fd;
919         char            buffer[1];
920
921         /* Do nothing if we did not create a socket... */
922         if (socketLockFile[0] != '\0')
923         {
924                 /* XXX any need to complain about errors here? */
925                 fd = open(socketLockFile, O_RDONLY | PG_BINARY, 0);
926                 if (fd >= 0)
927                 {
928                         read(fd, buffer, sizeof(buffer));
929                         close(fd);
930                 }
931         }
932 }
933
934 /*
935  * Append information about a shared memory segment to the data directory
936  * lock file (if we have created one).
937  *
938  * This may be called multiple times in the life of a postmaster, if we
939  * delete and recreate shmem due to backend crash.      Therefore, be prepared
940  * to overwrite existing information.  (As of 7.1, a postmaster only creates
941  * one shm seg anyway; but for the purposes here, if we did have more than
942  * one then any one of them would do anyway.)
943  */
944 void
945 RecordSharedMemoryInLockFile(IpcMemoryKey shmKey, IpcMemoryId shmId)
946 {
947         int                     fd;
948         int                     len;
949         char       *ptr;
950         char            buffer[BLCKSZ];
951
952         /*
953          * Do nothing if we did not create a lockfile (probably because we are
954          * running standalone).
955          */
956         if (directoryLockFile[0] == '\0')
957                 return;
958
959         fd = open(directoryLockFile, O_RDWR | PG_BINARY, 0);
960         if (fd < 0)
961         {
962                 elog(LOG, "Failed to rewrite %s: %m", directoryLockFile);
963                 return;
964         }
965         len = read(fd, buffer, sizeof(buffer) - 100);
966         if (len <= 0)
967         {
968                 elog(LOG, "Failed to read %s: %m", directoryLockFile);
969                 close(fd);
970                 return;
971         }
972         buffer[len] = '\0';
973
974         /*
975          * Skip over first two lines (PID and path).
976          */
977         ptr = strchr(buffer, '\n');
978         if (ptr == NULL ||
979                 (ptr = strchr(ptr + 1, '\n')) == NULL)
980         {
981                 elog(LOG, "Bogus data in %s", directoryLockFile);
982                 close(fd);
983                 return;
984         }
985         ptr++;
986
987         /*
988          * Append shm key and ID.  Format to try to keep it the same length
989          * always (trailing junk won't hurt, but might confuse humans).
990          */
991         sprintf(ptr, "%9lu %9lu\n",
992                         (unsigned long) shmKey, (unsigned long) shmId);
993
994         /*
995          * And rewrite the data.  Since we write in a single kernel call, this
996          * update should appear atomic to onlookers.
997          */
998         len = strlen(buffer);
999         errno = 0;
1000         if (lseek(fd, (off_t) 0, SEEK_SET) != 0 ||
1001                 (int) write(fd, buffer, len) != len)
1002         {
1003                 /* if write didn't set errno, assume problem is no disk space */
1004                 if (errno == 0)
1005                         errno = ENOSPC;
1006                 elog(LOG, "Failed to write %s: %m", directoryLockFile);
1007                 close(fd);
1008                 return;
1009         }
1010         close(fd);
1011 }
1012
1013
1014 /*-------------------------------------------------------------------------
1015  *                              Version checking support
1016  *-------------------------------------------------------------------------
1017  */
1018
1019 /*
1020  * Determine whether the PG_VERSION file in directory `path' indicates
1021  * a data version compatible with the version of this program.
1022  *
1023  * If compatible, return. Otherwise, elog(FATAL).
1024  */
1025 void
1026 ValidatePgVersion(const char *path)
1027 {
1028         char            full_path[MAXPGPATH];
1029         FILE       *file;
1030         int                     ret;
1031         long            file_major,
1032                                 file_minor;
1033         long            my_major = 0,
1034                                 my_minor = 0;
1035         char       *endptr;
1036         const char *version_string = PG_VERSION;
1037
1038         my_major = strtol(version_string, &endptr, 10);
1039         if (*endptr == '.')
1040                 my_minor = strtol(endptr + 1, NULL, 10);
1041
1042         snprintf(full_path, MAXPGPATH, "%s/PG_VERSION", path);
1043
1044         file = AllocateFile(full_path, "r");
1045         if (!file)
1046         {
1047                 if (errno == ENOENT)
1048                         elog(FATAL, "File %s is missing. This is not a valid data directory.", full_path);
1049                 else
1050                         elog(FATAL, "cannot open %s: %m", full_path);
1051         }
1052
1053         ret = fscanf(file, "%ld.%ld", &file_major, &file_minor);
1054         if (ret != 2)
1055                 elog(FATAL, "File %s does not contain valid data. You need to initdb.", full_path);
1056
1057         FreeFile(file);
1058
1059         if (my_major != file_major || my_minor != file_minor)
1060                 elog(FATAL, "The data directory was initialized by PostgreSQL version %ld.%ld, "
1061                          "which is not compatible with this version %s.",
1062                          file_major, file_minor, version_string);
1063 }