]> granicus.if.org Git - postgresql/blob - src/backend/utils/init/miscinit.c
Version number now set in configure, available through Makefile.global
[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.51 2000/07/02 15:20:56 petere 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                         if (dest)
264                                 *buff = RecodeForwTable[*buff - 128];
265                         else
266                                 *buff = RecodeBackTable[*buff - 128];
267         }
268         return ch;
269 }
270
271 #endif
272
273 /* ----------------
274  *              GetPgUserName and SetPgUserName
275  *
276  *              SetPgUserName must be called before InitPostgres, since the setuid()
277  *              is done there.
278  *
279  *              Replace GetPgUserName() with a lower-case version
280  *              to allow use in new case-insensitive SQL (referenced
281  *              in pg_proc.h). Define GetPgUserName() as a macro - tgl 97/04/26
282  * ----------------
283  */
284 char *
285 getpgusername()
286 {
287         return UserName;
288 }
289
290 void
291 SetPgUserName()
292 {
293 #ifndef NO_SECURITY
294         char       *p;
295         struct passwd *pw;
296
297         if (IsUnderPostmaster)
298         {
299                 /* use the (possibly) authenticated name that's provided */
300                 if (!(p = getenv("PG_USER")))
301                         elog(FATAL, "SetPgUserName: PG_USER environment variable is unset");
302         }
303         else
304         {
305                 /* setuid() has not yet been done, see above comment */
306                 if (!(pw = getpwuid(geteuid())))
307                         elog(FATAL, "SetPgUserName: no entry in host passwd file");
308                 p = pw->pw_name;
309         }
310         if (UserName)
311                 free(UserName);
312         UserName = malloc(strlen(p) + 1);
313         strcpy(UserName, p);
314 #endif   /* NO_SECURITY */
315 }
316
317 /* ----------------------------------------------------------------
318  *              GetUserId and SetUserId
319  * ----------------------------------------------------------------
320  */
321 static Oid      UserId = InvalidOid;
322
323 int
324 GetUserId()
325 {
326         AssertState(OidIsValid(UserId));
327         return UserId;
328 }
329
330 void
331 SetUserId()
332 {
333         HeapTuple       userTup;
334         char       *userName;
335
336         AssertState(!OidIsValid(UserId));       /* only once */
337
338         /*
339          * Don't do scans if we're bootstrapping, none of the system catalogs
340          * exist yet, and they should be owned by postgres anyway.
341          */
342         if (IsBootstrapProcessingMode())
343         {
344                 UserId = geteuid();
345                 return;
346         }
347
348         userName = GetPgUserName();
349         userTup = SearchSysCacheTuple(SHADOWNAME,
350                                                                   PointerGetDatum(userName),
351                                                                   0, 0, 0);
352         if (!HeapTupleIsValid(userTup))
353                 elog(FATAL, "SetUserId: user '%s' is not in '%s'",
354                          userName,
355                          ShadowRelationName);
356         UserId = (Oid) ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid;
357 }
358
359 /*-------------------------------------------------------------------------
360  *
361  * posmaster pid file stuffs. $DATADIR/postmaster.pid is created when:
362  *
363  *      (1) postmaster starts. In this case pid > 0.
364  *      (2) postgres starts in standalone mode. In this case
365  *              pid < 0
366  *
367  * to gain an interlock.
368  *
369  *      SetPidFname(datadir)
370  *              Remember the the pid file name. This is neccesary
371  *              UnlinkPidFile() is called from proc_exit().
372  *
373  *      GetPidFname(datadir)
374  *              Get the pid file name. SetPidFname() should be called
375  *              before GetPidFname() gets called.
376  *
377  *      UnlinkPidFile()
378  *              This is called from proc_exit() and unlink the pid file.
379  *
380  *      SetPidFile(pid_t pid)
381  *              Create the pid file. On failure, it checks if the process
382  *              actually exists or not. SetPidFname() should be called
383  *              in prior to calling SetPidFile().
384  *
385  *-------------------------------------------------------------------------
386  */
387
388 /*
389  * Path to pid file. proc_exit() remember it to unlink the file.
390  */
391 static char PidFile[MAXPGPATH];
392
393 /*
394  * Remove the pid file. This function is called from proc_exit.
395  */
396 void
397 UnlinkPidFile(void)
398 {
399         unlink(PidFile);
400 }
401
402 /*
403  * Set path to the pid file
404  */
405 void
406 SetPidFname(char *datadir)
407 {
408         snprintf(PidFile, sizeof(PidFile), "%s/%s", datadir, PIDFNAME);
409 }
410
411 /*
412  * Get path to the pid file
413  */
414 static char *
415 GetPidFname(void)
416 {
417         return (PidFile);
418 }
419
420 /*
421  * Create the pid file
422  */
423 int
424 SetPidFile(pid_t pid)
425 {
426         int                     fd;
427         char       *pidfile;
428         char            pidstr[32];
429         int                     len;
430         pid_t           post_pid;
431         int                     is_postgres = 0;
432
433         /*
434          * Creating pid file
435          */
436         pidfile = GetPidFname();
437         fd = open(pidfile, O_RDWR | O_CREAT | O_EXCL, 0600);
438         if (fd < 0)
439         {
440
441                 /*
442                  * Couldn't create the pid file. Probably it already exists. Read
443                  * the file to see if the process actually exists
444                  */
445                 fd = open(pidfile, O_RDONLY, 0600);
446                 if (fd < 0)
447                 {
448                         fprintf(stderr, "Can't open pid file: %s\n", pidfile);
449                         fprintf(stderr, "Please check the permission and try again.\n");
450                         return (-1);
451                 }
452                 if ((len = read(fd, pidstr, sizeof(pidstr) - 1)) < 0)
453                 {
454                         fprintf(stderr, "Can't read pid file: %s\n", pidfile);
455                         fprintf(stderr, "Please check the permission and try again.\n");
456                         close(fd);
457                         return (-1);
458                 }
459                 close(fd);
460
461                 /*
462                  * Check to see if the process actually exists
463                  */
464                 pidstr[len] = '\0';
465                 post_pid = (pid_t) atoi(pidstr);
466
467                 /* if pid < 0, the pid is for postgres, not postmatser */
468                 if (post_pid < 0)
469                 {
470                         is_postgres++;
471                         post_pid = -post_pid;
472                 }
473
474                 if (post_pid == 0 || (post_pid > 0 && kill(post_pid, 0) < 0))
475                 {
476
477                         /*
478                          * No, the process did not exist. Unlink the file and try to
479                          * create it
480                          */
481                         if (unlink(pidfile) < 0)
482                         {
483                                 fprintf(stderr, "Can't remove pid file: %s\n", pidfile);
484                                 fprintf(stderr, "The file seems accidently left, but I couldn't remove it.\n");
485                                 fprintf(stderr, "Please remove the file by hand and try again.\n");
486                                 return (-1);
487                         }
488                         fd = open(pidfile, O_RDWR | O_CREAT | O_EXCL, 0600);
489                         if (fd < 0)
490                         {
491                                 fprintf(stderr, "Can't create pid file: %s\n", pidfile);
492                                 fprintf(stderr, "Please check the permission and try again.\n");
493                                 return (-1);
494                         }
495                 }
496                 else
497                 {
498
499                         /*
500                          * Another postmaster is running
501                          */
502                         fprintf(stderr, "Can't create pid file: %s\n", pidfile);
503                         if (is_postgres)
504                                 fprintf(stderr, "Is another postgres (pid: %d) running?\n", (int) post_pid);
505                         else
506                                 fprintf(stderr, "Is another postmaster (pid: %s) running?\n", pidstr);
507                         return (-1);
508                 }
509         }
510
511         sprintf(pidstr, "%d", (int) pid);
512         if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr))
513         {
514                 fprintf(stderr, "Write to pid file failed\n");
515                 fprintf(stderr, "Please check the permission and try again.\n");
516                 close(fd);
517                 unlink(pidfile);
518                 return (-1);
519         }
520         close(fd);
521
522         return (0);
523 }
524
525
526
527 /*
528  * Determine whether the PG_VERSION file in directory `path' indicates
529  * a data version compatible with the version of this program.
530  *
531  * If compatible, return. Otherwise, elog(FATAL).
532  */
533 void
534 ValidatePgVersion(const char *path)
535 {
536         char            full_path[MAXPGPATH];
537         FILE       *file;
538         int                     ret;
539         long        file_major, file_minor;
540         long        my_major = 0, my_minor = 0;
541         char       *endptr;
542         const char *version_string = PG_VERSION;
543
544         my_major = strtol(version_string, &endptr, 10);
545         if (*endptr == '.')
546                 my_minor = strtol(endptr+1, NULL, 10);
547
548         snprintf(full_path, MAXPGPATH, "%s/PG_VERSION", path);
549
550         file = AllocateFile(full_path, "r");
551         if (!file)
552         {
553                 if (errno == ENOENT)
554                         elog(FATAL, "File %s is missing. This is not a valid data directory.", full_path);
555                 else
556                         elog(FATAL, "cannot open %s: %s", full_path, strerror(errno));
557         }
558
559         ret = fscanf(file, "%ld.%ld", &file_major, &file_minor);
560         if (ret == EOF)
561                 elog(FATAL, "cannot read %s: %s", full_path, strerror(errno));
562         else if (ret != 2)
563                 elog(FATAL, "`%s' does not have a valid format. You need to initdb.", full_path);
564
565         FreeFile(file);
566
567         if (my_major != file_major || my_minor != file_minor)
568                 elog(FATAL, "The data directory was initalized by PostgreSQL version %ld.%ld, "
569                          "which is not compatible with this verion %s.",
570                          file_major, file_minor, version_string);
571 }