[SATYR PATCH 2/4] Add support for parsing /etc/os-release

Martin Milata mmilata at redhat.com
Tue Aug 20 16:40:34 UTC 2013


Actual parsing code taken from libreport, originally written by Jakub
Filak.

Implementing the parser with callback allows reusing it in libreport without
bringing in the libreport-specific hacks and workarounds.

Related to #94.

Signed-off-by: Martin Milata <mmilata at redhat.com>
---
 include/operating_system.h |  3 ++
 include/utils.h            | 10 ++++++
 lib/abrt.c                 | 31 ++++++++++++------
 lib/operating_system.c     | 31 ++++++++++++++++++
 lib/utils.c                | 79 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/operating_system.at  | 57 +++++++++++++++++++++++++++++++++
 6 files changed, 202 insertions(+), 9 deletions(-)

diff --git a/include/operating_system.h b/include/operating_system.h
index a2f5e2c..ff8e3ef 100644
--- a/include/operating_system.h
+++ b/include/operating_system.h
@@ -52,6 +52,9 @@ bool
 sr_operating_system_parse_etc_system_release(const char *etc_system_release,
                                              char **name,
                                              char **version);
+bool
+sr_operating_system_parse_etc_os_release(const char *etc_os_release,
+                                         struct sr_operating_system *operating_system);
 
 #ifdef __cplusplus
 }
diff --git a/include/utils.h b/include/utils.h
index 5684620..fe8b13f 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -381,6 +381,16 @@ sr_indent_except_first_line(const char *input, int spaces);
 char *
 sr_build_path(const char *first_element, ...);
 
+/**
+ * Parses /etc/os-release file, see
+ * http://www.freedesktop.org/software/systemd/man/os-release.html for more
+ * information. Calls callback for each key-value pair it reads from the file.
+ */
+void
+sr_parse_os_release(const char *input,
+                    void (*callback)(char*, char*, void*),
+                    void *data);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/abrt.c b/lib/abrt.c
index 1b13c37..cdee0c6 100644
--- a/lib/abrt.c
+++ b/lib/abrt.c
@@ -278,17 +278,30 @@ struct sr_operating_system *
 sr_abrt_operating_system_from_dir(const char *directory,
                                   char **error_message)
 {
-    char *release_contents = file_contents(directory, "os_release",
-                                           error_message);
-    if (!release_contents)
-        return NULL;
-
+    bool success = false;
     struct sr_operating_system *os = sr_operating_system_new();
-    bool success = sr_operating_system_parse_etc_system_release(release_contents,
-                                                                &os->name,
-                                                                &os->version);
 
-    free(release_contents);
+    char *osinfo_contents = file_contents(directory, "os_info", error_message);
+    if (osinfo_contents)
+    {
+        success = sr_operating_system_parse_etc_os_release(osinfo_contents, os);
+        free(osinfo_contents);
+    }
+
+    /* fall back to os_release if parsing os_info fails */
+    if (!success)
+    {
+        char *release_contents = file_contents(directory, "os_release",
+                                               error_message);
+        if (release_contents)
+        {
+            success = sr_operating_system_parse_etc_system_release(release_contents,
+                                                                   &os->name,
+                                                                   &os->version);
+            free(release_contents);
+        }
+    }
+
     if (!success)
     {
         sr_operating_system_free(os);
diff --git a/lib/operating_system.c b/lib/operating_system.c
index cfb33bc..a93175c 100644
--- a/lib/operating_system.c
+++ b/lib/operating_system.c
@@ -22,6 +22,7 @@
 #include "utils.h"
 #include "json.h"
 #include "strbuf.h"
+#include "internal_utils.h"
 #include <string.h>
 #include <ctype.h>
 #include <stddef.h>
@@ -133,3 +134,33 @@ sr_operating_system_parse_etc_system_release(const char *etc_system_release,
     *version = sr_strndup(version_begin, version_end - version_begin);
     return true;
 }
+
+static void
+os_release_callback(char *key, char *value, void *data)
+{
+    struct sr_operating_system *operating_system =
+        (struct sr_operating_system *)data;
+
+    if (0 == strcmp(key, "ID"))
+    {
+        operating_system->name = value;
+    }
+    else if (0 == strcmp(key, "VERSION_ID"))
+    {
+        operating_system->version = value;
+    }
+    else
+    {
+        free(value);
+    }
+    free(key);
+}
+
+bool
+sr_operating_system_parse_etc_os_release(const char *etc_os_release,
+                                         struct sr_operating_system *operating_system)
+{
+    sr_parse_os_release(etc_os_release, os_release_callback, (void*)operating_system);
+
+    return (operating_system->name && operating_system->version);
+}
diff --git a/lib/utils.c b/lib/utils.c
index 206c1fa..888d24d 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -706,3 +706,82 @@ sr_build_path(const char *first_element, ...)
     va_end(elements);
     return sr_strbuf_free_nobuf(strbuf);
 }
+
+static void
+unescape_osinfo_value(const char *source, char* dest)
+{
+    while (source[0] != '\0')
+    {
+        if (source[0] == '\\')
+        {   /* some characters may be escaped -> remove '\' */
+            ++source;
+            if (source[0] == '\0')
+                break;
+            *dest++ = *source++;
+        }
+        else if (source[0] == '\'' || source[0] == '"')
+        {   /* skip non escaped quotes and don't care where they are */
+            ++source;
+        }
+        else
+        {
+            *dest++ = *source++;
+        }
+    }
+    dest[0] = '\0';
+}
+
+void
+sr_parse_os_release(const char *input, void (*callback)(char*, char*, void*),
+                    void *data)
+{
+    const char *cursor = input;
+    unsigned line = 0;
+    while (cursor[0] != '\0')
+    {
+        ++line;
+        if (cursor[0] == '#')
+            goto skip_line;
+
+        const char *key_end = strchrnul(cursor, '=');
+        if (key_end[0] == '\0')
+        {
+            warn("os-release:%u: non empty last line", line);
+            break;
+        }
+
+        if (key_end - cursor == 0)
+        {
+            warn("os-release:%u: 0 length key", line);
+            goto skip_line;
+        }
+
+        const char *value_end = strchrnul(cursor, '\n');
+        if (key_end > value_end)
+        {
+            warn("os-release:%u: missing '='", line);
+            goto skip_line;
+        }
+
+        char *key = sr_strndup(cursor, key_end - cursor);
+        char *value = sr_strndup(key_end + 1, value_end - key_end - 1);
+        unescape_osinfo_value(value, value);
+
+        warn("os-release:%u: parsed line: '%s'='%s'", line, key, value);
+
+        callback(key, value, data);
+
+        cursor = value_end;
+        if (value_end[0] == '\0')
+        {
+            warn("os-release:%u: the last value is not terminated by newline", line);
+        }
+        else
+            ++cursor;
+
+        continue;
+  skip_line:
+        cursor = strchrnul(cursor, '\n');
+        cursor += (cursor[0] != '\0');
+    }
+}
diff --git a/tests/operating_system.at b/tests/operating_system.at
index 063f4c9..0c175c3 100644
--- a/tests/operating_system.at
+++ b/tests/operating_system.at
@@ -38,3 +38,60 @@ main(void)
     return 0;
 }
 ]])
+
+## ---------------------------------------- ##
+## sr_operating_system_parse_etc_os_release ##
+##----------------------------------------- ##
+AT_TESTFUN([sr_operating_system_parse_etc_os_release],
+[[
+#include "operating_system.h"
+#include "utils.h"
+#include <stdio.h>
+#include <assert.h>
+
+void check(const char *etc_os_release,
+           const char *expected_name,
+           const char *expected_version)
+{
+    struct sr_operating_system *os = sr_operating_system_new();
+    bool success = sr_operating_system_parse_etc_os_release(
+        etc_os_release, os);
+
+    assert(success);
+    assert(0 == strcmp(os->name, expected_name));
+    assert(0 == strcmp(os->version, expected_version));
+    sr_operating_system_free(os);
+}
+
+int
+main(void)
+{
+    char *f19 =
+"NAME=Fedora\n"
+"VERSION=\"19 (Schrödinger’s Cat)\"\n"
+"ID=fedora\n"
+"VERSION_ID=19\n"
+"PRETTY_NAME=\"Fedora 19 (Schrödinger’s Cat)\"\n"
+"ANSI_COLOR=\"0;34\"\n"
+"CPE_NAME=\"cpe:/o:fedoraproject:fedora:19\"\n";
+
+    char *el7 =
+"NAME=\"Red Hat Enterprise Linux Workstation\"\n"
+"VERSION=\"7.0 (Codename)\"\n"
+"ID=\"rhel\"\n"
+"VERSION_ID=\"7.0\"\n"
+"PRETTY_NAME=\"Red Hat Enterprise Linux Workstation 7.0 (Codename)\"\n"
+"ANSI_COLOR=\"0;31\"\n"
+"CPE_NAME=\"cpe:/o:redhat:enterprise_linux:7.0:beta:workstation\"\n"
+"\n"
+"REDHAT_BUGZILLA_PRODUCT=\"Red Hat Enterprise Linux 7\"\n"
+"REDHAT_BUGZILLA_PRODUCT_VERSION=7.0\n"
+"REDHAT_SUPPORT_PRODUCT=\"Red Hat Enterprise Linux\"\n"
+"REDHAT_SUPPORT_PRODUCT_VERSION=7.0\n";
+
+    check(f19, "fedora", "19");
+    check(el7, "rhel", "7.0");
+
+    return 0;
+}
+]])
-- 
1.8.3.1



More information about the Crash-catcher mailing list