11/*-------------------------------------------------------------------------
22 *
33 * exec.c
4+ *Functions for finding and validating executable files
5+ *
46 *
57 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
68 * Portions Copyright (c) 1994, Regents of the University of California
79 *
810 *
911 * IDENTIFICATION
10- * $PostgreSQL: pgsql/src/port/exec.c,v 1.31 2004/11/0601:16:22 tgl Exp $
12+ * $PostgreSQL: pgsql/src/port/exec.c,v 1.32 2004/11/0623:06:29 tgl Exp $
1113 *
1214 *-------------------------------------------------------------------------
1315 */
4749#define log_error (str ,param )(fprintf(stderr, str, param), fputc('\n', stderr))
4850#endif
4951
52+ #ifdef WIN32_CLIENT_ONLY
53+ #define getcwd (cwd ,len ) GetCurrentDirectory(len, cwd)
54+ #endif
55+
56+ static int validate_exec (const char * path );
57+ static int resolve_symlinks (char * path );
58+ static char * pipe_read_line (char * cmd ,char * line ,int maxsize );
59+
5060
5161/*
5262 * validate_exec -- validate "path" as an executable file
@@ -154,13 +164,20 @@ validate_exec(const char *path)
154164#endif
155165}
156166
167+
157168/*
158169 * find_my_exec -- find an absolute path to a valid executable
159170 *
171+ *argv0 is the name passed on the command line
172+ *retpath is the output area (must be of size MAXPGPATH)
173+ *Returns 0 if OK, -1 if error.
174+ *
160175 * The reason we have to work so hard to find an absolute path is that
161176 * on some platforms we can't do dynamic loading unless we know the
162177 * executable's location. Also, we need a full path not a relative
163- * path because we will later change working directory.
178+ * path because we will later change working directory. Finally, we want
179+ * a true path not a symlink location, so that we can locate other files
180+ * that are part of our installation relative to the executable.
164181 *
165182 * This function is not thread-safe because it calls validate_exec(),
166183 * which calls getgrgid().This function should be used only in
@@ -173,13 +190,12 @@ find_my_exec(const char *argv0, char *retpath)
173190test_path [MAXPGPATH ];
174191char * path ;
175192
176- #ifndef WIN32_CLIENT_ONLY
177193if (!getcwd (cwd ,MAXPGPATH ))
178- strcpy ( cwd , "." ); /* cheesy, but better than nothing */
179- #else
180- if (! GetCurrentDirectory ( MAXPGPATH , cwd ))
181- strcpy ( cwd , "." ); /* cheesy, but better than nothing */
182- #endif
194+ {
195+ log_error ( _ ( "could not identify current directory: %s" ),
196+ strerror ( errno ));
197+ return -1 ;
198+ }
183199
184200/*
185201 * If argv0 contains a separator, then PATH wasn't used.
@@ -193,7 +209,7 @@ find_my_exec(const char *argv0, char *retpath)
193209canonicalize_path (retpath );
194210
195211if (validate_exec (retpath )== 0 )
196- return 0 ;
212+ return resolve_symlinks ( retpath ) ;
197213
198214log_error ("invalid binary \"%s\"" ,retpath );
199215return -1 ;
@@ -203,7 +219,7 @@ find_my_exec(const char *argv0, char *retpath)
203219/* Win32 checks the current directory first for names without slashes */
204220join_path_components (retpath ,cwd ,argv0 );
205221if (validate_exec (retpath )== 0 )
206- return 0 ;
222+ return resolve_symlinks ( retpath ) ;
207223#endif
208224
209225/*
@@ -240,7 +256,7 @@ find_my_exec(const char *argv0, char *retpath)
240256switch (validate_exec (retpath ))
241257{
242258case 0 :/* found ok */
243- return 0 ;
259+ return resolve_symlinks ( retpath ) ;
244260case -1 :/* wasn't even a candidate, keep looking */
245261break ;
246262case -2 :/* found but disqualified */
@@ -254,6 +270,141 @@ find_my_exec(const char *argv0, char *retpath)
254270return -1 ;
255271}
256272
273+
274+ /*
275+ * resolve_symlinks - resolve symlinks to the underlying file
276+ *
277+ * If path does not point to a symlink, leave it alone. If it does,
278+ * replace it by the absolute path to the referenced file.
279+ *
280+ * Returns 0 if OK, -1 if error.
281+ *
282+ * Note: we are not particularly tense about producing nice error messages
283+ * because we are not really expecting error here; we just determined that
284+ * the symlink does point to a valid executable.
285+ */
286+ static int
287+ resolve_symlinks (char * path )
288+ {
289+ #ifdef HAVE_READLINK
290+ struct stat buf ;
291+ char orig_wd [MAXPGPATH ],
292+ link_buf [MAXPGPATH ];
293+ char * fname ;
294+
295+ /* Quick out if it's not a symlink */
296+ if (lstat (path ,& buf )< 0 ||
297+ (buf .st_mode & S_IFMT )!= S_IFLNK )
298+ return 0 ;
299+
300+ /*
301+ * To resolve a symlink properly, we have to chdir into its directory
302+ * and then chdir to where the symlink points; otherwise we may fail to
303+ * resolve relative links correctly (consider cases involving mount
304+ * points, for example). After following the final symlink, we use
305+ * getcwd() to figure out where the heck we're at.
306+ */
307+ if (!getcwd (orig_wd ,MAXPGPATH ))
308+ {
309+ log_error (_ ("could not identify current directory: %s" ),
310+ strerror (errno ));
311+ return -1 ;
312+ }
313+
314+ for (;;)
315+ {
316+ char * lsep ;
317+ int rllen ;
318+
319+ lsep = last_dir_separator (path );
320+ if (lsep )
321+ {
322+ * lsep = '\0' ;
323+ if (chdir (path )== -1 )
324+ {
325+ log_error (_ ("could not change directory to \"%s\"" ),path );
326+ return -1 ;
327+ }
328+ fname = lsep + 1 ;
329+ }
330+ else
331+ fname = path ;
332+
333+ if (lstat (fname ,& buf )< 0 ||
334+ (buf .st_mode & S_IFMT )!= S_IFLNK )
335+ break ;
336+
337+ rllen = readlink (fname ,link_buf ,sizeof (link_buf ));
338+ if (rllen < 0 || rllen >=sizeof (link_buf ))
339+ {
340+ log_error (_ ("could not read symbolic link \"%s\"" ),fname );
341+ return -1 ;
342+ }
343+ link_buf [rllen ]= '\0' ;
344+ strcpy (path ,link_buf );
345+ }
346+
347+ /* must copy final component out of 'path' temporarily */
348+ strcpy (link_buf ,fname );
349+
350+ if (!getcwd (path ,MAXPGPATH ))
351+ {
352+ log_error (_ ("could not identify current directory: %s" ),
353+ strerror (errno ));
354+ return -1 ;
355+ }
356+ join_path_components (path ,path ,link_buf );
357+ canonicalize_path (path );
358+
359+ if (chdir (orig_wd )== -1 )
360+ {
361+ log_error (_ ("could not change directory to \"%s\"" ),orig_wd );
362+ return -1 ;
363+ }
364+
365+ #endif /* HAVE_READLINK */
366+
367+ return 0 ;
368+ }
369+
370+
371+ /*
372+ * Find another program in our binary's directory,
373+ * then make sure it is the proper version.
374+ */
375+ int
376+ find_other_exec (const char * argv0 ,const char * target ,
377+ const char * versionstr ,char * retpath )
378+ {
379+ char cmd [MAXPGPATH ];
380+ char line [100 ];
381+
382+ if (find_my_exec (argv0 ,retpath )< 0 )
383+ return -1 ;
384+
385+ /* Trim off program name and keep just directory */
386+ * last_dir_separator (retpath )= '\0' ;
387+ canonicalize_path (retpath );
388+
389+ /* Now append the other program's name */
390+ snprintf (retpath + strlen (retpath ),MAXPGPATH - strlen (retpath ),
391+ "/%s%s" ,target ,EXE );
392+
393+ if (validate_exec (retpath )!= 0 )
394+ return -1 ;
395+
396+ snprintf (cmd ,sizeof (cmd ),"\"%s\" -V 2>%s" ,retpath ,DEVNULL );
397+
398+ if (!pipe_read_line (cmd ,line ,sizeof (line )))
399+ return -1 ;
400+
401+ if (strcmp (line ,versionstr )!= 0 )
402+ return -2 ;
403+
404+ return 0 ;
405+ }
406+
407+
257408/*
258409 * The runtime library's popen() on win32 does not work when being
259410 * called from a service when running on windows <= 2000, because
@@ -262,7 +413,6 @@ find_my_exec(const char *argv0, char *retpath)
262413 * Executing a command in a pipe and reading the first line from it
263414 * is all we need.
264415 */
265-
266416static char *
267417pipe_read_line (char * cmd ,char * line ,int maxsize )
268418{
@@ -286,8 +436,9 @@ pipe_read_line(char *cmd, char *line, int maxsize)
286436return NULL ;
287437
288438return line ;
289- #else
290- /* Win32 */
439+
440+ #else /* WIN32 */
441+
291442SECURITY_ATTRIBUTES sattr ;
292443HANDLE childstdoutrd ,
293444childstdoutwr ,
@@ -392,44 +543,7 @@ pipe_read_line(char *cmd, char *line, int maxsize)
392543CloseHandle (childstdoutrddup );
393544
394545return retval ;
395- #endif
396- }
397-
398-
399- /*
400- * Find another program in our binary's directory,
401- * then make sure it is the proper version.
402- */
403- int
404- find_other_exec (const char * argv0 ,const char * target ,
405- const char * versionstr ,char * retpath )
406- {
407- char cmd [MAXPGPATH ];
408- char line [100 ];
409-
410- if (find_my_exec (argv0 ,retpath )< 0 )
411- return -1 ;
412-
413- /* Trim off program name and keep just directory */
414- * last_dir_separator (retpath )= '\0' ;
415- canonicalize_path (retpath );
416-
417- /* Now append the other program's name */
418- snprintf (retpath + strlen (retpath ),MAXPGPATH - strlen (retpath ),
419- "/%s%s" ,target ,EXE );
420-
421- if (validate_exec (retpath ))
422- return -1 ;
423-
424- snprintf (cmd ,sizeof (cmd ),"\"%s\" -V 2>%s" ,retpath ,DEVNULL );
425-
426- if (!pipe_read_line (cmd ,line ,sizeof (line )))
427- return -1 ;
428-
429- if (strcmp (line ,versionstr )!= 0 )
430- return -2 ;
431-
432- return 0 ;
546+ #endif /* WIN32 */
433547}
434548
435549
@@ -454,20 +568,14 @@ pclose_check(FILE *stream)
454568perror ("pclose failed" );
455569}
456570else if (WIFEXITED (exitstatus ))
457- {
458571log_error (_ ("child process exited with exit code %d" ),
459572WEXITSTATUS (exitstatus ));
460- }
461573else if (WIFSIGNALED (exitstatus ))
462- {
463574log_error (_ ("child process was terminated by signal %d" ),
464575WTERMSIG (exitstatus ));
465- }
466576else
467- {
468577log_error (_ ("child process exited with unrecognized status %d" ),
469578exitstatus );
470- }
471579
472580return -1 ;
473581}