Hostinfo day 2: Implement the daemon.
[virt-hostinfo.git] / hostinfod / configuration.c
index 14a2b80..0d71908 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <apr_general.h>
+#include <apr_strings.h>
 
 #include "hostinfod.h"
 
-typedef int (*process_line_fn) (const char *key, const char *value);
-static void process_conf_file (const char *path, int exit_if_not_exist, process_line_fn process_line);
+typedef int (*process_line_fn) (const char *path, int lineno,
+                               const char *key, const char *value);
+typedef int (*process_section_fn) (const char *path, int lineno,
+                                  const char *section);
+static void process_conf_file (const char *path, int exit_if_not_exist,
+                              process_line_fn process_line,
+                              process_section_fn process_section);
+static int get_bool (const char *str);
+
+/* Read the main configuration file. */
+static int main_process_line (const char *path, int lineno, const char *key, const char *value);
 
-/* Read the main configuration file.  This file is NOT reread if it
- * changes, but the auxiliary guests.conf file IS reread (and so is
- * the socket directory of course).
- */
 void
 read_main_conf_file (void)
 {
-  process_conf_file (conf_file, 0, NULL);
+  process_conf_file (conf_file, 0, main_process_line, NULL);
+}
+
+static int
+main_process_line (const char *path, int lineno,
+                  const char *key, const char *value)
+{
+  int bool;
+
+  if (strcasecmp (key, "guests") == 0) {
+    if (!value) {
+      error ("%s:%d: directive is empty: %s", path, lineno, key);
+      return -1;
+    }
+    guests_file = apr_pstrdup (pool, value);
+  } else if (strcasecmp (key, "sockets") == 0) {
+    if (!value) {
+      error ("%s:%d: directive is empty: %s", path, lineno, key);
+      return -1;
+    }
+    socket_dir = apr_pstrdup (pool, value);
+  } else if (strcasecmp (key, "verbose") == 0) {
+    bool = get_bool (value);
+    if (bool == -1) {
+      error ("%s:%d: %s: not a valid boolean - use 1 or 0", path, lineno, key);
+      return -1;
+    }
+    if (!verbose_set_on_cmdline)
+      verbose = bool;
+  } else if (strcasecmp (key, "foreground") == 0) {
+    bool = get_bool (value);
+    if (bool == -1) {
+      error ("%s:%d: %s: not a valid boolean - use 1 or 0", path, lineno, key);
+      return -1;
+    }
+    if (!foreground_set_on_cmdline)
+      foreground = bool;
+  } else {
+    error ("%s:%d: unknown directive in configuration file: %s",
+          path, lineno, key);
+    return -1;
+  }
+  return 0;
 }
 
+/* Configuration file parser. */
 static void
 process_conf_file (const char *path, int exit_if_not_exist,
-                  process_line_fn process_line)
+                  process_line_fn process_line,
+                  process_section_fn process_section)
 {
   static const char *whitespace = " \t\n\v";
   FILE *fp;
   char *line = NULL;
+  int lineno = 0;
   size_t len = 0;
   size_t real_len, key_len;
   ssize_t r;
   const char *key, *value;
 
+  debug ("begin processing configuration file %s", path);
+
   fp = fopen (path, "r");
   if (!fp) {
     if (exit_if_not_exist) {
-      perror (path);
+      perrorf ("%s", path);
       exit (1);
-    }
+    } else
+      pwarningf ("%s", path);
     return;
   }
 
   while ((r = getline (&line, &len, fp)) != -1) {
+    lineno++;
+
     /* Remove trailing \n */
     real_len = len;
     if (real_len > 0 && line[real_len-1] == '\n')
@@ -74,29 +131,69 @@ process_conf_file (const char *path, int exit_if_not_exist,
     if (line[0] == '#' || strspn (line, whitespace) == real_len)
       continue;
 
-    /* Split into key value. */
-    key_len = strcspn (line, whitespace);
-    line[key_len] = '\0';
-    key = line;
-    value = key_len < real_len ? &line[key_len+1] : NULL;
-    if (value) {
-      value += strspn (line, whitespace);
-      if (value[0] == '\0')
-       value = NULL;
+    if (line[0] == '[') {      /* Section. */
+      if (line[real_len-1] == ']')
+       line[--real_len] = '\0';
+      else {
+       error ("%s:%d: in section header, ']' not found (is there trailing whitespace or a comment?), near: %s",
+              path, lineno, line);
+       exit (1);
+      }
+
+      debug ("configuration file: section [%s]", line);
+
+      if (process_section && process_section (path, lineno, line) == -1)
+       exit (1);
     }
+    else {                     /* Key value */
+      key_len = strcspn (line, whitespace);
+      line[key_len] = '\0';
+      key = line;
+      value = key_len < real_len ? &line[key_len+1] : NULL;
+      if (value) {
+       value += strspn (line, whitespace);
+       if (value[0] == '\0')
+         value = NULL;
+      }
 
-    if (process_line && process_line (key, value) == -1)
-      exit (1);
+      debug ("configuration file: key '%s', value '%s'", key, value);
+
+      if (process_line && process_line (path, lineno, key, value) == -1)
+       exit (1);
+    }
   }
 
   free (line);
 
   if (ferror (fp)) {
-    fprintf (stderr, "error reading configuration file: %s\n", path);
+    error ("%s: error reading configuration file", path);
     exit (1);
   }
   if (fclose (fp) == EOF) {
-    perror (path);
+    perrorf ("%s", path);
     exit (1);
   }
+
+  debug ("finished processing configuration file successfully");
+}
+
+static int
+get_bool (const char *str)
+{
+  if (!str)
+    return -1;
+
+  if (strcasecmp (str, "on") == 0)
+    return 1;
+  if (strcasecmp (str, "off") == 0)
+    return 0;
+
+  switch (str[0]) {
+  case '1': case 'y': case 'Y': case 't': case 'T': case 'e': case 'E':
+    return 1;
+  case '0': case 'n': case 'N': case 'f': case 'F': case 'd': case 'D':
+    return 0;
+  }
+
+  return -1;
 }