]> granicus.if.org Git - postgresql/blob - src/backend/utils/init/miscinit.c
Change SearchSysCache coding conventions so that a reference count is
[postgresql] / src / backend / utils / init / miscinit.c
1 /*-------------------------------------------------------------------------
2  *
3  * miscinit.c
4  *        miscellanious initialization support stuff
5  *
6  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
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.57 2000/11/16 22:30:39 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <signal.h>
20 #include <sys/stat.h>
21 #include <sys/file.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <grp.h>
25 #include <pwd.h>
26 #include <stdlib.h>
27 #include <errno.h>
28
29 #include "catalog/catname.h"
30 #include "catalog/pg_shadow.h"
31 #include "miscadmin.h"
32 #include "utils/builtins.h"
33 #include "utils/syscache.h"
34
35 static char *GetPidFname(void);
36
37
38 #ifdef CYR_RECODE
39 unsigned char RecodeForwTable[128];
40 unsigned char RecodeBackTable[128];
41
42 #endif
43
44 ProcessingMode Mode = InitProcessing;
45
46 /* ----------------------------------------------------------------
47  *              ignoring system indexes support stuff
48  * ----------------------------------------------------------------
49  */
50
51 static bool isIgnoringSystemIndexes = false;
52
53 /*
54  * IsIgnoringSystemIndexes
55  *              True if ignoring system indexes.
56  */
57 bool
58 IsIgnoringSystemIndexes()
59 {
60         return isIgnoringSystemIndexes;
61 }
62
63 /*
64  * IgnoreSystemIndexes
65  *      Set true or false whether PostgreSQL ignores system indexes.
66  *
67  */
68 void
69 IgnoreSystemIndexes(bool mode)
70 {
71         isIgnoringSystemIndexes = mode;
72 }
73
74 /* ----------------------------------------------------------------
75  *                              database path / name support stuff
76  * ----------------------------------------------------------------
77  */
78
79 void
80 SetDatabasePath(const char *path)
81 {
82         free(DatabasePath);
83         /* use strdup since this is done before memory contexts are set up */
84         if (path)
85         {
86                 DatabasePath = strdup(path);
87                 AssertState(DatabasePath);
88         }
89 }
90
91 void
92 SetDatabaseName(const char *name)
93 {
94         free(DatabaseName);
95         if (name)
96         {
97                 DatabaseName = strdup(name);
98                 AssertState(DatabaseName);
99         }
100 }
101
102 #ifndef MULTIBYTE
103 /* even if MULTIBYTE is not enabled, these functions are necessary
104  * since pg_proc.h has references to them.
105  */
106
107 Datum
108 getdatabaseencoding(PG_FUNCTION_ARGS)
109 {
110         PG_RETURN_NAME("SQL_ASCII");
111 }
112
113 Datum
114 PG_encoding_to_char(PG_FUNCTION_ARGS)
115 {
116         PG_RETURN_NAME("SQL_ASCII");
117 }
118
119 Datum
120 PG_char_to_encoding(PG_FUNCTION_ARGS)
121 {
122         PG_RETURN_INT32(0);
123 }
124
125 #endif
126
127 #ifdef CYR_RECODE
128 #define MAX_TOKEN       80
129
130 /* Some standard C libraries, including GNU, have an isblank() function.
131    Others, including Solaris, do not.  So we have our own.
132 */
133 static bool
134 isblank(const char c)
135 {
136         return c == ' ' || c == 9 /* tab */ ;
137 }
138
139 static void
140 next_token(FILE *fp, char *buf, const int bufsz)
141 {
142 /*--------------------------------------------------------------------------
143   Grab one token out of fp.  Tokens are strings of non-blank
144   characters bounded by blank characters, beginning of line, and end
145   of line.      Blank means space or tab.  Return the token as *buf.
146   Leave file positioned to character immediately after the token or
147   EOF, whichever comes first.  If no more tokens on line, return null
148   string as *buf and position file to beginning of next line or EOF,
149   whichever comes first.
150 --------------------------------------------------------------------------*/
151         int                     c;
152         char       *eb = buf + (bufsz - 1);
153
154         /* Move over inital token-delimiting blanks */
155         while (isblank(c = getc(fp)));
156
157         if (c != '\n')
158         {
159
160                 /*
161                  * build a token in buf of next characters up to EOF, eol, or
162                  * blank.
163                  */
164                 while (c != EOF && c != '\n' && !isblank(c))
165                 {
166                         if (buf < eb)
167                                 *buf++ = c;
168                         c = getc(fp);
169
170                         /*
171                          * Put back the char right after the token (putting back EOF
172                          * is ok)
173                          */
174                 }
175                 ungetc(c, fp);
176         }
177         *buf = '\0';
178 }
179
180 static void
181 read_through_eol(FILE *file)
182 {
183         int                     c;
184
185         do
186                 c = getc(file);
187         while (c != '\n' && c != EOF);
188 }
189
190 void
191 SetCharSet()
192 {
193         FILE       *file;
194         char       *p,
195                                 c,
196                                 eof = false;
197         char       *map_file;
198         char            buf[MAX_TOKEN];
199         int                     i;
200         unsigned char FromChar,
201                                 ToChar;
202
203         for (i = 0; i < 128; i++)
204         {
205                 RecodeForwTable[i] = i + 128;
206                 RecodeBackTable[i] = i + 128;
207         }
208
209         p = getenv("PG_RECODETABLE");
210         if (p && *p != '\0')
211         {
212                 map_file = (char *) malloc((strlen(DataDir) +
213                                                                         strlen(p) + 2) * sizeof(char));
214                 sprintf(map_file, "%s/%s", DataDir, p);
215                 file = AllocateFile(map_file, PG_BINARY_R);
216                 if (file == NULL)
217                         return;
218                 eof = false;
219                 while (!eof)
220                 {
221                         c = getc(file);
222                         ungetc(c, file);
223                         if (c == EOF)
224                                 eof = true;
225                         else
226                         {
227                                 if (c == '#')
228                                         read_through_eol(file);
229                                 else
230                                 {
231                                         /* Read the FromChar */
232                                         next_token(file, buf, sizeof(buf));
233                                         if (buf[0] != '\0')
234                                         {
235                                                 FromChar = strtoul(buf, 0, 0);
236                                                 /* Read the ToChar */
237                                                 next_token(file, buf, sizeof(buf));
238                                                 if (buf[0] != '\0')
239                                                 {
240                                                         ToChar = strtoul(buf, 0, 0);
241                                                         RecodeForwTable[FromChar - 128] = ToChar;
242                                                         RecodeBackTable[ToChar - 128] = FromChar;
243                                                 }
244                                                 read_through_eol(file);
245                                         }
246                                 }
247                         }
248                 }
249                 FreeFile(file);
250                 free(map_file);
251         }
252 }
253
254 char *
255 convertstr(unsigned char *buff, int len, int dest)
256 {
257         int                     i;
258         char       *ch = buff;
259
260         for (i = 0; i < len; i++, buff++)
261         {
262                 if (*buff > 127)
263                 {
264                         if (dest)
265                                 *buff = RecodeForwTable[*buff - 128];
266                         else
267                                 *buff = RecodeBackTable[*buff - 128];
268                 }
269         }
270         return ch;
271 }
272
273 #endif
274
275
276
277 /* ----------------------------------------------------------------
278  *      User ID things
279  *
280  * The session user is determined at connection start and never
281  * changes.  The current user may change when "setuid" functions
282  * are implemented.  Conceptually there is a stack, whose bottom
283  * is the session user.  You are yourself responsible to save and
284  * restore the current user id if you need to change it.
285  * ----------------------------------------------------------------
286  */
287 static Oid      CurrentUserId = InvalidOid;
288 static Oid      SessionUserId = InvalidOid;
289
290
291 /*
292  * This function is relevant for all privilege checks.
293  */
294 Oid
295 GetUserId(void)
296 {
297         AssertState(OidIsValid(CurrentUserId));
298         return CurrentUserId;
299 }
300
301
302 void
303 SetUserId(Oid newid)
304 {
305         AssertArg(OidIsValid(newid));
306         CurrentUserId = newid;
307 }
308
309
310 /*
311  * This value is only relevant for informational purposes.
312  */
313 Oid
314 GetSessionUserId(void)
315 {
316         AssertState(OidIsValid(SessionUserId));
317         return SessionUserId;
318 }
319
320
321 void
322 SetSessionUserId(Oid newid)
323 {
324         AssertArg(OidIsValid(newid));
325         SessionUserId = newid;
326         /* Current user defaults to session user. */
327         if (!OidIsValid(CurrentUserId))
328                 CurrentUserId = newid;
329 }
330
331
332 void
333 SetSessionUserIdFromUserName(const char *username)
334 {
335         HeapTuple       userTup;
336
337         /*
338          * Don't do scans if we're bootstrapping, none of the system catalogs
339          * exist yet, and they should be owned by postgres anyway.
340          */
341         AssertState(!IsBootstrapProcessingMode());
342
343         userTup = SearchSysCache(SHADOWNAME,
344                                                          PointerGetDatum(username),
345                                                          0, 0, 0);
346         if (!HeapTupleIsValid(userTup))
347                 elog(FATAL, "user \"%s\" does not exist", username);
348
349         SetSessionUserId( ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid );
350
351         ReleaseSysCache(userTup);
352 }
353
354
355 /*
356  * Get user name from user id
357  */
358 char *
359 GetUserName(Oid userid)
360 {
361         HeapTuple       tuple;
362         char       *result;
363
364         tuple = SearchSysCache(SHADOWSYSID,
365                                                    ObjectIdGetDatum(userid),
366                                                    0, 0, 0);
367         if (!HeapTupleIsValid(tuple))
368                 elog(ERROR, "invalid user id %u", (unsigned) userid);
369
370         result = pstrdup( NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename) );
371
372         ReleaseSysCache(tuple);
373         return result;
374 }
375
376
377
378 /*-------------------------------------------------------------------------
379  * Set data directory, but make sure it's an absolute path.  Use this,
380  * never set DataDir directly.
381  *-------------------------------------------------------------------------
382  */
383 void
384 SetDataDir(const char *dir)
385 {
386         char *new;
387
388         AssertArg(dir);
389         if (DataDir)
390                 free(DataDir);
391
392         if (dir[0] != '/')
393         {
394                 char *buf;
395                 size_t buflen;
396
397                 buflen = MAXPGPATH;
398                 for (;;)
399                 {
400                         buf = malloc(buflen);
401                         if (!buf)
402                                 elog(FATAL, "out of memory");
403
404                         if (getcwd(buf, buflen))
405                                 break;
406                         else if (errno == ERANGE)
407                         {
408                                 free(buf);
409                                 buflen *= 2;
410                                 continue;
411                         }
412                         else
413                         {
414                                 free(buf);
415                                 elog(FATAL, "cannot get current working directory: %m");
416                         }
417                 }
418
419                 new = malloc(strlen(buf) + 1 + strlen(dir) + 1);
420                 sprintf(new, "%s/%s", buf, dir);
421         }
422         else
423         {
424                 new = strdup(dir);
425         }
426
427         if (!new)
428                 elog(FATAL, "out of memory");
429         DataDir = new;          
430 }
431
432
433
434 /*-------------------------------------------------------------------------
435  *
436  * postmaster pid file stuffs. $DATADIR/postmaster.pid is created when:
437  *
438  *      (1) postmaster starts. In this case pid > 0.
439  *      (2) postgres starts in standalone mode. In this case
440  *              pid < 0
441  *
442  * to gain an interlock.
443  *
444  *      SetPidFname(datadir)
445  *              Remember the the pid file name. This is neccesary
446  *              UnlinkPidFile() is called from proc_exit().
447  *
448  *      GetPidFname(datadir)
449  *              Get the pid file name. SetPidFname() should be called
450  *              before GetPidFname() gets called.
451  *
452  *      UnlinkPidFile()
453  *              This is called from proc_exit() and unlink the pid file.
454  *
455  *      SetPidFile(pid_t pid)
456  *              Create the pid file. On failure, it checks if the process
457  *              actually exists or not. SetPidFname() should be called
458  *              in prior to calling SetPidFile().
459  *
460  *-------------------------------------------------------------------------
461  */
462
463 /*
464  * Path to pid file. proc_exit() remember it to unlink the file.
465  */
466 static char PidFile[MAXPGPATH];
467
468 /*
469  * Remove the pid file. This function is called from proc_exit.
470  */
471 void
472 UnlinkPidFile(void)
473 {
474         unlink(PidFile);
475 }
476
477 /*
478  * Set path to the pid file
479  */
480 void
481 SetPidFname(char *datadir)
482 {
483         snprintf(PidFile, sizeof(PidFile), "%s/%s", datadir, PIDFNAME);
484 }
485
486 /*
487  * Get path to the pid file
488  */
489 static char *
490 GetPidFname(void)
491 {
492         return (PidFile);
493 }
494
495 /*
496  * Create the pid file
497  */
498 int
499 SetPidFile(pid_t pid)
500 {
501         int                     fd;
502         char       *pidfile;
503         char            pidstr[32];
504         int                     len;
505         pid_t           post_pid;
506         int                     is_postgres = 0;
507
508         /*
509          * Creating pid file
510          */
511         pidfile = GetPidFname();
512         fd = open(pidfile, O_RDWR | O_CREAT | O_EXCL, 0600);
513         if (fd < 0)
514         {
515
516                 /*
517                  * Couldn't create the pid file. Probably it already exists. Read
518                  * the file to see if the process actually exists
519                  */
520                 fd = open(pidfile, O_RDONLY, 0600);
521                 if (fd < 0)
522                 {
523                         fprintf(stderr, "Can't open pid file: %s\n", pidfile);
524                         fprintf(stderr, "Please check the permission and try again.\n");
525                         return (-1);
526                 }
527                 if ((len = read(fd, pidstr, sizeof(pidstr) - 1)) < 0)
528                 {
529                         fprintf(stderr, "Can't read pid file: %s\n", pidfile);
530                         fprintf(stderr, "Please check the permission and try again.\n");
531                         close(fd);
532                         return (-1);
533                 }
534                 close(fd);
535
536                 /*
537                  * Check to see if the process actually exists
538                  */
539                 pidstr[len] = '\0';
540                 post_pid = (pid_t) atoi(pidstr);
541
542                 /* if pid < 0, the pid is for postgres, not postmatser */
543                 if (post_pid < 0)
544                 {
545                         is_postgres++;
546                         post_pid = -post_pid;
547                 }
548
549                 if (post_pid == 0 || (post_pid > 0 && kill(post_pid, 0) < 0))
550                 {
551
552                         /*
553                          * No, the process did not exist. Unlink the file and try to
554                          * create it
555                          */
556                         if (unlink(pidfile) < 0)
557                         {
558                                 fprintf(stderr, "Can't remove pid file: %s\n", pidfile);
559                                 fprintf(stderr, "The file seems accidently left, but I couldn't remove it.\n");
560                                 fprintf(stderr, "Please remove the file by hand and try again.\n");
561                                 return (-1);
562                         }
563                         fd = open(pidfile, O_RDWR | O_CREAT | O_EXCL, 0600);
564                         if (fd < 0)
565                         {
566                                 fprintf(stderr, "Can't create pid file: %s\n", pidfile);
567                                 fprintf(stderr, "Please check the permission and try again.\n");
568                                 return (-1);
569                         }
570                 }
571                 else
572                 {
573
574                         /*
575                          * Another postmaster is running
576                          */
577                         fprintf(stderr, "Can't create pid file: %s\n", pidfile);
578                         if (is_postgres)
579                                 fprintf(stderr, "Is another postgres (pid: %d) running?\n", (int) post_pid);
580                         else
581                                 fprintf(stderr, "Is another postmaster (pid: %s) running?\n", pidstr);
582                         return (-1);
583                 }
584         }
585
586         sprintf(pidstr, "%d", (int) pid);
587         if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr))
588         {
589                 fprintf(stderr, "Write to pid file failed\n");
590                 fprintf(stderr, "Please check the permission and try again.\n");
591                 close(fd);
592                 unlink(pidfile);
593                 return (-1);
594         }
595         close(fd);
596
597         return (0);
598 }
599
600
601
602 /*
603  * Determine whether the PG_VERSION file in directory `path' indicates
604  * a data version compatible with the version of this program.
605  *
606  * If compatible, return. Otherwise, elog(FATAL).
607  */
608 void
609 ValidatePgVersion(const char *path)
610 {
611         char            full_path[MAXPGPATH];
612         FILE       *file;
613         int                     ret;
614         long        file_major, file_minor;
615         long        my_major = 0, my_minor = 0;
616         char       *endptr;
617         const char *version_string = PG_VERSION;
618
619         my_major = strtol(version_string, &endptr, 10);
620         if (*endptr == '.')
621                 my_minor = strtol(endptr+1, NULL, 10);
622
623         snprintf(full_path, MAXPGPATH, "%s/PG_VERSION", path);
624
625         file = AllocateFile(full_path, "r");
626         if (!file)
627         {
628                 if (errno == ENOENT)
629                         elog(FATAL, "File %s is missing. This is not a valid data directory.", full_path);
630                 else
631                         elog(FATAL, "cannot open %s: %s", full_path, strerror(errno));
632         }
633
634         ret = fscanf(file, "%ld.%ld", &file_major, &file_minor);
635         if (ret == EOF)
636                 elog(FATAL, "cannot read %s: %s", full_path, strerror(errno));
637         else if (ret != 2)
638                 elog(FATAL, "`%s' does not have a valid format. You need to initdb.", full_path);
639
640         FreeFile(file);
641
642         if (my_major != file_major || my_minor != file_minor)
643                 elog(FATAL, "The data directory was initalized by PostgreSQL version %ld.%ld, "
644                          "which is not compatible with this verion %s.",
645                          file_major, file_minor, version_string);
646 }