X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fos_installation.c;h=1226c59662272be443a1a0bf17a21717ec43d4ab;hb=79fb947eb8fba243ea65e19b40b65e04f8806865;hp=39b5174e360394c61f415c7036989d848b59585f;hpb=9dac7b6b7b035d55bdb9731795712ead92e11f76;p=oweals%2Fgnunet.git diff --git a/src/util/os_installation.c b/src/util/os_installation.c index 39b5174e3..1226c5966 100644 --- a/src/util/os_installation.c +++ b/src/util/os_installation.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2006 Christian Grothoff (and other contributing authors) + Copyright (C) 2006-2016 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -14,132 +14,310 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** * @file src/util/os_installation.c * @brief get paths used by the program * @author Milan + * @author Christian Fuchs + * @author Christian Grothoff + * @author Matthias Wachs + * @author Heikki Lindholm + * @author LRN */ - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - #include #include #include #include +#include /* for u16_to_u8 */ #include "platform.h" -#include "gnunet_common.h" -#include "gnunet_configuration_lib.h" -#include "gnunet_disk_lib.h" -#include "gnunet_os_lib.h" -#if OSX +#include "gnunet_util_lib.h" +#if DARWIN #include #include +#elif WINDOWS +#include #endif + +#define LOG(kind,...) GNUNET_log_from (kind, "util-os-installation", __VA_ARGS__) + +#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-os-installation", syscall, filename) + + +/** + * Default project data used for installation path detection + * for GNUnet (core). + */ +static const struct GNUNET_OS_ProjectData default_pd = { + .libname = "libgnunetutil", + .project_dirname = "gnunet", + .binary_name = "gnunet-arm", + .env_varname = "GNUNET_PREFIX", + .base_config_varname = "GNUNET_BASE_CONFIG", + .bug_email = "gnunet-developers@gnu.org", + .homepage = "http://www.gnu.org/s/gnunet/", + .config_file = "gnunet.conf", + .user_config_file = "~/.config/gnunet.conf", +}; + +/** + * Which project data do we currently use for installation + * path detection? Never NULL. + */ +static const struct GNUNET_OS_ProjectData *current_pd = &default_pd; + +/** + * Return default project data used by 'libgnunetutil' for GNUnet. + */ +const struct GNUNET_OS_ProjectData * +GNUNET_OS_project_data_default (void) +{ + return &default_pd; +} + + +/** + * @return current project data. + */ +const struct GNUNET_OS_ProjectData * +GNUNET_OS_project_data_get () +{ + return current_pd; +} + + +/** + * Setup OS subsystem with project data. + * + * @param pd project data used to determine paths + */ +void +GNUNET_OS_init (const struct GNUNET_OS_ProjectData *pd) +{ + GNUNET_assert (NULL != pd); + current_pd = pd; +} + + #if LINUX /** * Try to determine path by reading /proc/PID/exe + * + * @return NULL on error */ static char * get_path_from_proc_maps () { char fn[64]; - char *line; - char *dir; + char line[1024]; + char dir[1024]; FILE *f; + char *lgu; - GNUNET_snprintf (fn, 64, "/proc/%u/maps", getpid ()); - line = GNUNET_malloc (1024); - dir = GNUNET_malloc (1024); - f = fopen (fn, "r"); - if (f != NULL) + GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/maps", getpid ()); + if (NULL == (f = FOPEN (fn, "r"))) + return NULL; + while (NULL != fgets (line, sizeof (line), f)) + { + if ((1 == + SSCANF (line, "%*x-%*x %*c%*c%*c%*c %*x %*2x:%*2x %*u%*[ ]%1023s", dir)) && + (NULL != (lgu = strstr (dir, + current_pd->libname)))) { - while (NULL != fgets (line, 1024, f)) - { - if ((1 == sscanf (line, - "%*x-%*x %*c%*c%*c%*c %*x %*2u:%*2u %*u%*[ ]%s", - dir)) && (NULL != strstr (dir, "libgnunetutil"))) - { - strstr (dir, "libgnunetutil")[0] = '\0'; - fclose (f); - GNUNET_free (line); - return dir; - } - } - fclose (f); + lgu[0] = '\0'; + FCLOSE (f); + return GNUNET_strdup (dir); } - GNUNET_free (dir); - GNUNET_free (line); + } + FCLOSE (f); return NULL; } + /** * Try to determine path by reading /proc/PID/exe + * + * @return NULL on error */ static char * get_path_from_proc_exe () { char fn[64]; - char *lnk; - size_t size; - - GNUNET_snprintf (fn, 64, "/proc/%u/exe", getpid ()); - lnk = GNUNET_malloc (1024); - size = readlink (fn, lnk, 1023); - if ((size == 0) || (size >= 1024)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "readlink", fn); - GNUNET_free (lnk); - return NULL; - } + char lnk[1024]; + ssize_t size; + char *lep; + + GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/exe", getpid ()); + size = readlink (fn, lnk, sizeof (lnk) - 1); + if (size <= 0) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn); + return NULL; + } + GNUNET_assert (size < sizeof (lnk)); lnk[size] = '\0'; while ((lnk[size] != '/') && (size > 0)) size--; + GNUNET_asprintf (&lep, + "/%s/libexec/", + current_pd->project_dirname); + /* test for being in lib/gnunet/libexec/ or lib/MULTIARCH/gnunet/libexec */ + if ( (size > strlen (lep)) && + (0 == strcmp (lep, + &lnk[size - strlen (lep)])) ) + size -= strlen (lep) - 1; + GNUNET_free (lep); if ((size < 4) || (lnk[size - 4] != '/')) - { - /* not installed in "/bin/" -- binary path probably useless */ - GNUNET_free (lnk); - return NULL; - } + { + /* not installed in "/bin/" -- binary path probably useless */ + return NULL; + } lnk[size] = '\0'; - return lnk; + return GNUNET_strdup (lnk); } #endif + #if WINDOWS +static HINSTANCE dll_instance; + + +/** + * GNUNET_util_cl_init() in common_logging.c is preferred. + * This function is only for thread-local storage (not used in GNUnet) + * and hInstance saving. + */ +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + dll_instance = hinstDLL; + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + + /** * Try to determine path with win32-specific function + * + * @return NULL on error */ static char * get_path_from_module_filename () { - char *path; - char *idx; - - path = GNUNET_malloc (4097); - GetModuleFileName (NULL, path, 4096); - idx = path + strlen (path); - while ((idx > path) && (*idx != '\\') && (*idx != '/')) + size_t pathlen = 512; + DWORD real_pathlen; + wchar_t *idx; + wchar_t *modulepath = NULL; + char *upath; + uint8_t *u8_string; + size_t u8_string_length; + + /* This braindead function won't tell us how much space it needs, so + * we start at 1024 and double the space up if it doesn't fit, until + * it fits, or we exceed the threshold. + */ + do + { + pathlen = pathlen * 2; + modulepath = GNUNET_realloc (modulepath, + pathlen * sizeof (wchar_t)); + SetLastError (0); + real_pathlen = GetModuleFileNameW (dll_instance, + modulepath, + pathlen * sizeof (wchar_t)); + } while (real_pathlen >= pathlen && pathlen < 16*1024); + if (real_pathlen >= pathlen) + GNUNET_assert (0); + /* To be safe */ + modulepath[real_pathlen] = '\0'; + + idx = modulepath + real_pathlen; + while ((idx > modulepath) && (*idx != L'\\') && (*idx != L'/')) idx--; - *idx = '\0'; - return path; + *idx = L'\0'; + + /* Now modulepath holds full path to the directory where libgnunetutil is. + * This directory should look like /bin or . + */ + if (wcschr (modulepath, L'/') || wcschr (modulepath, L'\\')) + { + /* At least one directory component (i.e. we're not in a root directory) */ + wchar_t *dirname = idx; + while ((dirname > modulepath) && (*dirname != L'\\') && (*dirname != L'/')) + dirname--; + *dirname = L'\0'; + if (dirname > modulepath) + { + dirname++; + /* Now modulepath holds full path to the parent directory of the directory + * where libgnunetutil is. + * dirname holds the name of the directory where libgnunetutil is. + */ + if (wcsicmp (dirname, L"bin") == 0) + { + /* pass */ + } + else + { + /* Roll back our changes to modulepath */ + dirname--; + *dirname = L'/'; + } + } + } + + /* modulepath is GNUNET_PREFIX */ + u8_string = u16_to_u8 (modulepath, wcslen (modulepath), NULL, &u8_string_length); + if (NULL == u8_string) + GNUNET_assert (0); + + upath = GNUNET_malloc (u8_string_length + 1); + GNUNET_memcpy (upath, u8_string, u8_string_length); + upath[u8_string_length] = '\0'; + + free (u8_string); + GNUNET_free (modulepath); + + return upath; } #endif -#if OSX -typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize); +#if DARWIN +/** + * Signature of the '_NSGetExecutablePath" function. + * + * @param buf where to write the path + * @param number of bytes available in @a buf + * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize' + */ +typedef int +(*MyNSGetExecutablePathProto) (char *buf, + size_t *bufsize); + + +/** + * Try to obtain the path of our executable using '_NSGetExecutablePath'. + * + * @return NULL on error + */ static char * get_path_from_NSGetExecutablePath () { @@ -147,26 +325,23 @@ get_path_from_NSGetExecutablePath () char *path; size_t len; MyNSGetExecutablePathProto func; - int ret; path = NULL; - func = - (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath"); - if (!func) + if (NULL == (func = + (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath"))) return NULL; path = &zero; len = 0; /* get the path len, including the trailing \0 */ - func (path, &len); - if (len == 0) + (void) func (path, &len); + if (0 == len) return NULL; path = GNUNET_malloc (len); - ret = func (path, &len); - if (ret != 0) - { - GNUNET_free (path); - return NULL; - } + if (0 != func (path, &len)) + { + GNUNET_free (path); + return NULL; + } len = strlen (path); while ((path[len] != '/') && (len > 0)) len--; @@ -174,39 +349,52 @@ get_path_from_NSGetExecutablePath () return path; } + +/** + * Try to obtain the path of our executable using '_dyld_image' API. + * + * @return NULL on error + */ static char * get_path_from_dyld_image () { const char *path; - char *p, *s; - int i; + char *p; + char *s; + unsigned int i; int c; - p = NULL; c = _dyld_image_count (); for (i = 0; i < c; i++) - { - if (_dyld_get_image_header (i) == &_mh_dylib_header) - { - path = _dyld_get_image_name (i); - if (path != NULL && strlen (path) > 0) - { - p = strdup (path); - s = p + strlen (p); - while ((s > p) && (*s != '/')) - s--; - s++; - *s = '\0'; - } - break; - } - } - return p; + { + if (((const void *) _dyld_get_image_header (i)) != + ((const void *) &_mh_dylib_header) ) + continue; + path = _dyld_get_image_name (i); + if ( (NULL == path) || (0 == strlen (path)) ) + continue; + p = GNUNET_strdup (path); + s = p + strlen (p); + while ((s > p) && ('/' != *s)) + s--; + s++; + *s = '\0'; + return p; + } + return NULL; } #endif + +/** + * Return the actual path to a file found in the current + * PATH environment variable. + * + * @param binary the name of the file to find + * @return path to binary, NULL if not found + */ static char * -get_path_from_PATH () +get_path_from_PATH (const char *binary) { char *path; char *pos; @@ -214,51 +402,65 @@ get_path_from_PATH () char *buf; const char *p; - p = getenv ("PATH"); - if (p == NULL) + if (NULL == (p = getenv ("PATH"))) return NULL; +#if WINDOWS + /* On W32 look in CWD first. */ + GNUNET_asprintf (&path, ".%c%s", PATH_SEPARATOR, p); +#else path = GNUNET_strdup (p); /* because we write on it */ - buf = GNUNET_malloc (strlen (path) + 20); +#endif + buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1); pos = path; - - while (NULL != (end = strchr (pos, ':'))) - { - *end = '\0'; - sprintf (buf, "%s/%s", pos, "gnunetd"); - if (GNUNET_DISK_file_test (buf) == GNUNET_YES) - { - pos = GNUNET_strdup (pos); - GNUNET_free (buf); - GNUNET_free (path); - return pos; - } - pos = end + 1; - } - sprintf (buf, "%s/%s", pos, "gnunetd"); - if (GNUNET_DISK_file_test (buf) == GNUNET_YES) + while (NULL != (end = strchr (pos, PATH_SEPARATOR))) + { + *end = '\0'; + sprintf (buf, "%s/%s", pos, binary); + if (GNUNET_DISK_file_test (buf) == GNUNET_YES) { pos = GNUNET_strdup (pos); GNUNET_free (buf); GNUNET_free (path); return pos; } + pos = end + 1; + } + sprintf (buf, "%s/%s", pos, binary); + if (GNUNET_YES == GNUNET_DISK_file_test (buf)) + { + pos = GNUNET_strdup (pos); + GNUNET_free (buf); + GNUNET_free (path); + return pos; + } GNUNET_free (buf); GNUNET_free (path); return NULL; } + +/** + * Try to obtain the installation path using the "GNUNET_PREFIX" environment + * variable. + * + * @return NULL on error (environment variable not set) + */ static char * get_path_from_GNUNET_PREFIX () { const char *p; - p = getenv ("GNUNET_PREFIX"); - if (p != NULL) + if ( (NULL != current_pd->env_varname) && + (NULL != (p = getenv (current_pd->env_varname))) ) + return GNUNET_strdup (p); + if ( (NULL != current_pd->env_varname_alt) && + (NULL != (p = getenv (current_pd->env_varname_alt))) ) return GNUNET_strdup (p); return NULL; } -/* + +/** * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path * @author Milan * @@ -269,85 +471,80 @@ os_get_gnunet_path () { char *ret; - ret = get_path_from_GNUNET_PREFIX (); - if (ret != NULL) + if (NULL != (ret = get_path_from_GNUNET_PREFIX ())) return ret; #if LINUX - ret = get_path_from_proc_maps (); - if (ret != NULL) + if (NULL != (ret = get_path_from_proc_maps ())) + return ret; + /* try path *first*, before /proc/exe, as /proc/exe can be wrong */ + if ( (NULL != current_pd->binary_name) && + (NULL != (ret = get_path_from_PATH (current_pd->binary_name))) ) return ret; - ret = get_path_from_proc_exe (); - if (ret != NULL) + if (NULL != (ret = get_path_from_proc_exe ())) return ret; #endif #if WINDOWS - ret = get_path_from_module_filename (); - if (ret != NULL) + if (NULL != (ret = get_path_from_module_filename ())) return ret; #endif -#if OSX - ret = get_path_from_dyld_image (); - if (ret != NULL) +#if DARWIN + if (NULL != (ret = get_path_from_dyld_image ())) return ret; - ret = get_path_from_NSGetExecutablePath (); - if (ret != NULL) + if (NULL != (ret = get_path_from_NSGetExecutablePath ())) return ret; #endif - ret = get_path_from_PATH (); - if (ret != NULL) + if ( (NULL != current_pd->binary_name) && + (NULL != (ret = get_path_from_PATH (current_pd->binary_name))) ) return ret; /* other attempts here */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Could not determine installation path for GNUnet. Set `%s' environment variable.\n"), - "GNUNET_PREFIX"); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Could not determine installation path for %s. Set `%s' environment variable.\n"), + current_pd->project_dirname, + current_pd->env_varname); return NULL; } -/* + +/** * @brief get the path to current app's bin/ - * @author Milan - * * @return a pointer to the executable path, or NULL on error */ static char * os_get_exec_path () { - char *ret; + char *ret = NULL; #if LINUX - ret = get_path_from_proc_exe (); - if (ret != NULL) + if (NULL != (ret = get_path_from_proc_exe ())) return ret; #endif #if WINDOWS - ret = get_path_from_module_filename (); - if (ret != NULL) + if (NULL != (ret = get_path_from_module_filename ())) return ret; #endif -#if OSX - ret = get_path_from_NSGetExecutablePath (); - if (ret != NULL) +#if DARWIN + if (NULL != (ret = get_path_from_NSGetExecutablePath ())) return ret; #endif /* other attempts here */ - return NULL; + return ret; } - /** * @brief get the path to a specific GNUnet installation directory or, - * with GNUNET_IPK_SELF_PREFIX, the current running apps installation directory - * @author Milan + * with #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation directory * @return a pointer to the dir path (to be freed by the caller) */ char * GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind) { size_t n; - const char *dirname; + char *dirname; char *execpath = NULL; char *tmp; + char *multiarch; + char *libdir; int isbasedir; /* if wanted, try to get the current app's bin/ */ @@ -356,89 +553,419 @@ GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind) /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some * guess for the current app */ - if (execpath == NULL) + if (NULL == execpath) execpath = os_get_gnunet_path (); - if (execpath == NULL) + if (NULL == execpath) return NULL; n = strlen (execpath); - if (n == 0) - { - /* should never happen, but better safe than sorry */ - GNUNET_free (execpath); - return NULL; - } + if (0 == n) + { + /* should never happen, but better safe than sorry */ + GNUNET_free (execpath); + return NULL; + } /* remove filename itself */ - while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR)) + while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1])) execpath[--n] = '\0'; isbasedir = 1; - if ((n > 5) && - ((0 == strcasecmp (&execpath[n - 5], "lib32")) || - (0 == strcasecmp (&execpath[n - 5], "lib64")))) + if ((n > 6) && + ((0 == strcasecmp (&execpath[n - 6], "/lib32")) || + (0 == strcasecmp (&execpath[n - 6], "/lib64")))) + { + if ( (GNUNET_OS_IPK_LIBDIR != dirkind) && + (GNUNET_OS_IPK_LIBEXECDIR != dirkind) ) { - if (dirkind != GNUNET_OS_IPK_LIBDIR) - { - /* strip '/lib32' or '/lib64' */ - execpath[n - 5] = '\0'; - n -= 5; - } - else - isbasedir = 0; - } - else if ((n > 3) && - ((0 == strcasecmp (&execpath[n - 3], "bin")) || - (0 == strcasecmp (&execpath[n - 3], "lib")))) - { - /* strip '/bin' or '/lib' */ - execpath[n - 3] = '\0'; - n -= 3; + /* strip '/lib32' or '/lib64' */ + execpath[n - 6] = '\0'; + n -= 6; } + else + isbasedir = 0; + } + else if ((n > 4) && + ((0 == strcasecmp (&execpath[n - 4], "/bin")) || + (0 == strcasecmp (&execpath[n - 4], "/lib")))) + { + /* strip '/bin' or '/lib' */ + execpath[n - 4] = '\0'; + n -= 4; + } + multiarch = NULL; + if (NULL != (libdir = strstr (execpath, "/lib/"))) + { + /* test for multi-arch path of the form "PREFIX/lib/MULTIARCH/"; + here we need to re-add 'multiarch' to lib and libexec paths later! */ + multiarch = &libdir[5]; + if (NULL == strchr (multiarch, '/')) + libdir[0] = '\0'; /* Debian multiarch format, cut of from 'execpath' but preserve in multicarch */ + else + multiarch = NULL; /* maybe not, multiarch still has a '/', which is not OK */ + } /* in case this was a directory named foo-bin, remove "foo-" */ while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR)) execpath[--n] = '\0'; switch (dirkind) + { + case GNUNET_OS_IPK_PREFIX: + case GNUNET_OS_IPK_SELF_PREFIX: + dirname = GNUNET_strdup (DIR_SEPARATOR_STR); + break; + case GNUNET_OS_IPK_BINDIR: + dirname = GNUNET_strdup (DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR); + break; + case GNUNET_OS_IPK_LIBDIR: + if (isbasedir) { - case GNUNET_OS_IPK_PREFIX: - case GNUNET_OS_IPK_SELF_PREFIX: - dirname = DIR_SEPARATOR_STR; - break; - case GNUNET_OS_IPK_BINDIR: - dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR; - break; - case GNUNET_OS_IPK_LIBDIR: - if (isbasedir) - dirname = - DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" - DIR_SEPARATOR_STR; - else - dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR; - break; - case GNUNET_OS_IPK_DATADIR: - dirname = - DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "gnunet" - DIR_SEPARATOR_STR; - break; - case GNUNET_OS_IPK_LOCALEDIR: - dirname = - DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" - DIR_SEPARATOR_STR; - break; - default: - GNUNET_free (execpath); - return NULL; + GNUNET_asprintf (&tmp, + "%s%s%s%s%s%s%s", + execpath, + DIR_SEPARATOR_STR "lib", + (NULL != multiarch) ? DIR_SEPARATOR_STR : "", + (NULL != multiarch) ? multiarch : "", + DIR_SEPARATOR_STR, + current_pd->project_dirname, + DIR_SEPARATOR_STR); + if (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) + { + GNUNET_free (execpath); + return tmp; + } + GNUNET_free (tmp); + tmp = NULL; + dirname = NULL; + if (4 == sizeof (void *)) + { + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR, + current_pd->project_dirname); + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + if (8 == sizeof (void *)) + { + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR, + current_pd->project_dirname); + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + + if ( (NULL != tmp) && + (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) ) + { + GNUNET_free (execpath); + GNUNET_free_non_null (dirname); + return tmp; + } + GNUNET_free (tmp); + GNUNET_free_non_null (dirname); } - tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1); - sprintf (tmp, "%s%s", execpath, dirname); + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR, + current_pd->project_dirname); + break; + case GNUNET_OS_IPK_DATADIR: + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR, + current_pd->project_dirname); + break; + case GNUNET_OS_IPK_LOCALEDIR: + dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" DIR_SEPARATOR_STR); + break; + case GNUNET_OS_IPK_ICONDIR: + dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "icons" DIR_SEPARATOR_STR); + break; + case GNUNET_OS_IPK_DOCDIR: + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "doc" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR, + current_pd->project_dirname); + break; + case GNUNET_OS_IPK_LIBEXECDIR: + if (isbasedir) + { + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR, + current_pd->project_dirname); + GNUNET_asprintf (&tmp, + "%s%s%s%s", + execpath, + DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR, + (NULL != multiarch) ? multiarch : "", + dirname); + if (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) + { + GNUNET_free (execpath); + GNUNET_free (dirname); + return tmp; + } + GNUNET_free (tmp); + tmp = NULL; + dirname = NULL; + if (4 == sizeof (void *)) + { + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR, + current_pd->project_dirname); + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + if (8 == sizeof (void *)) + { + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR, + current_pd->project_dirname); + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + if ( (NULL != tmp) && + (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) ) + { + GNUNET_free (execpath); + GNUNET_free_non_null (dirname); + return tmp; + } + GNUNET_free (tmp); + GNUNET_free_non_null (dirname); + } + GNUNET_asprintf (&dirname, + DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR, + current_pd->project_dirname); + break; + default: + GNUNET_free (execpath); + return NULL; + } + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + GNUNET_free (dirname); GNUNET_free (execpath); return tmp; } -#if 0 /* keep Emacsens' auto-indent happy */ + +/** + * Given the name of a gnunet-helper, gnunet-service or gnunet-daemon + * binary, try to prefix it with the libexec/-directory to get the + * full path. + * + * @param progname name of the binary + * @return full path to the binary, if possible, otherwise copy of 'progname' + */ +char * +GNUNET_OS_get_libexec_binary_path (const char *progname) { -#endif -#ifdef __cplusplus + static char *cache; + char *libexecdir; + char *binary; + + if ( (DIR_SEPARATOR == progname[0]) || + (GNUNET_YES == + GNUNET_STRINGS_path_is_absolute (progname, + GNUNET_NO, + NULL, NULL)) ) + return GNUNET_strdup (progname); + if (NULL != cache) + libexecdir = cache; + else + libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR); + if (NULL == libexecdir) + return GNUNET_strdup (progname); + GNUNET_asprintf (&binary, + "%s%s", + libexecdir, + progname); + cache = libexecdir; + return binary; } + + +/** + * Check whether an executable exists and possibly if the suid bit is + * set on the file. Attempts to find the file using the current PATH + * environment variable as a search path. + * + * @param binary the name of the file to check. + * W32: must not have an .exe suffix. + * @param check_suid input true if the binary should be checked for SUID (*nix) + * W32: checks if the program has sufficient privileges by executing this + * binary with the -d flag. -d omits a programs main loop and only + * executes all privileged operations in an binary. + * @param params parameters used for w32 privilege checking (can be NULL for != w32 ) + * @return #GNUNET_YES if the file is SUID (*nix) or can be executed with current privileges (W32), + * #GNUNET_NO if not SUID (but binary exists), + * #GNUNET_SYSERR on error (no such binary or not executable) + */ +int +GNUNET_OS_check_helper_binary (const char *binary, + int check_suid, + const char *params) +{ + struct stat statbuf; + char *p; + char *pf; +#ifdef MINGW + char *binaryexe; + + GNUNET_asprintf (&binaryexe, + "%s.exe", + binary); + if ( (GNUNET_YES == + GNUNET_STRINGS_path_is_absolute (binaryexe, + GNUNET_NO, + NULL, NULL)) || + (0 == strncmp (binary, "./", 2)) ) + p = GNUNET_strdup (binaryexe); + else + { + p = get_path_from_PATH (binaryexe); + if (NULL != p) + { + GNUNET_asprintf (&pf, "%s/%s", p, binaryexe); + GNUNET_free (p); + p = pf; + } + } + GNUNET_free (binaryexe); +#else + if ( (GNUNET_YES == + GNUNET_STRINGS_path_is_absolute (binary, + GNUNET_NO, + NULL, + NULL)) || + (0 == strncmp (binary, "./", 2)) ) + { + p = GNUNET_strdup (binary); + } + else + { + p = get_path_from_PATH (binary); + if (NULL != p) + { + GNUNET_asprintf (&pf, + "%s/%s", + p, + binary); + GNUNET_free (p); + p = pf; + } + } +#endif + if (NULL == p) + { + LOG (GNUNET_ERROR_TYPE_INFO, + _("Could not find binary `%s' in PATH!\n"), + binary); + return GNUNET_SYSERR; + } + if (0 != ACCESS (p, + X_OK)) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "access", + p); + GNUNET_free (p); + return GNUNET_SYSERR; + } +#ifndef MINGW + if (0 == getuid ()) + { + /* as we run as root, we don't insist on SUID */ + GNUNET_free (p); + return GNUNET_YES; + } #endif -/* end of installpath.c */ + if (0 != STAT (p, + &statbuf)) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "stat", + p); + GNUNET_free (p); + return GNUNET_SYSERR; + } + if (check_suid) + { +#ifndef MINGW + if ( (0 != (statbuf.st_mode & S_ISUID)) && + (0 == statbuf.st_uid) ) + { + GNUNET_free (p); + return GNUNET_YES; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Binary `%s' exists, but is not SUID\n"), + p); + /* binary exists, but not SUID */ +#else + STARTUPINFO start; + char parameters[512]; + PROCESS_INFORMATION proc; + DWORD exit_value; + + GNUNET_snprintf (parameters, + sizeof (parameters), + "-d %s", params); + memset (&start, 0, sizeof (start)); + start.cb = sizeof (start); + memset (&proc, 0, sizeof (proc)); + + + // Start the child process. + if ( ! (CreateProcess( p, // current windows (2k3 and up can handle / instead of \ in paths)) + parameters, // execute dryrun/priviliege checking mode + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + CREATE_DEFAULT_ERROR_MODE, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &start, // Pointer to STARTUPINFO structure + &proc ) // Pointer to PROCESS_INFORMATION structure + )) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("CreateProcess failed for binary %s (%d).\n"), + p, GetLastError()); + return GNUNET_SYSERR; + } + + // Wait until child process exits. + WaitForSingleObject( proc.hProcess, INFINITE ); + + if ( ! GetExitCodeProcess (proc.hProcess, &exit_value)){ + LOG (GNUNET_ERROR_TYPE_ERROR, + _("GetExitCodeProcess failed for binary %s (%d).\n"), + p, GetLastError() ); + return GNUNET_SYSERR; + } + // Close process and thread handles. + CloseHandle( proc.hProcess ); + CloseHandle( proc.hThread ); + + if (!exit_value) + return GNUNET_YES; +#endif + } + GNUNET_free (p); + return GNUNET_NO; +} + + +/* end of os_installation.c */