[glib/wip/lantw/use-uname-as-a-fallback-to-get-os-info: 3/3] gutils: Use uname to report OS info when there is no os-release file



commit 68105eccae362c4ffc2e6dccd2a49c332a10e815
Author: Ting-Wei Lan <lantw src gnome org>
Date:   Sun Oct 13 20:44:24 2019 +0800

    gutils: Use uname to report OS info when there is no os-release file
    
    There are a lot of Unix-like systems which have not implemented the
    os-release spec. On such system, we can use POSIX uname function as a
    fallback to get basic information of the system.

 glib/gutils.c      | 184 +++++++++++++++++++++++++++++++++++++++++------------
 glib/tests/utils.c |   9 ++-
 2 files changed, 153 insertions(+), 40 deletions(-)
---
diff --git a/glib/gutils.c b/glib/gutils.c
index da897bb08..89304a0df 100644
--- a/glib/gutils.c
+++ b/glib/gutils.c
@@ -42,6 +42,7 @@
 #include <sys/stat.h>
 #ifdef G_OS_UNIX
 #include <pwd.h>
+#include <sys/utsname.h>
 #include <unistd.h>
 #endif
 #include <sys/types.h>
@@ -1154,6 +1155,137 @@ g_set_application_name (const gchar *application_name)
     g_warning ("g_set_application_name() called multiple times");
 }
 
+#ifdef G_OS_UNIX
+static gchar*
+get_os_info_from_os_release (const gchar *key_name,
+                             const gchar *buffer)
+{
+  GStrv lines;
+  gchar *prefix;
+  size_t i;
+  gchar *result = NULL;
+
+  lines = g_strsplit (buffer, "\n", -1);
+  prefix = g_strdup_printf ("%s=", key_name);
+  for (i = 0; lines[i] != NULL; i++)
+    {
+      const gchar *line = lines[i];
+      const gchar *value;
+
+      if (g_str_has_prefix (line, prefix))
+        {
+          value = line + strlen (prefix);
+          result = g_shell_unquote (value, NULL);
+          if (result == NULL)
+            result = g_strdup (value);
+          break;
+        }
+    }
+  g_strfreev (lines);
+  g_free (prefix);
+
+#ifdef __linux__
+  /* Default values in spec */
+  if (result == NULL)
+    {
+      if (g_str_equal (key_name, G_OS_INFO_KEY_NAME))
+        return g_strdup ("Linux");
+      if (g_str_equal (key_name, G_OS_INFO_KEY_ID))
+        return g_strdup ("linux");
+      if (g_str_equal (key_name, G_OS_INFO_KEY_PRETTY_NAME))
+        return g_strdup ("Linux");
+    }
+#endif
+
+  return g_steal_pointer (&result);
+}
+
+static gchar*
+get_os_info_from_uname (const gchar *key_name)
+{
+  struct utsname info;
+
+  if (uname (&info) == -1)
+    return NULL;
+
+  if (strcmp (key_name, G_OS_INFO_KEY_NAME) == 0)
+    return g_strdup (info.sysname);
+  else if (strcmp (key_name, G_OS_INFO_KEY_VERSION) == 0)
+    return g_strdup (info.release);
+  else if (strcmp (key_name, G_OS_INFO_KEY_PRETTY_NAME) == 0)
+    return g_strdup_printf ("%s %s", info.sysname, info.release);
+  else if (strcmp (key_name, G_OS_INFO_KEY_ID) == 0)
+    {
+      gchar *result = g_ascii_strdown (info.sysname, -1);
+
+      g_strcanon (result, "abcdefghijklmnopqrstuvwxyz0123456789_-.", '_');
+      return g_steal_pointer (&result);
+    }
+  else if (strcmp (key_name, G_OS_INFO_KEY_VERSION_ID) == 0)
+    {
+      /* We attempt to convert the version string to the format returned by
+       * config.guess, which is the script used to generate target triplets
+       * in GNU autotools. There are a lot of rules in the script. We only
+       * implement a few rules which are easy to understand here.
+       *
+       * config.guess can be found at https://savannah.gnu.org/projects/config.
+       */
+      gchar *result;
+
+      if (strcmp (info.sysname, "NetBSD") == 0)
+        {
+          /* sed -e 's,[-_].*,,' */
+          gssize len = G_MAXSSIZE;
+          const gchar *c;
+
+          if ((c = strchr (info.release, '-')) != NULL)
+            len = MIN (len, c - info.release);
+          if ((c = strchr (info.release, '_')) != NULL)
+            len = MIN (len, c - info.release);
+          if (len == G_MAXSSIZE)
+            len = -1;
+
+          result = g_ascii_strdown (info.release, len);
+        }
+      else if (strcmp (info.sysname, "GNU") == 0)
+        {
+          /* sed -e 's,/.*$,,' */
+          gssize len = -1;
+          const gchar *c = strchr (info.release, '/');
+
+          if (c != NULL)
+            len = c - info.release;
+
+          result = g_ascii_strdown (info.release, len);
+        }
+      else if (g_str_has_prefix (info.sysname, "GNU/") ||
+               strcmp (info.sysname, "FreeBSD") == 0 ||
+               strcmp (info.sysname, "DragonFly") == 0)
+        {
+          /* sed -e 's,[-(].*,,' */
+          gssize len = G_MAXSSIZE;
+          const gchar *c;
+
+          if ((c = strchr (info.release, '-')) != NULL)
+            len = MIN (len, c - info.release);
+          if ((c = strchr (info.release, '(')) != NULL)
+            len = MIN (len, c - info.release);
+          if (len == G_MAXSSIZE)
+            len = -1;
+
+          result = g_ascii_strdown (info.release, len);
+        }
+      else
+        result = g_ascii_strdown (info.release, -1);
+
+      g_strcanon (result, "abcdefghijklmnopqrstuvwxyz0123456789_-.", '_');
+      return g_steal_pointer (&result);
+    }
+  else
+    return NULL;
+}
+#endif
+
 /**
  * g_get_os_info:
  * @key_name: a key for the OS info being requested, for example %G_OS_INFO_KEY_NAME.
@@ -1181,60 +1313,34 @@ g_get_os_info (const gchar *key_name)
   else
     return NULL;
 #elif defined (G_OS_UNIX)
+  const gchar *os_release_files[] = {"/etc/os-release", "/usr/lib/os-release"};
+  size_t i;
   gchar *buffer;
-  gchar *prefix;
-  GStrv lines;
-  int i;
   gchar *result = NULL;
-  GError *error = NULL;
 
   g_return_val_if_fail (key_name != NULL, NULL);
 
-  if (!g_file_get_contents ("/etc/os-release", &buffer, NULL, &error))
+  for (i = 0; i < G_N_ELEMENTS (os_release_files); i++)
     {
+      GError *error = NULL;
       gboolean file_missing;
 
+      if (g_file_get_contents (os_release_files[i], &buffer, NULL, &error))
+        break;
+
       file_missing = g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
       g_clear_error (&error);
 
-      if (!file_missing ||
-          !g_file_get_contents ("/usr/lib/os-release", &buffer, NULL, NULL))
+      if (!file_missing)
         return NULL;
     }
 
-  lines = g_strsplit (buffer, "\n", -1);
-  g_free (buffer);
-  prefix = g_strdup_printf ("%s=", key_name);
-  for (i = 0; lines[i] != NULL; i++)
-    {
-      const gchar *line = lines[i];
-      const gchar *value;
-
-      if (g_str_has_prefix (line, prefix))
-        {
-          value = line + strlen (prefix);
-          result = g_shell_unquote (value, NULL);
-          if (result == NULL)
-            result = g_strdup (value);
-          break;
-        }
-    }
-  g_strfreev (lines);
-  g_free (prefix);
-
-#ifdef __linux__
-  /* Default values in spec */
-  if (result == NULL)
-    {
-      if (g_str_equal (key_name, G_OS_INFO_KEY_NAME))
-        return g_strdup ("Linux");
-      if (g_str_equal (key_name, G_OS_INFO_KEY_ID))
-        return g_strdup ("linux");
-      if (g_str_equal (key_name, G_OS_INFO_KEY_PRETTY_NAME))
-        return g_strdup ("Linux");
-    }
-#endif
+  if (buffer != NULL)
+    result = get_os_info_from_os_release (key_name, buffer);
+  else
+    result = get_os_info_from_uname (key_name);
 
+  g_free (buffer);
   return g_steal_pointer (&result);
 #elif defined (G_OS_WIN32)
   if (g_strcmp0 (key_name, G_OS_INFO_KEY_NAME) == 0)
diff --git a/glib/tests/utils.c b/glib/tests/utils.c
index ce956a270..cf8a5cbc3 100644
--- a/glib/tests/utils.c
+++ b/glib/tests/utils.c
@@ -29,6 +29,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#ifdef G_OS_UNIX
+#include <sys/utsname.h>
+#endif
 #ifdef G_OS_WIN32
 #include <windows.h>
 #endif
@@ -522,6 +525,9 @@ test_os_info (void)
 {
   gchar *name;
   gchar *contents = NULL;
+#ifdef G_OS_UNIX
+  struct utsname info;
+#endif
 
   /* Whether this is implemented or not, it must not crash */
   name = g_get_os_info (G_OS_INFO_KEY_NAME);
@@ -534,7 +540,8 @@ test_os_info (void)
   g_assert_nonnull (name);
 #elif defined (G_OS_UNIX)
   if (g_file_get_contents ("/etc/os-release", &contents, NULL, NULL) ||
-      g_file_get_contents ("/usr/lib/os-release", &contents, NULL, NULL))
+      g_file_get_contents ("/usr/lib/os-release", &contents, NULL, NULL) ||
+      uname (&info) == 0)
     g_assert_nonnull (name);
   else
     g_test_skip ("os-release(5) API not implemented on this platform");


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]