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