Mercurial > trustbridge
diff common/util.c @ 841:216a65d7fc4b
(issue66) Implement is_system_install and use it
This has completly different implementations for linux
and Windows. The commit also moves some code into util.c
for better reuse.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Tue, 29 Jul 2014 18:12:57 +0200 |
parents | 4ad764bfb39c |
children | f89b41fa7048 |
line wrap: on
line diff
--- a/common/util.c Tue Jul 29 13:15:32 2014 +0200 +++ b/common/util.c Tue Jul 29 18:12:57 2014 +0200 @@ -19,8 +19,181 @@ #include <windows.h> #endif +#ifndef APPNAME +#define APPNAME "TrustBridge" +#endif + #ifdef WIN32 -char * get_install_dir() +char* +read_registry_string (const HKEY root, const wchar_t *key, + const wchar_t *name) +{ + HKEY key_handle = NULL; + DWORD size = 0, + type = 0, + ex_size = 0, + dwRet = 0; + LONG ret = 0; + char *retval = NULL; + wchar_t *buf = NULL, + *ex_buf = NULL; + if (root == NULL || key == NULL || name == NULL) + { + ERRORPRINTF ("Invalid call to read_registry_string"); + return NULL; + } + + ret = RegOpenKeyExW (root, key, 0, KEY_READ, &key_handle); + if (ret != ERROR_SUCCESS) + { + ERRORPRINTF ("Failed to open key."); + return NULL; + } + + /* Get the size */ + ret = RegQueryValueExW (key_handle, name, 0, NULL, NULL, &size); + if (ret != ERROR_MORE_DATA && !(ret == ERROR_SUCCESS && size != 0)) + { + ERRORPRINTF ("Failed to get required registry size."); + return retval; + } + + /* Size is size in bytes not in characters */ + buf = xmalloc (size + sizeof(wchar_t)); + + /* If the stored value is not zero terminated the returned value also + is not zero terminated. That's why we reserve more and ensure it's + initialized. */ + memset (buf, 0, size + sizeof(wchar_t)); + + ret = RegQueryValueExW (key_handle, name, 0, &type, (LPBYTE) buf, &size); + if (ret != ERROR_SUCCESS) + { + ERRORPRINTF ("Failed get registry value."); + return retval; + } + + if (type == REG_SZ || (type == REG_EXPAND_SZ && wcschr (buf, '%') == NULL)) + { + /* Nothing to expand, we are done */ + retval = wchar_to_utf8 (buf, wcslen (buf)); + goto done; + } + + if (type != REG_EXPAND_SZ) + { + ERRORPRINTF ("Unhandled registry type %i", type); + goto done; + } + + /* Expand the registry string */ + ex_size = ExpandEnvironmentStringsW (buf, NULL, 0); + + if (ex_size == 0) + { + PRINTLASTERROR ("Failed to determine expanded environment size."); + goto done; + } + + ex_buf = xmalloc ((ex_size + 1) * sizeof(wchar_t)); + + dwRet = ExpandEnvironmentStringsW (buf, ex_buf, ex_size); + + ex_buf[ex_size] = '\0'; /* Make sure it's a string */ + + if (dwRet == 0 || dwRet != ex_size) + { + PRINTLASTERROR ("Failed to expand environment variables."); + goto done; + } + + retval = wchar_to_utf8 (ex_buf, ex_size); + +done: + xfree (ex_buf); + xfree (buf); + + RegCloseKey (key_handle); + return retval; +} + + +/** @brief Compare two paths for equality based on the filename. + * + * Expand the paths by using GetFullPathName and do a string + * comparison on the result to check for equality. + * + * To be sure if it is really the same file it would be better + * to open the files and compare the serial number but this + * suffices for checks that only impact on the options presented + * to the user (try a system wide installation or not) + * + * If one file does not exist the function returns false. If + * The path is longer then MAX_PATH this function also returns + * false. + * + * @param [in] path1 first path to compare + * @paran [in] path2 first path to compare + * @returns true if the paths are the same. + */ +bool +paths_equal (const char *path1, const char *path2) +{ + bool ret = false; + wchar_t buf1[MAX_PATH], + buf2[MAX_PATH]; + wchar_t *wpath1 = NULL, + *wpath2 = NULL; + DWORD retval = 0; + + if (!path1 || !path2) + { + return false; + } + + wpath1 = utf8_to_wchar(path1, strnlen(path1, MAX_PATH)); + wpath2 = utf8_to_wchar(path2, strnlen(path2, MAX_PATH)); + + if (wpath1 == NULL || wpath2 == NULL) + { + ERRORPRINTF ("Failed to convert paths to wchar."); + goto done; + } + + retval = GetFullPathNameW (wpath1, MAX_PATH, buf1, NULL); + if (retval >= MAX_PATH || retval != wcsnlen (buf1, MAX_PATH)) + { + ERRORPRINTF ("Path1 too long."); + goto done; + } + if (retval == 0) + { + PRINTLASTERROR ("Failed to get Full Path name."); + goto done; + } + + retval = GetFullPathNameW (wpath2, MAX_PATH, buf2, NULL); + if (retval >= MAX_PATH || retval != wcsnlen (buf2, MAX_PATH)) + { + ERRORPRINTF ("Path2 too long."); + goto done; + } + if (retval == 0) + { + PRINTLASTERROR ("Failed to get Full Path name."); + goto done; + } + + ret = wcscmp (buf1, buf2) == 0; +done: + xfree (wpath1); + xfree (wpath2); + + return ret; +} + +char * +get_install_dir() { wchar_t wPath[MAX_PATH]; char *utf8path = NULL; @@ -68,7 +241,6 @@ return to; } - PSID get_process_owner(HANDLE hProcess) { @@ -102,6 +274,132 @@ } return NULL; } + +bool +is_system_install() +{ + char *reg_inst_dir = NULL, + *real_prefix = NULL; + bool ret = false; + + reg_inst_dir = read_registry_string (HKEY_LOCAL_MACHINE, + L"Software\\"APPNAME, L""); + + if (reg_inst_dir == NULL) + { + return false; + } + DEBUGPRINTF ("Registered installation directory: %s\n", reg_inst_dir); + + real_prefix = get_install_dir(); + + if (!real_prefix) + { + DEBUGPRINTF ("Failed to obtain installation prefix."); + xfree (reg_inst_dir); + return false; + } + + ret = paths_equal (real_prefix, reg_inst_dir); + + xfree (real_prefix); + xfree (reg_inst_dir); + DEBUGPRINTF ("Is system install? %s\n", ret ? "true" : "false"); + return ret; +} +#else /* WIN32 */ + +char * +get_install_dir() +{ + char *retval = NULL, + *p = NULL, + buf[MAX_PATH_LINUX]; + ssize_t ret; + size_t path_len = 0; + + ret = readlink ("/proc/self/exe", buf, MAX_PATH_LINUX); + if (ret <= 0) + { + ERRORPRINTF ("readlink failed\n"); + return NULL; + } + + buf[ret] = '\0'; + + /* cut off the filename */ + p = strrchr (buf, '/'); + if (p == NULL) + { + ERRORPRINTF ("No filename found.\n"); + return NULL; + } + *(p + 1) = '\0'; + + path_len = strlen (buf); + retval = xmalloc (path_len + 1); + strncpy (retval, buf, path_len); + retval[path_len] = '\0'; + + return retval; +} + +bool +is_system_install() +{ + FILE *system_config; + int read_lines = 0; + char linebuf[MAX_PATH_LINUX + 7], + * inst_dir = NULL; + bool retval = false; + size_t inst_dir_len = 0; + + system_config = fopen ("/etc/"APPNAME"/"APPNAME"-inst.cfg", "r"); + if (system_config == NULL) + { + DEBUGPRINTF ("No system wide install configuration found.\n"); + return false; + } + inst_dir = get_install_dir (); + + if (inst_dir == NULL) + { + ERRORPRINTF ("Failed to find installation directory.\n"); + fclose(system_config); + return false; + } + + inst_dir_len = strnlen (inst_dir, MAX_PATH_LINUX); + + if (inst_dir_len == 0 || inst_dir_len >= MAX_PATH_LINUX) + { + ERRORPRINTF ("Installation directory invalid.\n"); + fclose(system_config); + return false; + } + + /* Read the first 10 lines and look for PREFIX. if it is not found + we return false. */ + while (read_lines < 10 && fgets (linebuf, MAX_PATH_LINUX + 7, + system_config) != NULL) + { + if (str_starts_with (linebuf, "PREFIX=")) + { + /* The last character is always a linebreak in a valid system_config + file so we can strip it. If this is not true the file is invalid. + linebuf is > 7 atm otherwise prefix= would not have been matched. */ + linebuf[strlen(linebuf) - 1] = '\0'; + retval = str_starts_with (inst_dir, linebuf + 7); + break; + } + read_lines++; + } + + fclose (system_config); + xfree (inst_dir); + DEBUGPRINTF ("Is system install? %s\n", retval ? "true" : "false"); + return retval; +} #endif bool @@ -128,7 +426,8 @@ return ret; } -bool is_admin() +bool +is_admin() { #ifndef _WIN32 struct passwd *current_user = getpwuid (geteuid());