fileio: make get_status_field() more generic
authorAaro Koskinen <aaro.koskinen@nokia.com>
Wed, 30 Sep 2015 12:57:55 +0000 (15:57 +0300)
committerAaro Koskinen <aaro.koskinen@nokia.com>
Wed, 30 Sep 2015 12:57:55 +0000 (15:57 +0300)
All users of get_status_field() expect the field pattern to occur in
the beginning of a line, and the delimiter is ':'.

Hardcode this into the function, and also skip any whitespace before ':'
to support fields in files like /proc/cpuinfo. Add support for returning
the full field value (currently stops on first whitespace).

Rename the function so it's easier to ensure all callers switch to new
semantics.

src/basic/fileio.c
src/basic/fileio.h
src/basic/process-util.c
src/basic/virt.c
src/shared/sleep-config.c
src/test/test-fileio.c

index 4a9105f..13a85e1 100644 (file)
@@ -775,15 +775,19 @@ int executable_is_script(const char *path, char **interpreter) {
 
 /**
  * Retrieve one field from a file like /proc/self/status.  pattern
- * should start with '\n' and end with a ':'. Whitespace and zeros
- * after the ':' will be skipped. field must be freed afterwards.
+ * should not include whitespace or the delimiter (':'). pattern matches only
+ * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
+ * zeros after the ':' will be skipped. field must be freed afterwards.
+ * terminator specifies the terminating characters of the field value (not
+ * included in the value).
  */
-int get_status_field(const char *filename, const char *pattern, char **field) {
+int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
         _cleanup_free_ char *status = NULL;
         char *t, *f;
         size_t len;
         int r;
 
+        assert(terminator);
         assert(filename);
         assert(pattern);
         assert(field);
@@ -792,11 +796,31 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
         if (r < 0)
                 return r;
 
-        t = strstr(status, pattern);
-        if (!t)
-                return -ENOENT;
+        t = status;
+
+        do {
+                bool pattern_ok;
+
+                do {
+                        t = strstr(t, pattern);
+                        if (!t)
+                                return -ENOENT;
+
+                        /* Check that pattern occurs in beginning of line. */
+                        pattern_ok = (t == status || t[-1] == '\n');
+
+                        t += strlen(pattern);
+
+                } while (!pattern_ok);
+
+                t += strspn(t, " \t");
+                if (!*t)
+                        return -ENOENT;
+
+        } while (*t != ':');
+
+        t++;
 
-        t += strlen(pattern);
         if (*t) {
                 t += strspn(t, " \t");
 
@@ -812,7 +836,7 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
                         t --;
         }
 
-        len = strcspn(t, WHITESPACE);
+        len = strcspn(t, terminator);
 
         f = strndup(t, len);
         if (!f)
index 2e8148f..4998d4d 100644 (file)
@@ -48,4 +48,4 @@ int write_env_file(const char *fname, char **l);
 
 int executable_is_script(const char *path, char **interpreter);
 
-int get_status_field(const char *filename, const char *pattern, char **field);
+int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
index cff2d2a..d8a94a4 100644 (file)
@@ -215,7 +215,7 @@ int get_process_capeff(pid_t pid, char **capeff) {
 
         p = procfs_file_alloca(pid, "status");
 
-        r = get_status_field(p, "\nCapEff:", capeff);
+        r = get_proc_field(p, "CapEff", WHITESPACE, capeff);
         if (r == -ENOENT)
                 return -ESRCH;
 
index 1fc6c1b..7054317 100644 (file)
@@ -240,7 +240,7 @@ static int detect_vm_zvm(void) {
         _cleanup_free_ char *t = NULL;
         int r;
 
-        r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
+        r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
         if (r == -ENOENT)
                 return VIRTUALIZATION_NONE;
         if (r < 0)
index 1064fd5..3dedbd1 100644 (file)
@@ -226,7 +226,7 @@ static bool enough_memory_for_hibernation(void) {
         if (r < 0)
                 return false;
 
-        r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
+        r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
         if (r < 0) {
                 log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
                 return false;
index be3a879..ad54782 100644 (file)
@@ -241,18 +241,18 @@ static void test_status_field(void) {
         unsigned long long total = 0, buffers = 0;
         int r;
 
-        assert_se(get_status_field("/proc/self/status", "\nThreads:", &t) == 0);
+        assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0);
         puts(t);
         assert_se(streq(t, "1"));
 
-        r = get_status_field("/proc/meminfo", "MemTotal:", &p);
+        r = get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE, &p);
         if (r != -ENOENT) {
                 assert_se(r == 0);
                 puts(p);
                 assert_se(safe_atollu(p, &total) == 0);
         }
 
-        r = get_status_field("/proc/meminfo", "\nBuffers:", &s);
+        r = get_proc_field("/proc/meminfo", "Buffers", WHITESPACE, &s);
         if (r != -ENOENT) {
                 assert_se(r == 0);
                 puts(s);
@@ -263,7 +263,7 @@ static void test_status_field(void) {
                 assert_se(buffers < total);
 
         /* Seccomp should be a good test for field full of zeros. */
-        r = get_status_field("/proc/meminfo", "\nSeccomp:", &z);
+        r = get_proc_field("/proc/meminfo", "Seccomp", WHITESPACE, &z);
         if (r != -ENOENT) {
                 assert_se(r == 0);
                 puts(z);