]> granicus.if.org Git - postgresql/blob - src/backend/utils/init/miscinit.c
Adjust 'permission denied' messages to be more useful and consistent.
[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.110 2003/08/01 00:15:23 tgl 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   if( fname != NULL )
690     {
691       if( unlink(fname) != 0 )
692         {
693           /* Should we complain if the unlink fails? */
694         }
695       free(fname);
696     }
697 }
698
699 /*
700  * Create a lockfile.
701  *
702  * filename is the name of the lockfile to create.
703  * amPostmaster is used to determine how to encode the output PID.
704  * isDDLock and refName are used to determine what error message to produce.
705  */
706 static void
707 CreateLockFile(const char *filename, bool amPostmaster,
708                            bool isDDLock, const char *refName)
709 {
710         int                     fd;
711         char            buffer[MAXPGPATH + 100];
712         int                     ntries;
713         int                     len;
714         int                     encoded_pid;
715         pid_t           other_pid;
716         pid_t           my_pid = getpid();
717
718         /*
719          * We need a loop here because of race conditions.      But don't loop
720          * forever (for example, a non-writable $PGDATA directory might cause
721          * a failure that won't go away).  100 tries seems like plenty.
722          */
723         for (ntries = 0;; ntries++)
724         {
725                 /*
726                  * Try to create the lock file --- O_EXCL makes this atomic.
727                  */
728                 fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
729                 if (fd >= 0)
730                         break;                          /* Success; exit the retry loop */
731
732                 /*
733                  * Couldn't create the pid file. Probably it already exists.
734                  */
735                 if ((errno != EEXIST && errno != EACCES) || ntries > 100)
736                         ereport(FATAL,
737                                         (errcode_for_file_access(),
738                                          errmsg("could not create lock file \"%s\": %m",
739                                                         filename)));
740
741                 /*
742                  * Read the file to get the old owner's PID.  Note race condition
743                  * here: file might have been deleted since we tried to create it.
744                  */
745                 fd = open(filename, O_RDONLY, 0600);
746                 if (fd < 0)
747                 {
748                         if (errno == ENOENT)
749                                 continue;               /* race condition; try again */
750                         ereport(FATAL,
751                                         (errcode_for_file_access(),
752                                          errmsg("could not open lock file \"%s\": %m",
753                                                         filename)));
754                 }
755                 if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0)
756                         ereport(FATAL,
757                                         (errcode_for_file_access(),
758                                          errmsg("could not read lock file \"%s\": %m",
759                                                         filename)));
760                 close(fd);
761
762                 buffer[len] = '\0';
763                 encoded_pid = atoi(buffer);
764
765                 /* if pid < 0, the pid is for postgres, not postmaster */
766                 other_pid = (pid_t) (encoded_pid < 0 ? -encoded_pid : encoded_pid);
767
768                 if (other_pid <= 0)
769                         elog(FATAL, "bogus data in lock file \"%s\"", filename);
770
771                 /*
772                  * Check to see if the other process still exists
773                  *
774                  * Normally kill() will fail with ESRCH if the given PID doesn't
775                  * exist.  BeOS returns EINVAL for some silly reason, however.
776                  */
777                 if (other_pid != my_pid)
778                 {
779                         if (kill(other_pid, 0) == 0 ||
780                                 (errno != ESRCH
781 #ifdef __BEOS__
782                                  && errno != EINVAL
783 #endif
784                                  ))
785                         {
786                                 /* lockfile belongs to a live process */
787                                 ereport(FATAL,
788                                                 (errcode(ERRCODE_LOCK_FILE_EXISTS),
789                                                  errmsg("lock file \"%s\" already exists",
790                                                                 filename),
791                                                  isDDLock ?
792                                                  errhint("Is another %s (pid %d) running in \"%s\"?",
793                                                                  (encoded_pid < 0 ? "postgres" : "postmaster"),
794                                                                  (int) other_pid, refName) :
795                                                  errhint("Is another %s (pid %d) using \"%s\"?",
796                                                                  (encoded_pid < 0 ? "postgres" : "postmaster"),
797                                                                  (int) other_pid, refName)));
798                         }
799                 }
800
801                 /*
802                  * No, the creating process did not exist.      However, it could be
803                  * that the postmaster crashed (or more likely was kill -9'd by a
804                  * clueless admin) but has left orphan backends behind.  Check for
805                  * this by looking to see if there is an associated shmem segment
806                  * that is still in use.
807                  */
808                 if (isDDLock)
809                 {
810                         char       *ptr;
811                         unsigned long id1,
812                                                 id2;
813
814                         ptr = strchr(buffer, '\n');
815                         if (ptr != NULL &&
816                                 (ptr = strchr(ptr + 1, '\n')) != NULL)
817                         {
818                                 ptr++;
819                                 if (sscanf(ptr, "%lu %lu", &id1, &id2) == 2)
820                                 {
821                                         if (PGSharedMemoryIsInUse(id1, id2))
822                                                 ereport(FATAL,
823                                                                 (errcode(ERRCODE_LOCK_FILE_EXISTS),
824                                                                  errmsg("pre-existing shared memory block "
825                                                                                 "(key %lu, id %lu) is still in use",
826                                                                                 id1, id2),
827                                                                  errhint("If you're sure there are no old "
828                                                                                  "backends still running, remove "
829                                                                                  "the shared memory block with "
830                                                                                  "ipcrm(1), or just delete \"%s\".",
831                                                                                  filename)));
832                                 }
833                         }
834                 }
835
836                 /*
837                  * Looks like nobody's home.  Unlink the file and try again to
838                  * create it.  Need a loop because of possible race condition
839                  * against other would-be creators.
840                  */
841                 if (unlink(filename) < 0)
842                         ereport(FATAL,
843                                         (errcode_for_file_access(),
844                                          errmsg("could not remove old lock file \"%s\": %m",
845                                                         filename),
846                                          errhint("The file seems accidentally left over, but "
847                                                          "I couldn't remove it. Please remove the file "
848                                                          "by hand and try again.")));
849         }
850
851         /*
852          * Successfully created the file, now fill it.
853          */
854         snprintf(buffer, sizeof(buffer), "%d\n%s\n",
855                          amPostmaster ? (int) my_pid : -((int) my_pid),
856                          DataDir);
857         errno = 0;
858         if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
859         {
860                 int                     save_errno = errno;
861
862                 close(fd);
863                 unlink(filename);
864                 /* if write didn't set errno, assume problem is no disk space */
865                 errno = save_errno ? save_errno : ENOSPC;
866                 ereport(FATAL,
867                                 (errcode_for_file_access(),
868                                  errmsg("could not write lock file \"%s\": %m", filename)));
869         }
870         close(fd);
871
872         /*
873          * Arrange for automatic removal of lockfile at proc_exit.
874          */
875         on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename)));
876 }
877
878 void
879 CreateDataDirLockFile(const char *datadir, bool amPostmaster)
880 {
881         char            lockfile[MAXPGPATH];
882
883         snprintf(lockfile, sizeof(lockfile), "%s/postmaster.pid", datadir);
884         CreateLockFile(lockfile, amPostmaster, true, datadir);
885         /* Save name of lockfile for RecordSharedMemoryInLockFile */
886         strcpy(directoryLockFile, lockfile);
887 }
888
889 void
890 CreateSocketLockFile(const char *socketfile, bool amPostmaster)
891 {
892         char            lockfile[MAXPGPATH];
893
894         snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile);
895         CreateLockFile(lockfile, amPostmaster, false, socketfile);
896         /* Save name of lockfile for TouchSocketLockFile */
897         strcpy(socketLockFile, lockfile);
898 }
899
900 /*
901  * TouchSocketLockFile -- mark socket lock file as recently accessed
902  *
903  * This routine should be called every so often to ensure that the lock file
904  * has a recent mod or access date.  That saves it
905  * from being removed by overenthusiastic /tmp-directory-cleaner daemons.
906  * (Another reason we should never have put the socket file in /tmp...)
907  */
908 void
909 TouchSocketLockFile(void)
910 {
911         /* Do nothing if we did not create a socket... */
912         if (socketLockFile[0] != '\0')
913         {
914                 /*
915                  * utime() is POSIX standard, utimes() is a common alternative;
916                  * if we have neither, fall back to actually reading the file
917                  * (which only sets the access time not mod time, but that should
918                  * be enough in most cases).  In all paths, we ignore errors.
919                  */
920 #ifdef HAVE_UTIME
921                 utime(socketLockFile, NULL);
922 #else /* !HAVE_UTIME */
923 #ifdef HAVE_UTIMES
924                 utimes(socketLockFile, NULL);
925 #else /* !HAVE_UTIMES */
926                 int                     fd;
927                 char            buffer[1];
928
929                 fd = open(socketLockFile, O_RDONLY | PG_BINARY, 0);
930                 if (fd >= 0)
931                 {
932                         read(fd, buffer, sizeof(buffer));
933                         close(fd);
934                 }
935 #endif /* HAVE_UTIMES */
936 #endif /* HAVE_UTIME */
937         }
938 }
939
940 /*
941  * Append information about a shared memory segment to the data directory
942  * lock file (if we have created one).
943  *
944  * This may be called multiple times in the life of a postmaster, if we
945  * delete and recreate shmem due to backend crash.      Therefore, be prepared
946  * to overwrite existing information.  (As of 7.1, a postmaster only creates
947  * one shm seg at a time; but for the purposes here, if we did have more than
948  * one then any one of them would do anyway.)
949  */
950 void
951 RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2)
952 {
953         int                     fd;
954         int                     len;
955         char       *ptr;
956         char            buffer[BLCKSZ];
957
958         /*
959          * Do nothing if we did not create a lockfile (probably because we are
960          * running standalone).
961          */
962         if (directoryLockFile[0] == '\0')
963                 return;
964
965         fd = open(directoryLockFile, O_RDWR | PG_BINARY, 0);
966         if (fd < 0)
967         {
968                 ereport(LOG,
969                                 (errcode_for_file_access(),
970                                  errmsg("could not rewrite \"%s\": %m",
971                                                 directoryLockFile)));
972                 return;
973         }
974         len = read(fd, buffer, sizeof(buffer) - 100);
975         if (len <= 0)
976         {
977                 ereport(LOG,
978                                 (errcode_for_file_access(),
979                                  errmsg("could not read \"%s\": %m",
980                                                 directoryLockFile)));
981                 close(fd);
982                 return;
983         }
984         buffer[len] = '\0';
985
986         /*
987          * Skip over first two lines (PID and path).
988          */
989         ptr = strchr(buffer, '\n');
990         if (ptr == NULL ||
991                 (ptr = strchr(ptr + 1, '\n')) == NULL)
992         {
993                 elog(LOG, "bogus data in \"%s\"", directoryLockFile);
994                 close(fd);
995                 return;
996         }
997         ptr++;
998
999         /*
1000          * Append key information.      Format to try to keep it the same length
1001          * always (trailing junk won't hurt, but might confuse humans).
1002          */
1003         sprintf(ptr, "%9lu %9lu\n", id1, id2);
1004
1005         /*
1006          * And rewrite the data.  Since we write in a single kernel call, this
1007          * update should appear atomic to onlookers.
1008          */
1009         len = strlen(buffer);
1010         errno = 0;
1011         if (lseek(fd, (off_t) 0, SEEK_SET) != 0 ||
1012                 (int) write(fd, buffer, len) != len)
1013         {
1014                 /* if write didn't set errno, assume problem is no disk space */
1015                 if (errno == 0)
1016                         errno = ENOSPC;
1017                 ereport(LOG,
1018                                 (errcode_for_file_access(),
1019                                  errmsg("could not write \"%s\": %m",
1020                                                 directoryLockFile)));
1021                 close(fd);
1022                 return;
1023         }
1024         close(fd);
1025 }
1026
1027
1028 /*-------------------------------------------------------------------------
1029  *                              Version checking support
1030  *-------------------------------------------------------------------------
1031  */
1032
1033 /*
1034  * Determine whether the PG_VERSION file in directory `path' indicates
1035  * a data version compatible with the version of this program.
1036  *
1037  * If compatible, return. Otherwise, ereport(FATAL).
1038  */
1039 void
1040 ValidatePgVersion(const char *path)
1041 {
1042         char            full_path[MAXPGPATH];
1043         FILE       *file;
1044         int                     ret;
1045         long            file_major,
1046                                 file_minor;
1047         long            my_major = 0,
1048                                 my_minor = 0;
1049         char       *endptr;
1050         const char *version_string = PG_VERSION;
1051
1052         my_major = strtol(version_string, &endptr, 10);
1053         if (*endptr == '.')
1054                 my_minor = strtol(endptr + 1, NULL, 10);
1055
1056         snprintf(full_path, sizeof(full_path), "%s/PG_VERSION", path);
1057
1058         file = AllocateFile(full_path, "r");
1059         if (!file)
1060         {
1061                 if (errno == ENOENT)
1062                         ereport(FATAL,
1063                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1064                                          errmsg("\"%s\" is not a valid data directory",
1065                                                         path),
1066                                          errdetail("File \"%s\" is missing.", full_path)));
1067                 else
1068                         ereport(FATAL,
1069                                         (errcode_for_file_access(),
1070                                          errmsg("could not open \"%s\": %m", full_path)));
1071         }
1072
1073         ret = fscanf(file, "%ld.%ld", &file_major, &file_minor);
1074         if (ret != 2)
1075                         ereport(FATAL,
1076                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1077                                          errmsg("\"%s\" is not a valid data directory",
1078                                                         path),
1079                                          errdetail("File \"%s\" does not contain valid data.",
1080                                                            full_path),
1081                                          errhint("You may need to initdb.")));
1082
1083         FreeFile(file);
1084
1085         if (my_major != file_major || my_minor != file_minor)
1086                 ereport(FATAL,
1087                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1088                                  errmsg("database files are incompatible with server"),
1089                                  errdetail("The data directory was initialized by PostgreSQL version %ld.%ld, "
1090                                                    "which is not compatible with this version %s.",
1091                                                    file_major, file_minor, version_string)));
1092 }
1093
1094 /*-------------------------------------------------------------------------
1095  *                              Library preload support
1096  *-------------------------------------------------------------------------
1097  */
1098
1099 #if defined(__mc68000__) && defined(__ELF__)
1100 typedef int32 ((*func_ptr) ());
1101 #else
1102 typedef char *((*func_ptr) ());
1103 #endif
1104
1105 /*
1106  * process any libraries that should be preloaded and
1107  * optionally pre-initialized
1108  */
1109 void
1110 process_preload_libraries(char *preload_libraries_string)
1111 {
1112         char       *rawstring;
1113         List       *elemlist;
1114         List       *l;
1115
1116         if (preload_libraries_string == NULL)
1117                 return;
1118
1119         /* Need a modifiable copy of string */
1120         rawstring = pstrdup(preload_libraries_string);
1121
1122         /* Parse string into list of identifiers */
1123         if (!SplitIdentifierString(rawstring, ',', &elemlist))
1124         {
1125                 /* syntax error in list */
1126                 pfree(rawstring);
1127                 freeList(elemlist);
1128                 ereport(LOG,
1129                                 (errcode(ERRCODE_SYNTAX_ERROR),
1130                                  errmsg("invalid list syntax for preload_libraries configuration option")));
1131                 return;
1132         }
1133
1134         foreach(l, elemlist)
1135         {
1136                 char       *tok = (char *) lfirst(l);
1137                 char       *sep = strstr(tok, ":");
1138                 char       *filename = NULL;
1139                 char       *funcname = NULL;
1140                 func_ptr        initfunc;
1141
1142                 if (sep)
1143                 {
1144                         /*
1145                          * a colon separator implies there is an initialization function
1146                          * that we need to run in addition to loading the library
1147                          */
1148                         size_t          filename_len = sep - tok;
1149                         size_t          funcname_len = strlen(tok) - filename_len - 1;
1150
1151                         filename = (char *) palloc(filename_len + 1);
1152                         memcpy(filename, tok, filename_len);
1153                         filename[filename_len] = '\0';
1154
1155                         funcname = (char *) palloc(funcname_len + 1);
1156                         strcpy(funcname, sep + 1);
1157                 }
1158                 else
1159                 {
1160                         /*
1161                          * no separator -- just load the library
1162                          */
1163                         filename = pstrdup(tok);
1164                         funcname = NULL;
1165                 }
1166
1167                 initfunc = (func_ptr) load_external_function(filename, funcname,
1168                                                                                                          true, NULL);
1169                 if (initfunc)
1170                         (*initfunc)();
1171
1172                 if (funcname)
1173                         ereport(LOG,
1174                                         (errmsg("preloaded library \"%s\" with initialization function \"%s\"",
1175                                                         filename, funcname)));
1176                 else
1177                         ereport(LOG,
1178                                         (errmsg("preloaded library \"%s\"",
1179                                                         filename)));
1180
1181                 pfree(filename);
1182                 if (funcname)
1183                         pfree(funcname);
1184         }
1185
1186         pfree(rawstring);
1187         freeList(elemlist);
1188 }
1189