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