Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions

exec.c File Reference

#include "postgres.h"
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
Include dependency graph for exec.c:

Go to the source code of this file.

Defines

#define log_error(str, param)   elog(LOG, str, param)
#define log_error4(str, param, arg1)   elog(LOG, str, param, arg1)

Functions

static int validate_exec (const char *path)
static int resolve_symlinks (char *path)
static char * pipe_read_line (char *cmd, char *line, int maxsize)
int find_my_exec (const char *argv0, char *retpath)
int find_other_exec (const char *argv0, const char *target, const char *versionstr, char *retpath)
int pclose_check (FILE *stream)
void set_pglocale_pgservice (const char *argv0, const char *app)

Define Documentation

#define log_error (   str,
  param 
)    elog(LOG, str, param)

Definition at line 31 of file exec.c.

Referenced by find_my_exec(), pclose_check(), and resolve_symlinks().

#define log_error4 (   str,
  param,
  arg1 
)    elog(LOG, str, param, arg1)

Definition at line 32 of file exec.c.

Referenced by resolve_symlinks().


Function Documentation

int find_my_exec ( const char *  argv0,
char *  retpath 
)

Definition at line 119 of file exec.c.

References _, canonicalize_path(), first_dir_separator(), first_path_var_separator(), is_absolute_path, join_path_components(), log_error, MAXPGPATH, Min, NULL, resolve_symlinks(), strerror(), StrNCpy, and validate_exec().

Referenced by AuxiliaryProcessMain(), find_other_exec(), find_other_exec_or_die(), getInstallationPaths(), main(), PostgresMain(), process_psqlrc(), set_pglocale_pgservice(), setup(), and setup_bin_paths().

{
    char        cwd[MAXPGPATH],
                test_path[MAXPGPATH];
    char       *path;

    if (!getcwd(cwd, MAXPGPATH))
    {
        log_error(_("could not identify current directory: %s"),
                  strerror(errno));
        return -1;
    }

    /*
     * If argv0 contains a separator, then PATH wasn't used.
     */
    if (first_dir_separator(argv0) != NULL)
    {
        if (is_absolute_path(argv0))
            StrNCpy(retpath, argv0, MAXPGPATH);
        else
            join_path_components(retpath, cwd, argv0);
        canonicalize_path(retpath);

        if (validate_exec(retpath) == 0)
            return resolve_symlinks(retpath);

        log_error(_("invalid binary \"%s\""), retpath);
        return -1;
    }

#ifdef WIN32
    /* Win32 checks the current directory first for names without slashes */
    join_path_components(retpath, cwd, argv0);
    if (validate_exec(retpath) == 0)
        return resolve_symlinks(retpath);
#endif

    /*
     * Since no explicit path was supplied, the user must have been relying on
     * PATH.  We'll search the same PATH.
     */
    if ((path = getenv("PATH")) && *path)
    {
        char       *startp = NULL,
                   *endp = NULL;

        do
        {
            if (!startp)
                startp = path;
            else
                startp = endp + 1;

            endp = first_path_var_separator(startp);
            if (!endp)
                endp = startp + strlen(startp); /* point to end */

            StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));

            if (is_absolute_path(test_path))
                join_path_components(retpath, test_path, argv0);
            else
            {
                join_path_components(retpath, cwd, test_path);
                join_path_components(retpath, retpath, argv0);
            }
            canonicalize_path(retpath);

            switch (validate_exec(retpath))
            {
                case 0: /* found ok */
                    return resolve_symlinks(retpath);
                case -1:        /* wasn't even a candidate, keep looking */
                    break;
                case -2:        /* found but disqualified */
                    log_error(_("could not read binary \"%s\""),
                              retpath);
                    break;
            }
        } while (*endp);
    }

    log_error(_("could not find a \"%s\" to execute"), argv0);
    return -1;
}

int find_other_exec ( const char *  argv0,
const char *  target,
const char *  versionstr,
char *  retpath 
)

Definition at line 307 of file exec.c.

References canonicalize_path(), EXE, find_my_exec(), last_dir_separator(), MAXPGPATH, pipe_read_line(), snprintf(), and validate_exec().

Referenced by find_other_exec_or_die(), getInstallationPaths(), main(), and setup_bin_paths().

{
    char        cmd[MAXPGPATH];
    char        line[100];

    if (find_my_exec(argv0, retpath) < 0)
        return -1;

    /* Trim off program name and keep just directory */
    *last_dir_separator(retpath) = '\0';
    canonicalize_path(retpath);

    /* Now append the other program's name */
    snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
             "/%s%s", target, EXE);

    if (validate_exec(retpath) != 0)
        return -1;

    snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);

    if (!pipe_read_line(cmd, line, sizeof(line)))
        return -1;

    if (strcmp(line, versionstr) != 0)
        return -2;

    return 0;
}

int pclose_check ( FILE *  stream  ) 

Definition at line 510 of file exec.c.

References _, free, log_error, pfree(), strerror(), and wait_result_to_str().

Referenced by pipe_read_line().

{
    int         exitstatus;
    char       *reason;

    exitstatus = pclose(stream);

    if (exitstatus == 0)
        return 0;               /* all is well */

    if (exitstatus == -1)
    {
        /* pclose() itself failed, and hopefully set errno */
        log_error(_("pclose failed: %s"), strerror(errno));
    }
    else
    {
        reason = wait_result_to_str(exitstatus);
        log_error("%s", reason);
#ifdef FRONTEND
        free(reason);
#else
        pfree(reason);
#endif
    }
    return exitstatus;
}

static char * pipe_read_line ( char *  cmd,
char *  line,
int  maxsize 
) [static]

Definition at line 348 of file exec.c.

References FALSE, NULL, pclose_check(), and TRUE.

Referenced by find_other_exec().

{
#ifndef WIN32
    FILE       *pgver;

    /* flush output buffers in case popen does not... */
    fflush(stdout);
    fflush(stderr);

    errno = 0;
    if ((pgver = popen(cmd, "r")) == NULL)
    {
        perror("popen failure");
        return NULL;
    }

    errno = 0;
    if (fgets(line, maxsize, pgver) == NULL)
    {
        if (feof(pgver))
            fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
        else
            perror("fgets failure");
        pclose(pgver);          /* no error checking */
        return NULL;
    }

    if (pclose_check(pgver))
        return NULL;

    return line;
#else                           /* WIN32 */

    SECURITY_ATTRIBUTES sattr;
    HANDLE      childstdoutrd,
                childstdoutwr,
                childstdoutrddup;
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    char       *retval = NULL;

    sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
    sattr.bInheritHandle = TRUE;
    sattr.lpSecurityDescriptor = NULL;

    if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
        return NULL;

    if (!DuplicateHandle(GetCurrentProcess(),
                         childstdoutrd,
                         GetCurrentProcess(),
                         &childstdoutrddup,
                         0,
                         FALSE,
                         DUPLICATE_SAME_ACCESS))
    {
        CloseHandle(childstdoutrd);
        CloseHandle(childstdoutwr);
        return NULL;
    }

    CloseHandle(childstdoutrd);

    ZeroMemory(&pi, sizeof(pi));
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdError = childstdoutwr;
    si.hStdOutput = childstdoutwr;
    si.hStdInput = INVALID_HANDLE_VALUE;

    if (CreateProcess(NULL,
                      cmd,
                      NULL,
                      NULL,
                      TRUE,
                      0,
                      NULL,
                      NULL,
                      &si,
                      &pi))
    {
        /* Successfully started the process */
        char       *lineptr;

        ZeroMemory(line, maxsize);

        /* Try to read at least one line from the pipe */
        /* This may require more than one wait/read attempt */
        for (lineptr = line; lineptr < line + maxsize - 1;)
        {
            DWORD       bytesread = 0;

            /* Let's see if we can read */
            if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
                break;          /* Timeout, but perhaps we got a line already */

            if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
                          &bytesread, NULL))
                break;          /* Error, but perhaps we got a line already */

            lineptr += strlen(lineptr);

            if (!bytesread)
                break;          /* EOF */

            if (strchr(line, '\n'))
                break;          /* One or more lines read */
        }

        if (lineptr != line)
        {
            /* OK, we read some data */
            int         len;

            /* If we got more than one line, cut off after the first \n */
            lineptr = strchr(line, '\n');
            if (lineptr)
                *(lineptr + 1) = '\0';

            len = strlen(line);

            /*
             * If EOL is \r\n, convert to just \n. Because stdout is a
             * text-mode stream, the \n output by the child process is
             * received as \r\n, so we convert it to \n.  The server main.c
             * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
             * disabling \n to \r\n expansion for stdout.
             */
            if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
            {
                line[len - 2] = '\n';
                line[len - 1] = '\0';
                len--;
            }

            /*
             * We emulate fgets() behaviour. So if there is no newline at the
             * end, we add one...
             */
            if (len == 0 || line[len - 1] != '\n')
                strcat(line, "\n");

            retval = line;
        }

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }

    CloseHandle(childstdoutwr);
    CloseHandle(childstdoutrddup);

    return retval;
#endif   /* WIN32 */
}

static int resolve_symlinks ( char *  path  )  [static]

Definition at line 219 of file exec.c.

References _, canonicalize_path(), join_path_components(), last_dir_separator(), log_error, log_error4, lstat, MAXPGPATH, and strerror().

Referenced by find_my_exec().

{
#ifdef HAVE_READLINK
    struct stat buf;
    char        orig_wd[MAXPGPATH],
                link_buf[MAXPGPATH];
    char       *fname;

    /*
     * To resolve a symlink properly, we have to chdir into its directory and
     * then chdir to where the symlink points; otherwise we may fail to
     * resolve relative links correctly (consider cases involving mount
     * points, for example).  After following the final symlink, we use
     * getcwd() to figure out where the heck we're at.
     *
     * One might think we could skip all this if path doesn't point to a
     * symlink to start with, but that's wrong.  We also want to get rid of
     * any directory symlinks that are present in the given path. We expect
     * getcwd() to give us an accurate, symlink-free path.
     */
    if (!getcwd(orig_wd, MAXPGPATH))
    {
        log_error(_("could not identify current directory: %s"),
                  strerror(errno));
        return -1;
    }

    for (;;)
    {
        char       *lsep;
        int         rllen;

        lsep = last_dir_separator(path);
        if (lsep)
        {
            *lsep = '\0';
            if (chdir(path) == -1)
            {
                log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
                return -1;
            }
            fname = lsep + 1;
        }
        else
            fname = path;

        if (lstat(fname, &buf) < 0 ||
            !S_ISLNK(buf.st_mode))
            break;

        rllen = readlink(fname, link_buf, sizeof(link_buf));
        if (rllen < 0 || rllen >= sizeof(link_buf))
        {
            log_error(_("could not read symbolic link \"%s\""), fname);
            return -1;
        }
        link_buf[rllen] = '\0';
        strcpy(path, link_buf);
    }

    /* must copy final component out of 'path' temporarily */
    strcpy(link_buf, fname);

    if (!getcwd(path, MAXPGPATH))
    {
        log_error(_("could not identify current directory: %s"),
                  strerror(errno));
        return -1;
    }
    join_path_components(path, path, link_buf);
    canonicalize_path(path);

    if (chdir(orig_wd) == -1)
    {
        log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
        return -1;
    }
#endif   /* HAVE_READLINK */

    return 0;
}

void set_pglocale_pgservice ( const char *  argv0,
const char *  app 
)

Definition at line 550 of file exec.c.

References canonicalize_path(), find_my_exec(), get_etc_path(), get_locale_path(), MAXPGPATH, my_exec_path, NULL, PG_TEXTDOMAIN, putenv, and snprintf().

Referenced by main(), and regression_main().

{
    char        path[MAXPGPATH];
    char        my_exec_path[MAXPGPATH];
    char        env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];  /* longer than
                                                                 * PGLOCALEDIR */

    /* don't set LC_ALL in the backend */
    if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
        setlocale(LC_ALL, "");

    if (find_my_exec(argv0, my_exec_path) < 0)
        return;

#ifdef ENABLE_NLS
    get_locale_path(my_exec_path, path);
    bindtextdomain(app, path);
    textdomain(app);

    if (getenv("PGLOCALEDIR") == NULL)
    {
        /* set for libpq to use */
        snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
        canonicalize_path(env_path + 12);
        putenv(strdup(env_path));
    }
#endif

    if (getenv("PGSYSCONFDIR") == NULL)
    {
        get_etc_path(my_exec_path, path);

        /* set for libpq to use */
        snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
        canonicalize_path(env_path + 13);
        putenv(strdup(env_path));
    }
}

static int validate_exec ( const char *  path  )  [static]

Definition at line 58 of file exec.c.

References MAXPGPATH, and pg_strcasecmp().

Referenced by find_my_exec(), and find_other_exec().

{
    struct stat buf;
    int         is_r;
    int         is_x;

#ifdef WIN32
    char        path_exe[MAXPGPATH + sizeof(".exe") - 1];

    /* Win32 requires a .exe suffix for stat() */
    if (strlen(path) >= strlen(".exe") &&
        pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
    {
        strcpy(path_exe, path);
        strcat(path_exe, ".exe");
        path = path_exe;
    }
#endif

    /*
     * Ensure that the file exists and is a regular file.
     *
     * XXX if you have a broken system where stat() looks at the symlink
     * instead of the underlying file, you lose.
     */
    if (stat(path, &buf) < 0)
        return -1;

    if (!S_ISREG(buf.st_mode))
        return -1;

    /*
     * Ensure that the file is both executable and readable (required for
     * dynamic loading).
     */
#ifndef WIN32
    is_r = (access(path, R_OK) == 0);
    is_x = (access(path, X_OK) == 0);
#else
    is_r = buf.st_mode & S_IRUSR;
    is_x = buf.st_mode & S_IXUSR;
#endif
    return is_x ? (is_r ? 0 : -2) : -1;
}