]> granicus.if.org Git - postgresql/blob - src/backend/utils/init/findbe.c
Add code to allow profiling of backends on Linux: save and restore the
[postgresql] / src / backend / utils / init / findbe.c
1 /*-------------------------------------------------------------------------
2  *
3  * findbe.c
4  *
5  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/findbe.c,v 1.26 2002/02/08 16:30:11 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include <sys/types.h>
17 #include <grp.h>
18 #include <pwd.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21
22 #include "miscadmin.h"
23
24 #ifndef S_IRUSR                                 /* XXX [TRH] should be in a header */
25 #define S_IRUSR          S_IREAD
26 #define S_IWUSR          S_IWRITE
27 #define S_IXUSR          S_IEXEC
28 #define S_IRGRP          ((S_IRUSR)>>3)
29 #define S_IWGRP          ((S_IWUSR)>>3)
30 #define S_IXGRP          ((S_IXUSR)>>3)
31 #define S_IROTH          ((S_IRUSR)>>6)
32 #define S_IWOTH          ((S_IWUSR)>>6)
33 #define S_IXOTH          ((S_IXUSR)>>6)
34 #endif
35
36
37 /*
38  * ValidateBinary -- validate "path" as a POSTMASTER/POSTGRES executable file
39  *
40  * returns 0 if the file is found and no error is encountered.
41  *                -1 if the regular file "path" does not exist or cannot be executed.
42  *                -2 if the file is otherwise valid but cannot be read.
43  */
44 static int
45 ValidateBinary(char *path)
46 {
47         struct stat buf;
48         uid_t           euid;
49         struct group *gp;
50         struct passwd *pwp;
51         int                     i;
52         int                     is_r = 0;
53         int                     is_x = 0;
54         int                     in_grp = 0;
55
56         /*
57          * Ensure that the file exists and is a regular file.
58          *
59          * XXX if you have a broken system where stat() looks at the symlink
60          * instead of the underlying file, you lose.
61          */
62         if (stat(path, &buf) < 0)
63         {
64                 if (DebugLvl > 1)
65                         fprintf(stderr, "ValidateBinary: can't stat \"%s\"\n",
66                                         path);
67                 return -1;
68         }
69
70         if ((buf.st_mode & S_IFMT) != S_IFREG)
71         {
72                 if (DebugLvl > 1)
73                         fprintf(stderr, "ValidateBinary: \"%s\" is not a regular file\n",
74                                         path);
75                 return -1;
76         }
77
78         /*
79          * Ensure that we are using an authorized backend.
80          *
81          * XXX I'm open to suggestions here.  I would like to enforce ownership
82          * of binaries by user "postgres" but people seem to like to run as
83          * users other than "postgres"...
84          */
85
86         /*
87          * Ensure that the file is both executable and readable (required for
88          * dynamic loading).
89          */
90         euid = geteuid();
91         if (euid == buf.st_uid)
92         {
93                 is_r = buf.st_mode & S_IRUSR;
94                 is_x = buf.st_mode & S_IXUSR;
95                 if (DebugLvl > 1 && !(is_r && is_x))
96                         fprintf(stderr, "ValidateBinary: \"%s\" is not user read/execute\n",
97                                         path);
98                 return is_x ? (is_r ? 0 : -2) : -1;
99         }
100         pwp = getpwuid(euid);
101         if (pwp)
102         {
103                 if (pwp->pw_gid == buf.st_gid)
104                         ++in_grp;
105                 else if (pwp->pw_name &&
106                                  (gp = getgrgid(buf.st_gid)) != NULL &&
107                                  gp->gr_mem != NULL)
108                 {
109                         for (i = 0; gp->gr_mem[i]; ++i)
110                         {
111                                 if (!strcmp(gp->gr_mem[i], pwp->pw_name))
112                                 {
113                                         ++in_grp;
114                                         break;
115                                 }
116                         }
117                 }
118                 if (in_grp)
119                 {
120                         is_r = buf.st_mode & S_IRGRP;
121                         is_x = buf.st_mode & S_IXGRP;
122                         if (DebugLvl > 1 && !(is_r && is_x))
123                                 fprintf(stderr, "ValidateBinary: \"%s\" is not group read/execute\n",
124                                                 path);
125                         return is_x ? (is_r ? 0 : -2) : -1;
126                 }
127         }
128         is_r = buf.st_mode & S_IROTH;
129         is_x = buf.st_mode & S_IXOTH;
130         if (DebugLvl > 1 && !(is_r && is_x))
131                 fprintf(stderr, "ValidateBinary: \"%s\" is not other read/execute\n",
132                                 path);
133         return is_x ? (is_r ? 0 : -2) : -1;
134 }
135
136 /*
137  * FindExec -- find an absolute path to a valid backend executable
138  *
139  * The reason we have to work so hard to find an absolute path is that
140  * on some platforms we can't do dynamic loading unless we know the
141  * executable's location.  Also, we need a full path not a relative
142  * path because we will later change working directory.
143  */
144 int
145 FindExec(char *full_path, const char *argv0, const char *binary_name)
146 {
147         char            buf[MAXPGPATH + 2];
148         char       *p;
149         char       *path,
150                            *startp,
151                            *endp;
152
153         /*
154          * for the postmaster: First try: use the binary that's located in the
155          * same directory as the postmaster, if it was invoked with an
156          * explicit path. Presumably the user used an explicit path because it
157          * wasn't in PATH, and we don't want to use incompatible executables.
158          *
159          * This has the neat property that it works for installed binaries, old
160          * source trees (obj/support/post{master,gres}) and new marc source
161          * trees (obj/post{master,gres}) because they all put the two binaries
162          * in the same place.
163          *
164          * for the binary: First try: if we're given some kind of path, use it
165          * (making sure that a relative path is made absolute before returning
166          * it).
167          */
168         if (argv0 && (p = strrchr(argv0, '/')) && *++p)
169         {
170                 if (*argv0 == '/' || !getcwd(buf, MAXPGPATH))
171                         buf[0] = '\0';
172                 else
173                         strcat(buf, "/");
174                 strcat(buf, argv0);
175                 p = strrchr(buf, '/');
176                 strcpy(++p, binary_name);
177                 if (ValidateBinary(buf) == 0)
178                 {
179                         strncpy(full_path, buf, MAXPGPATH);
180                         if (DebugLvl)
181                                 fprintf(stderr, "FindExec: found \"%s\" using argv[0]\n",
182                                                 full_path);
183                         return 0;
184                 }
185                 fprintf(stderr, "FindExec: invalid binary \"%s\"\n",
186                                 buf);
187                 return -1;
188         }
189
190         /*
191          * Second try: since no explicit path was supplied, the user must have
192          * been relying on PATH.  We'll use the same PATH.
193          */
194         if ((p = getenv("PATH")) && *p)
195         {
196                 if (DebugLvl)
197                         fprintf(stderr, "FindExec: searching PATH ...\n");
198                 path = strdup(p);               /* make a modifiable copy */
199                 for (startp = path, endp = strchr(path, ':');
200                          startp && *startp;
201                          startp = endp + 1, endp = strchr(startp, ':'))
202                 {
203                         if (startp == endp) /* it's a "::" */
204                                 continue;
205                         if (endp)
206                                 *endp = '\0';
207                         if (*startp == '/' || !getcwd(buf, MAXPGPATH))
208                                 buf[0] = '\0';
209                         else
210                                 strcat(buf, "/");
211                         strcat(buf, startp);
212                         strcat(buf, "/");
213                         strcat(buf, binary_name);
214                         switch (ValidateBinary(buf))
215                         {
216                                 case 0: /* found ok */
217                                         strncpy(full_path, buf, MAXPGPATH);
218                                         if (DebugLvl)
219                                                 fprintf(stderr, "FindExec: found \"%s\" using PATH\n",
220                                                                 full_path);
221                                         free(path);
222                                         return 0;
223                                 case -1:                /* wasn't even a candidate, keep looking */
224                                         break;
225                                 case -2:                /* found but disqualified */
226                                         fprintf(stderr, "FindExec: could not read binary \"%s\"\n",
227                                                         buf);
228                                         free(path);
229                                         return -1;
230                         }
231                         if (!endp)                      /* last one */
232                                 break;
233                 }
234                 free(path);
235         }
236
237         fprintf(stderr, "FindExec: could not find a %s to execute...\n", binary_name);
238         return -1;
239 }