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