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