[gnome-software] Only parse /etc/os-release once per check



commit 5d2d5e5dbcad80307c4fbb8b524e3e33b4f36ae3
Author: Richard Hughes <richard hughsie com>
Date:   Thu May 19 20:26:40 2016 +0100

    Only parse /etc/os-release once per check
    
    Also, add some unit tests.

 data/tests/Makefile.am                         |    1 +
 data/tests/os-release                          |   16 ++
 src/gs-os-release.c                            |  268 ++++++++++++++++++------
 src/gs-os-release.h                            |   24 +-
 src/gs-plugin.c                                |   14 +-
 src/gs-self-test.c                             |   23 ++
 src/plugins/gs-plugin-fedora-distro-upgrades.c |   10 +-
 src/plugins/gs-plugin-odrs.c                   |   10 +-
 8 files changed, 277 insertions(+), 89 deletions(-)
---
diff --git a/data/tests/Makefile.am b/data/tests/Makefile.am
index e3bfd24..019558b 100644
--- a/data/tests/Makefile.am
+++ b/data/tests/Makefile.am
@@ -1,4 +1,5 @@
 EXTRA_DIST =                                           \
+       os-release                                      \
        chiron-0.2.cab                                  \
        chiron-1.1-1.deb                                \
        chiron-1.1-1.fc24.x86_64.rpm
diff --git a/data/tests/os-release b/data/tests/os-release
new file mode 100644
index 0000000..79df6af
--- /dev/null
+++ b/data/tests/os-release
@@ -0,0 +1,16 @@
+NAME=Fedora
+VERSION="25 (Workstation Edition)"
+ID=fedora
+VERSION_ID=25
+PRETTY_NAME="Fedora 25 (Workstation Edition)"
+ANSI_COLOR="0;34"
+CPE_NAME="cpe:/o:fedoraproject:fedora:25"
+HOME_URL="https://fedoraproject.org/";
+BUG_REPORT_URL="https://bugzilla.redhat.com/";
+REDHAT_BUGZILLA_PRODUCT="Fedora"
+REDHAT_BUGZILLA_PRODUCT_VERSION=25
+REDHAT_SUPPORT_PRODUCT="Fedora"
+REDHAT_SUPPORT_PRODUCT_VERSION=25
+PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy
+VARIANT="Workstation Edition"
+VARIANT_ID=workstation
diff --git a/src/gs-os-release.c b/src/gs-os-release.c
index 9c639af..6182045 100644
--- a/src/gs-os-release.c
+++ b/src/gs-os-release.c
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2016 Kalev Lember <klember redhat com>
+ * Copyright (C) 2016 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU Lesser General Public License Version 2.1
  *
@@ -19,111 +20,240 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+/**
+ * SECTION:gs-os-release
+ * @title: GsOsRelease
+ * @include: gnome-software.h
+ * @stability: Unstable
+ * @short_description: Data from os-release
+ *
+ * This object allows plugins to parse /etc/os-release for distribution
+ * metadata information.
+ */
+
 #include "config.h"
 
+#include <glib.h>
+
 #include "gs-os-release.h"
 
-#include <string.h>
+struct _GsOsRelease
+{
+       GObject                  parent_instance;
+       gchar                   *name;
+       gchar                   *version;
+       gchar                   *id;
+       gchar                   *version_id;
+       gchar                   *pretty_name;
+};
+
+static void gs_os_release_initable_iface_init (GInitableIface *iface);
 
-G_DEFINE_QUARK (gs-os-release-error-quark, gs_os_release_error)
+G_DEFINE_TYPE_WITH_CODE (GsOsRelease, gs_os_release, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, gs_os_release_initable_iface_init))
 
-/* strip any quotes surrounding the string */
-static gchar *
-dequote (gchar *s)
+/**
+ * gs_os_release_initable_init:
+ **/
+static gboolean
+gs_os_release_initable_init (GInitable *initable,
+                            GCancellable *cancellable,
+                            GError **error)
 {
-       size_t len;
+       GsOsRelease *os_release = GS_OS_RELEASE (initable);
+       const gchar *filename;
+       g_autofree gchar *data = NULL;
+       g_auto(GStrv) lines = NULL;
+       guint i;
 
-       g_assert (s != NULL);
+       g_return_val_if_fail (GS_IS_OS_RELEASE (os_release), FALSE);
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-       len = strlen (s);
-       if (len >= 2 &&
-           *s == *(s + len - 1) &&
-           (*s == '"' || *s == '\'')) {
-               s[len - 1] = '\0';
-               s++;
+       /* get contents */
+       filename = g_getenv ("GS_SELF_TEST_OS_RELEASE_FILENAME");
+       if (filename == NULL) {
+               filename = "/etc/os-release";
+               if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+                       filename = "/usr/lib/os-release";
        }
+       if (!g_file_get_contents (filename, &data, NULL, error))
+               return FALSE;
 
-       return s;
-}
+       /* parse */
+       lines = g_strsplit (data, "\n", -1);
+       for (i = 0; lines[i] != NULL; i++) {
+               gchar *tmp;
 
-static gchar *
-get_item (gchar *line, const gchar *key)
-{
-       g_autofree gchar *label = NULL;
+               /* split the line up into two halves */
+               tmp = g_strstr_len (lines[i], -1, "=");
+               if (tmp == NULL)
+                       continue;
+               *tmp = '\0';
+               tmp++;
 
-       label = g_strconcat (key, "=", NULL);
-       if (g_str_has_prefix (line, label)) {
-               return g_strcompress (dequote (line + strlen (label)));
-       }
+               /* ignore trailing quote */
+               if (tmp[0] == '\"')
+                       tmp++;
+
+               /* ignore trailing quote */
+               g_strdelimit (tmp, "\"", '\0');
 
-       return NULL;
+               /* match fields we're interested in */
+               if (g_strcmp0 (lines[i], "NAME") == 0) {
+                       os_release->name = g_strdup (tmp);
+                       continue;
+               }
+               if (g_strcmp0 (lines[i], "VERSION") == 0) {
+                       os_release->version = g_strdup (tmp);
+                       continue;
+               }
+               if (g_strcmp0 (lines[i], "ID") == 0) {
+                       os_release->id = g_strdup (tmp);
+                       continue;
+               }
+               if (g_strcmp0 (lines[i], "VERSION_ID") == 0) {
+                       os_release->version_id = g_strdup (tmp);
+                       continue;
+               }
+               if (g_strcmp0 (lines[i], "PRETTY_NAME") == 0) {
+                       os_release->pretty_name = g_strdup (tmp);
+                       continue;
+               }
+       }
+       return TRUE;
 }
 
-static gchar *
-gs_os_release_parse_variable (const gchar *variable, GError **error)
+/**
+ * gs_os_release_get_name:
+ * @os_release: A #GsOsRelease
+ *
+ * Gets the name from the os-release parser.
+ *
+ * Returns: a string, or %NULL
+ **/
+const gchar *
+gs_os_release_get_name (GsOsRelease *os_release)
 {
-       const gchar *filename;
-       g_autofree gchar *buffer = NULL;
-
-       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-       g_return_val_if_fail (variable != NULL, NULL);
-
-       filename = "/etc/os-release";
-       if (!g_file_test (filename, G_FILE_TEST_EXISTS))
-               filename = "/usr/lib/os-release";
-
-       if (!g_file_get_contents (filename, &buffer, NULL, error))
-               return NULL;
+       g_return_val_if_fail (GS_IS_OS_RELEASE (os_release), NULL);
+       return os_release->name;
+}
 
-       if (buffer != NULL) {
-               gint i;
-               g_auto(GStrv) lines = NULL;
+/**
+ * gs_os_release_get_version:
+ * @os_release: A #GsOsRelease
+ *
+ * Gets the version from the os-release parser.
+ *
+ * Returns: a string, or %NULL
+ **/
+const gchar *
+gs_os_release_get_version (GsOsRelease *os_release)
+{
+       g_return_val_if_fail (GS_IS_OS_RELEASE (os_release), NULL);
+       return os_release->version;
+}
 
-               lines = g_strsplit (buffer, "\n", -1);
-               for (i = 0; lines[i] != NULL; i++) {
-                       gchar *line = g_strstrip (lines[i]);
-                       gchar *ret;
+/**
+ * gs_os_release_get_id:
+ * @os_release: A #GsOsRelease
+ *
+ * Gets the ID from the os-release parser.
+ *
+ * Returns: a string, or %NULL
+ **/
+const gchar *
+gs_os_release_get_id (GsOsRelease *os_release)
+{
+       g_return_val_if_fail (GS_IS_OS_RELEASE (os_release), NULL);
+       return os_release->id;
+}
 
-                       if ((ret = get_item (line, variable)) != NULL)
-                               return ret;
-               }
-       }
+/**
+ * gs_os_release_get_version_id:
+ * @os_release: A #GsOsRelease
+ *
+ * Gets the version ID from the os-release parser.
+ *
+ * Returns: a string, or %NULL
+ **/
+const gchar *
+gs_os_release_get_version_id (GsOsRelease *os_release)
+{
+       g_return_val_if_fail (GS_IS_OS_RELEASE (os_release), NULL);
+       return os_release->version_id;
+}
 
-       g_set_error (error,
-                    GS_OS_RELEASE_ERROR,
-                    GS_OS_RELEASE_ERROR_FAILED,
-                    "could not find variable '%s' in %s", variable, filename);
-       return NULL;
+/**
+ * gs_os_release_get_pretty_name:
+ * @os_release: A #GsOsRelease
+ *
+ * Gets the pretty name from the os-release parser.
+ *
+ * Returns: a string, or %NULL
+ **/
+const gchar *
+gs_os_release_get_pretty_name (GsOsRelease *os_release)
+{
+       g_return_val_if_fail (GS_IS_OS_RELEASE (os_release), NULL);
+       return os_release->pretty_name;
 }
 
-gchar *
-gs_os_release_get_name (GError **error)
+/**
+ * gs_os_release_finalize:
+ **/
+static void
+gs_os_release_finalize (GObject *object)
 {
-       return gs_os_release_parse_variable ("NAME", error);
+       GsOsRelease *os_release = GS_OS_RELEASE (object);
+       g_free (os_release->name);
+       g_free (os_release->version);
+       g_free (os_release->id);
+       g_free (os_release->version_id);
+       g_free (os_release->pretty_name);
+       G_OBJECT_CLASS (gs_os_release_parent_class)->finalize (object);
 }
 
-gchar *
-gs_os_release_get_version (GError **error)
+/**
+ * gs_os_release_class_init:
+ **/
+static void
+gs_os_release_class_init (GsOsReleaseClass *klass)
 {
-       return gs_os_release_parse_variable ("VERSION", error);
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = gs_os_release_finalize;
 }
 
-gchar *
-gs_os_release_get_id (GError **error)
+/**
+ * gs_os_release_initable_iface_init:
+ **/
+static void
+gs_os_release_initable_iface_init (GInitableIface *iface)
 {
-       return gs_os_release_parse_variable ("ID", error);
+       iface->init = gs_os_release_initable_init;
 }
 
-gchar *
-gs_os_release_get_version_id (GError **error)
+/**
+ * gs_os_release_init:
+ **/
+static void
+gs_os_release_init (GsOsRelease *os_release)
 {
-       return gs_os_release_parse_variable ("VERSION_ID", error);
 }
 
-gchar *
-gs_os_release_get_pretty_name (GError **error)
+/**
+ * gs_os_release_new:
+ * @error: a #GError, or %NULL
+ *
+ * Creates a new os_release.
+ *
+ * Returns: A newly allocated #GsOsRelease
+ **/
+GsOsRelease *
+gs_os_release_new (GError **error)
 {
-       return gs_os_release_parse_variable ("PRETTY_NAME", error);
+       GsOsRelease *os_release;
+       os_release = g_initable_new (GS_TYPE_OS_RELEASE, NULL, error, NULL);
+       return GS_OS_RELEASE (os_release);
 }
 
 /* vim: set noexpandtab: */
diff --git a/src/gs-os-release.h b/src/gs-os-release.h
index cc9b7c7..ffd338e 100644
--- a/src/gs-os-release.h
+++ b/src/gs-os-release.h
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2016 Kalev Lember <klember redhat com>
+ * Copyright (C) 2016 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU Lesser General Public License Version 2.1
  *
@@ -22,23 +23,22 @@
 #ifndef __GS_OS_RELEASE_H
 #define __GS_OS_RELEASE_H
 
-#include <glib.h>
+#include <glib-object.h>
+
+#include "gs-app.h"
 
 G_BEGIN_DECLS
 
-#define GS_OS_RELEASE_ERROR gs_os_release_error_quark ()
+#define GS_TYPE_OS_RELEASE (gs_os_release_get_type ())
 
-typedef enum
-{
-       GS_OS_RELEASE_ERROR_FAILED
-} GsOsReleaseError;
+G_DECLARE_FINAL_TYPE (GsOsRelease, gs_os_release, GS, OS_RELEASE, GObject)
 
-GQuark           gs_os_release_error_quark                     (void);
-gchar           *gs_os_release_get_name                (GError         **error);
-gchar           *gs_os_release_get_version             (GError         **error);
-gchar           *gs_os_release_get_id                  (GError         **error);
-gchar           *gs_os_release_get_version_id          (GError         **error);
-gchar           *gs_os_release_get_pretty_name         (GError         **error);
+GsOsRelease    *gs_os_release_new              (GError         **error);
+const gchar    *gs_os_release_get_name         (GsOsRelease    *os_release);
+const gchar    *gs_os_release_get_version      (GsOsRelease    *os_release);
+const gchar    *gs_os_release_get_id           (GsOsRelease    *os_release);
+const gchar    *gs_os_release_get_version_id   (GsOsRelease    *os_release);
+const gchar    *gs_os_release_get_pretty_name  (GsOsRelease    *os_release);
 
 G_END_DECLS
 
diff --git a/src/gs-plugin.c b/src/gs-plugin.c
index d5027f6..5fe945e 100644
--- a/src/gs-plugin.c
+++ b/src/gs-plugin.c
@@ -611,12 +611,20 @@ gboolean
 gs_plugin_check_distro_id (GsPlugin *plugin, const gchar *distro_id)
 {
        g_autoptr(GError) error = NULL;
-       g_autofree gchar *id = NULL;
+       g_autoptr(GsOsRelease) os_release = NULL;
+       const gchar *id = NULL;
+
+       /* load /etc/os-release */
+       os_release = gs_os_release_new (&error);
+       if (os_release == NULL) {
+               g_debug ("could not parse os-release: %s", error->message);
+               return FALSE;
+       }
 
        /* check that we are running on Fedora */
-       id = gs_os_release_get_id (&error);
+       id = gs_os_release_get_id (os_release);
        if (id == NULL) {
-               g_debug ("could not parse os-release: %s", error->message);
+               g_debug ("could not get distro ID");
                return FALSE;
        }
        if (g_strcmp0 (id, distro_id) != 0)
diff --git a/src/gs-self-test.c b/src/gs-self-test.c
index cc1882a..1222555 100644
--- a/src/gs-self-test.c
+++ b/src/gs-self-test.c
@@ -28,6 +28,7 @@
 
 #include "gs-app-private.h"
 #include "gs-app-list-private.h"
+#include "gs-os-release.h"
 #include "gs-plugin.h"
 #include "gs-plugin-loader.h"
 #include "gs-plugin-loader-sync.h"
@@ -104,6 +105,27 @@ gs_app_list_filter_cb (GsApp *app, gpointer user_data)
 }
 
 static void
+gs_os_release_func (void)
+{
+       g_autofree gchar *fn = NULL;
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GsOsRelease) os_release = NULL;
+
+       fn = gs_test_get_filename ("tests/os-release");
+       g_assert (fn != NULL);
+       g_setenv ("GS_SELF_TEST_OS_RELEASE_FILENAME", fn, TRUE);
+
+       os_release = gs_os_release_new (&error);
+       g_assert_no_error (error);
+       g_assert (os_release != NULL);
+       g_assert_cmpstr (gs_os_release_get_id (os_release), ==, "fedora");
+       g_assert_cmpstr (gs_os_release_get_name (os_release), ==, "Fedora");
+       g_assert_cmpstr (gs_os_release_get_version (os_release), ==, "25 (Workstation Edition)");
+       g_assert_cmpstr (gs_os_release_get_version_id (os_release), ==, "25");
+       g_assert_cmpstr (gs_os_release_get_pretty_name (os_release), ==, "Fedora 25 (Workstation Edition)");
+}
+
+static void
 gs_plugin_func (void)
 {
        GsAppList *list;
@@ -891,6 +913,7 @@ main (int argc, char **argv)
        g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
 
        /* generic tests go here */
+       g_test_add_func ("/gnome-software/os-release", gs_os_release_func);
        g_test_add_func ("/gnome-software/app", gs_app_func);
        g_test_add_func ("/gnome-software/plugin", gs_plugin_func);
 
diff --git a/src/plugins/gs-plugin-fedora-distro-upgrades.c b/src/plugins/gs-plugin-fedora-distro-upgrades.c
index dc96aa1..5429ca3 100644
--- a/src/plugins/gs-plugin-fedora-distro-upgrades.c
+++ b/src/plugins/gs-plugin-fedora-distro-upgrades.c
@@ -94,9 +94,10 @@ gboolean
 gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
+       const gchar *verstr = NULL;
        gchar *endptr = NULL;
-       g_autofree gchar *verstr = NULL;
        g_autoptr(GFile) file = NULL;
+       g_autoptr(GsOsRelease) os_release = NULL;
 
        /* get the file to cache */
        priv->cachefn = gs_utils_get_cache_filename ("upgrades",
@@ -118,10 +119,13 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
                          G_CALLBACK (gs_plugin_fedora_distro_upgrades_changed_cb), plugin);
 
        /* read os-release for the current versions */
-       priv->os_name = gs_os_release_get_name (error);
+       os_release = gs_os_release_new (error);
+       if (os_release == NULL)
+               return FALSE;
+       priv->os_name = g_strdup (gs_os_release_get_name (os_release));
        if (priv->os_name == NULL)
                return FALSE;
-       verstr = gs_os_release_get_version_id (error);
+       verstr = gs_os_release_get_version_id (os_release);
        if (verstr == NULL)
                return FALSE;
 
diff --git a/src/plugins/gs-plugin-odrs.c b/src/plugins/gs-plugin-odrs.c
index a776492..13ad8a4 100644
--- a/src/plugins/gs-plugin-odrs.c
+++ b/src/plugins/gs-plugin-odrs.c
@@ -48,6 +48,7 @@ gs_plugin_initialize (GsPlugin *plugin)
 {
        GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData));
        g_autoptr(GError) error = NULL;
+       g_autoptr(GsOsRelease) os_release = NULL;
 
        priv->settings = g_settings_new ("org.gnome.software");
        priv->review_server = g_settings_get_string (priv->settings,
@@ -61,9 +62,14 @@ gs_plugin_initialize (GsPlugin *plugin)
        }
 
        /* get the distro name (e.g. 'Fedora') but allow a fallback */
-       priv->distro = gs_os_release_get_name (&error);
+       os_release = gs_os_release_new (&error);
+       if (os_release == NULL) {
+               g_warning ("failed to get distro name: %s", error->message);
+               priv->distro = g_strdup ("Unknown");
+       }
+       priv->distro = g_strdup (gs_os_release_get_name (os_release));
        if (priv->distro == NULL) {
-               g_warning ("Failed to get distro name: %s", error->message);
+               g_warning ("failed to get distro name");
                priv->distro = g_strdup ("Unknown");
        }
 }


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