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());

http://wald.intevation.org/projects/trustbridge/