| /* ********************************************************** |
| * Copyright (c) 2013-2014 Google, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of Google, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| #include "share.h" |
| |
| #ifndef UNIX |
| # error UNIX-only |
| #endif |
| |
| #ifdef LINUX |
| # include <elf.h> |
| #elif defined(MACOS) |
| # include <mach-o/loader.h> /* mach_header */ |
| #endif |
| |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <iconv.h> |
| |
| #include <string.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <time.h> |
| #include <stdlib.h> |
| #include "dr_frontend.h" |
| |
| extern bool |
| module_get_platform(file_t f, dr_platform_t *platform); |
| |
| #define drfront_die() exit(1) |
| |
| /* up to caller to call die() if necessary */ |
| #define drfront_error(msg, ...) do { \ |
| fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__); \ |
| fflush(stderr); \ |
| } while (0) |
| |
| /* Semi-compatibility with the Windows CRT _access function. |
| */ |
| drfront_status_t |
| drfront_access(const char *fname, drfront_access_mode_t mode, OUT bool *ret) |
| { |
| int r; |
| struct stat st; |
| uid_t euid; |
| |
| if (ret == NULL) |
| return DRFRONT_ERROR_INVALID_PARAMETER; |
| |
| r = stat(fname, &st); |
| |
| if (r == -1) { |
| *ret = false; |
| if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) |
| return DRFRONT_SUCCESS; |
| return DRFRONT_ERROR; |
| } else if (mode == DRFRONT_EXIST) { |
| /* Just checking for existence */ |
| *ret = true; |
| return DRFRONT_SUCCESS; |
| } |
| |
| *ret = false; |
| euid = geteuid(); |
| /* It is assumed that (S_IRWXU >> 6) == DRFRONT_READ | DRFRONT_WRITE | DRFRONT_EXEC */ |
| if (euid == st.st_uid) { |
| /* Check owner permissions */ |
| *ret = TESTALL(mode << 6, st.st_mode); |
| } else { |
| /* Check other permissions */ |
| *ret = TESTALL(mode, st.st_mode); |
| } |
| |
| return DRFRONT_SUCCESS; |
| } |
| |
| /* Implements a normal path search for fname on the paths in env_var. |
| * Resolves symlinks, which is needed to get the right config filename (i#1062). |
| */ |
| drfront_status_t |
| drfront_searchenv(const char *fname, const char *env_var, OUT char *full_path, |
| const size_t full_path_size, OUT bool *ret) |
| { |
| const char *paths = getenv(env_var); |
| const char *cur; |
| const char *next; |
| const char *end; |
| char tmp[full_path_size]; |
| char realpath_buf[PATH_MAX]; /* realpath hardcodes its buffer length */ |
| drfront_status_t status_check = DRFRONT_ERROR; |
| bool access_ret = false; |
| |
| if (ret == NULL) |
| return DRFRONT_ERROR_INVALID_PARAMETER; |
| |
| /* Windows searches the current directory first. */ |
| /* XXX: realpath resolves symlinks, which we may not want. */ |
| if (realpath(fname, realpath_buf) != NULL) { |
| status_check = drfront_access(realpath_buf, 0, &access_ret); |
| if (status_check != DRFRONT_SUCCESS) { |
| *ret = false; |
| return status_check; |
| } else if (access_ret == true) { |
| *ret = true; |
| snprintf(full_path, full_path_size, "%s", realpath_buf); |
| full_path[full_path_size - 1] = '\0'; |
| return DRFRONT_SUCCESS; |
| } |
| } |
| |
| cur = paths; |
| end = strchr(paths, '\0'); |
| while (cur < end) { |
| next = strchr(cur, ':'); |
| next = (next == NULL ? end : next); |
| snprintf(tmp, BUFFER_SIZE_ELEMENTS(tmp), |
| "%.*s/%s", (int)(next - cur), cur, fname); |
| NULL_TERMINATE_BUFFER(tmp); |
| /* realpath checks for existence too. */ |
| if (realpath(tmp, realpath_buf) != NULL) { |
| status_check = drfront_access(realpath_buf, 0, &access_ret); |
| if (status_check != DRFRONT_SUCCESS) { |
| *ret = false; |
| return status_check; |
| } else if (access_ret == true) { |
| *ret = true; |
| snprintf(full_path, full_path_size, "%s", realpath_buf); |
| full_path[full_path_size - 1] = '\0'; |
| return DRFRONT_SUCCESS; |
| } |
| } |
| cur = next + 1; |
| } |
| full_path[0] = '\0'; |
| *ret = false; |
| return DRFRONT_ERROR; |
| } |
| |
| /* Always null-terminates. |
| * No conversion on UNIX, just copy the data. |
| */ |
| drfront_status_t |
| drfront_tchar_to_char(const char *wstr, OUT char *buf, size_t buflen/*# elements*/) |
| { |
| strncpy(buf, wstr, buflen); |
| buf[buflen - 1] = '\0'; |
| return DRFRONT_SUCCESS; |
| } |
| |
| /* includes the terminating null */ |
| drfront_status_t |
| drfront_tchar_to_char_size_needed(const char *wstr, OUT size_t *needed) |
| { |
| if (needed == NULL) |
| return DRFRONT_ERROR_INVALID_PARAMETER; |
| *needed = strlen(wstr) + 1; |
| return DRFRONT_SUCCESS; |
| } |
| |
| /* Always null-terminates. |
| * No conversion on UNIX, just copy the data. |
| */ |
| drfront_status_t |
| drfront_char_to_tchar(const char *str, OUT char *wbuf, size_t wbuflen/*# elements*/) |
| { |
| strncpy(wbuf, str, wbuflen); |
| wbuf[wbuflen - 1] = '\0'; |
| return DRFRONT_SUCCESS; |
| } |
| |
| drfront_status_t |
| drfront_is_64bit_app(const char *exe, OUT bool *is_64) |
| { |
| FILE *target_file; |
| drfront_status_t res = DRFRONT_ERROR; |
| |
| if (is_64 == NULL) |
| return DRFRONT_ERROR_INVALID_PARAMETER; |
| |
| target_file = fopen(exe, "rb"); |
| if (target_file != NULL) { |
| dr_platform_t platform; |
| if (module_get_platform(fileno(target_file), &platform)) { |
| res = DRFRONT_SUCCESS; |
| *is_64 = (platform == DR_PLATFORM_64BIT); |
| } |
| fclose(target_file); |
| } |
| return res; |
| } |
| |
| /* This function is only relevant on Windows, so we return false. */ |
| drfront_status_t |
| drfront_is_graphical_app(const char *exe, OUT bool *is_graphical) |
| { |
| if (is_graphical == NULL) |
| return DRFRONT_ERROR_INVALID_PARAMETER; |
| *is_graphical = false; |
| return DRFRONT_SUCCESS; |
| } |
| |
| drfront_status_t |
| drfront_get_env_var(const char *name, OUT char *buf, size_t buflen/*# elements*/) |
| { |
| const char *tmp_buf = getenv(name); |
| size_t len_var = 0; |
| |
| if (tmp_buf == NULL) { |
| return DRFRONT_ERROR_INVALID_PARAMETER; |
| } |
| len_var = strlen(tmp_buf); |
| /* strlen doesn't include \0 in length */ |
| if (len_var + 1 > buflen) { |
| return DRFRONT_ERROR_INVALID_SIZE; |
| } |
| strncpy(buf, tmp_buf, len_var + 1); |
| buf[buflen - 1] = '\0'; |
| return DRFRONT_SUCCESS; |
| } |
| |
| /* Simply concatenates the cwd with the given relative path. Previously we |
| * called realpath, but this requires the path to exist and expands symlinks, |
| * which is inconsistent with Windows GetFullPathName(). |
| */ |
| drfront_status_t |
| drfront_get_absolute_path(const char *rel, OUT char *buf, size_t buflen/*# elements*/) |
| { |
| size_t len = 0; |
| char *err = NULL; |
| |
| if (rel[0] != '/') { |
| err = getcwd(buf, buflen); |
| if (err != NULL) { |
| len = strlen(buf); |
| /* Append a slash if it doesn't have a trailing slash. */ |
| if (buf[len - 1] != '/' && len < buflen) { |
| buf[len++] = '/'; |
| buf[len] = '\0'; |
| } |
| /* Omit any leading "./". */ |
| if (rel[0] == '.' && rel[1] == '/') { |
| rel += 2; |
| } |
| } |
| } |
| strncpy(buf + len, rel, buflen - len); |
| buf[buflen - 1] = '\0'; |
| return DRFRONT_SUCCESS; |
| } |
| |
| drfront_status_t |
| drfront_get_app_full_path(const char *app, OUT char *buf, size_t buflen/*# elements*/) |
| { |
| bool res = false; |
| drfront_status_t status_check = DRFRONT_ERROR; |
| |
| status_check = drfront_searchenv(app, "PATH", buf, buflen, &res); |
| if (status_check != DRFRONT_SUCCESS) |
| return status_check; |
| |
| buf[buflen - 1] = '\0'; |
| if (buf[0] == '\0') { |
| /* last try: expand w/ cur dir */ |
| status_check = drfront_get_absolute_path(app, buf, buflen); |
| if (status_check != DRFRONT_SUCCESS) |
| return status_check; |
| buf[buflen - 1] = '\0'; |
| } |
| return DRFRONT_SUCCESS; |
| } |
| |
| drfront_status_t |
| drfront_dir_exists(const char *path, OUT bool *is_dir) |
| { |
| struct stat st_buf; |
| if (is_dir == NULL) |
| return DRFRONT_ERROR_INVALID_PARAMETER; |
| |
| /* check if path is a file or directory */ |
| if (stat(path, &st_buf) != 0) { |
| *is_dir = false; |
| return DRFRONT_ERROR_INVALID_PATH; |
| } else { |
| if (S_ISDIR(st_buf.st_mode)) |
| *is_dir = true; |
| else |
| *is_dir = false; |
| } |
| return DRFRONT_SUCCESS; |
| } |