1 /*-------------------------------------------------------------------------
5 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994, Regents of the University of California
10 * $PostgreSQL: pgsql/src/port/exec.c,v 1.3 2004/05/13 01:47:12 momjian Exp $
12 *-------------------------------------------------------------------------
18 #include "postgres_fe.h"
26 #include "miscadmin.h"
28 #ifndef S_IRUSR /* XXX [TRH] should be in a header */
29 #define S_IRUSR S_IREAD
30 #define S_IWUSR S_IWRITE
31 #define S_IXUSR S_IEXEC
32 #define S_IRGRP ((S_IRUSR)>>3)
33 #define S_IWGRP ((S_IWUSR)>>3)
34 #define S_IXGRP ((S_IXUSR)>>3)
35 #define S_IROTH ((S_IRUSR)>>6)
36 #define S_IWOTH ((S_IWUSR)>>6)
37 #define S_IXOTH ((S_IXUSR)>>6)
41 /* We use only 3-parameter elog calls in this file, for simplicity */
42 #define log_debug(str, param) elog(DEBUG2, str, param)
44 #define log_debug(str, param) {} /* do nothing */
47 static void win32_make_absolute(char *path);
50 * validate_exec -- validate "path" as an executable file
52 * returns 0 if the file is found and no error is encountered.
53 * -1 if the regular file "path" does not exist or cannot be executed.
54 * -2 if the file is otherwise valid but cannot be read.
57 validate_exec(char *path)
68 char path_exe[MAXPGPATH + 2 + strlen(".exe")];
74 /* Win32 requires a .exe suffix for stat() */
75 if (strlen(path) >= strlen(".exe") &&
76 pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
78 strcpy(path_exe, path);
79 strcat(path_exe, ".exe");
85 * Ensure that the file exists and is a regular file.
87 * XXX if you have a broken system where stat() looks at the symlink
88 * instead of the underlying file, you lose.
90 if (stat(path, &buf) < 0)
92 log_debug("could not stat \"%s\": %m", path);
96 if ((buf.st_mode & S_IFMT) != S_IFREG)
98 log_debug("\"%s\" is not a regular file", path);
103 * Ensure that we are using an authorized executable.
107 * Ensure that the file is both executable and readable (required for
111 is_r = buf.st_mode & S_IRUSR;
112 is_x = buf.st_mode & S_IXUSR;
113 return is_x ? (is_r ? 0 : -2) : -1;
117 /* If owned by us, just check owner bits */
118 if (euid == buf.st_uid)
120 is_r = buf.st_mode & S_IRUSR;
121 is_x = buf.st_mode & S_IXUSR;
123 log_debug("\"%s\" is not user read/execute", path);
124 return is_x ? (is_r ? 0 : -2) : -1;
127 /* OK, check group bits */
129 pwp = getpwuid(euid); /* not thread-safe */
132 if (pwp->pw_gid == buf.st_gid) /* my primary group? */
134 else if (pwp->pw_name &&
135 (gp = getgrgid(buf.st_gid)) != NULL && /* not thread-safe */
137 { /* try list of member groups */
138 for (i = 0; gp->gr_mem[i]; ++i)
140 if (!strcmp(gp->gr_mem[i], pwp->pw_name))
149 is_r = buf.st_mode & S_IRGRP;
150 is_x = buf.st_mode & S_IXGRP;
152 log_debug("\"%s\" is not group read/execute", path);
153 return is_x ? (is_r ? 0 : -2) : -1;
157 /* Check "other" bits */
158 is_r = buf.st_mode & S_IROTH;
159 is_x = buf.st_mode & S_IXOTH;
161 log_debug("\"%s\" is not other read/execute", path);
162 return is_x ? (is_r ? 0 : -2) : -1;
168 * find_my_exec -- find an absolute path to a valid executable
170 * The reason we have to work so hard to find an absolute path is that
171 * on some platforms we can't do dynamic loading unless we know the
172 * executable's location. Also, we need a full path not a relative
173 * path because we will later change working directory.
175 * This function is not thread-safe because of it calls validate_exec(),
176 * which calls getgrgid(). This function should be used only in
177 * non-threaded binaries, not in library routines.
180 find_my_exec(char *full_path, const char *argv0)
182 char buf[MAXPGPATH + 2];
187 const char *binary_name = get_progname(argv0);
190 * First try: use the binary that's located in the
191 * same directory if it was invoked with an explicit path.
192 * Presumably the user used an explicit path because it
193 * wasn't in PATH, and we don't want to use incompatible executables.
195 * This has the neat property that it works for installed binaries, old
196 * source trees (obj/support/post{master,gres}) and new source
197 * trees (obj/post{master,gres}) because they all put the two binaries
200 * for the binary: First try: if we're given some kind of path, use it
201 * (making sure that a relative path is made absolute before returning
204 if (argv0 && (p = last_path_separator(argv0)) && *++p)
206 if (is_absolute_path(argv0) || !getcwd(buf, MAXPGPATH))
211 p = last_path_separator(buf);
212 strcpy(++p, binary_name);
213 if (validate_exec(buf) == 0)
215 strncpy(full_path, buf, MAXPGPATH);
216 win32_make_absolute(full_path);
217 log_debug("found \"%s\" using argv[0]", full_path);
220 log_debug("invalid binary \"%s\"", buf);
225 * Second try: since no explicit path was supplied, the user must have
226 * been relying on PATH. We'll use the same PATH.
228 if ((p = getenv("PATH")) && *p)
230 log_debug("searching PATH for executable%s", "");
231 path = strdup(p); /* make a modifiable copy */
232 for (startp = path, endp = strchr(path, PATHSEP);
234 startp = endp + 1, endp = strchr(startp, PATHSEP))
236 if (startp == endp) /* it's a "::" */
240 if (is_absolute_path(startp) || !getcwd(buf, MAXPGPATH))
246 strcat(buf, binary_name);
247 switch (validate_exec(buf))
249 case 0: /* found ok */
250 strncpy(full_path, buf, MAXPGPATH);
251 win32_make_absolute(full_path);
252 log_debug("found \"%s\" using PATH", full_path);
255 case -1: /* wasn't even a candidate, keep looking */
257 case -2: /* found but disqualified */
258 log_debug("could not read binary \"%s\"", buf);
262 if (!endp) /* last one */
268 log_debug("could not find a \"%s\" to execute", binary_name);
274 * Find our binary directory, then make sure the "target" executable
275 * is the proper version.
277 int find_other_exec(char *retpath, const char *argv0,
278 char const *target, const char *versionstr)
284 if (find_my_exec(retpath, argv0) < 0)
287 /* Trim off program name and keep just directory */
288 *last_path_separator(retpath) = '\0';
290 snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
291 "/%s%s", target, EXE);
293 if (validate_exec(retpath))
296 snprintf(cmd, sizeof(cmd), "\"%s\" -V 2>%s", retpath, DEVNULL);
298 /* flush output buffers in case popen does not... */
302 if ((pgver = popen(cmd, "r")) == NULL)
305 if (fgets(line, sizeof(line), pgver) == NULL)
306 perror("fgets failure");
308 if (pclose_check(pgver))
311 if (strcmp(line, versionstr) != 0)
319 * Windows doesn't like relative paths to executables (other things work fine)
320 * so we call its builtin function to expand them. Elsewhere this is a NOOP
322 * Returns malloc'ed memory.
325 win32_make_absolute(char *path)
328 char abspath[MAXPGPATH];
330 if (_fullpath(abspath, path, MAXPGPATH) == NULL)
332 log_debug("Win32 path expansion failed: %s", strerror(errno));
335 canonicalize_path(abspath);
337 StrNCpy(path, abspath, MAXPGPATH);