Use json_is_true() in place of json_boolean_value() for compatibility
[trust_router.git] / tr / tr_cfgwatch.c
index 3439112..b0b7e34 100644 (file)
@@ -1,4 +1,36 @@
-/***** config file watching *****/
+/*
+ * Copyright (c) 2016, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 
 #include <sys/stat.h>
 #include <talloc.h>
@@ -14,21 +46,10 @@ TR_CFGWATCH *tr_cfgwatch_create(TALLOC_CTX *mem_ctx)
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_CFGWATCH *new_cfg;
   
-  new_cfg=talloc(tmp_ctx, TR_CFGWATCH);
+  new_cfg=talloc_zero(tmp_ctx, TR_CFGWATCH);
   if (new_cfg == NULL) {
     tr_debug("tr_cfgwatch_create: Allocation failed.");
-  } else {
-    timerclear(&new_cfg->poll_interval);
-    timerclear(&new_cfg->settling_time);
-    new_cfg->config_dir=NULL;
-    new_cfg->fstat_list=NULL;
-    new_cfg->n_files=0;
-    new_cfg->change_detected=0;
-    timerclear(&new_cfg->last_change_detected);
-    new_cfg->ctx=NULL;
-    new_cfg->tr=NULL;
-  }
-
+  } 
   talloc_steal(mem_ctx, new_cfg);
   talloc_free(tmp_ctx);
   return new_cfg;
@@ -142,7 +163,7 @@ static int tr_cfgwatch_update_needed(TR_CFGWATCH *cfg_status)
     talloc_free(cfg_status->fstat_list);
     cfg_status->n_files=n_files;
     cfg_status->fstat_list=fstat_list;
-    talloc_steal(cfg_status->ctx, fstat_list);
+    talloc_steal(cfg_status, fstat_list);
     goto cleanup;
   }
 
@@ -154,7 +175,7 @@ static int tr_cfgwatch_update_needed(TR_CFGWATCH *cfg_status)
       talloc_free(cfg_status->fstat_list);
       cfg_status->n_files=n_files;
       cfg_status->fstat_list=fstat_list;
-      talloc_steal(cfg_status->ctx, fstat_list);
+      talloc_steal(cfg_status, fstat_list);
       goto cleanup;
     }
   }
@@ -164,15 +185,64 @@ static int tr_cfgwatch_update_needed(TR_CFGWATCH *cfg_status)
   talloc_free(tmp_ctx);
   return update_needed;
 }
+
+/* Join two paths and return a pointer to the result. This should be freed
+ * via talloc_free. Returns NULL on failure. */
+static char *join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2)
+{
+  return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
+}
+
+/**
+ * Join a directory name with the filenames from an array of struct dirent.
+ * Outputs an array of pointers to strings that must be freed via talloc_free on
+ * the array. The strings in the array are in the context of the array, so will
+ * be freed automatically.
+ *
+ * @param ctx talloc context to contain the result on success
+ * @param dir
+ * @param n_files
+ * @param files
+ * @return Null on failure, or an array of pointers to strings
+ */
+static char **dirent_to_full_path(TALLOC_CTX *mem_ctx, const char *dir, unsigned int n_files, struct dirent **files)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  unsigned int ii=0;
+  char **files_with_paths=talloc_array(tmp_ctx, char *, n_files);
+
+  if (files_with_paths==NULL) {
+    tr_crit("dirent_to_full_path: unable to allocate filename array");
+    goto cleanup;
+  }
+
+  for (ii=0; ii<n_files; ii++) {
+    files_with_paths[ii]=join_paths(files_with_paths, dir, files[ii]->d_name);
+    if(files_with_paths[ii] == NULL) {
+      tr_crit("dirent_to_full_path: error joining path for %s.", files[ii]->d_name);
+      files_with_paths=NULL; /* will be freed automatically by talloc_free */
+      goto cleanup;
+    }
+  }
+
+cleanup:
+  if (files_with_paths!=NULL)
+    talloc_steal(mem_ctx, files_with_paths);
+  talloc_free(tmp_ctx);
+  return files_with_paths;
+}
+
+
 /* must specify the ctx and tr in cfgwatch! */
 int tr_read_and_apply_config(TR_CFGWATCH *cfgwatch)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   char *config_dir=cfgwatch->config_dir;
-  int n_files = 0;
+  unsigned int n_files = 0;
   struct dirent **cfg_files=NULL;
   TR_CFG_RC rc = TR_CFG_SUCCESS;       /* presume success */
   struct tr_fstat *new_fstat_list=NULL;
+  char **files_with_paths=NULL;
   int retval=0;
 
   /* find the configuration files -- n.b., tr_find_config_files()
@@ -183,37 +253,40 @@ int tr_read_and_apply_config(TR_CFGWATCH *cfgwatch)
     tr_debug("tr_read_and_apply_config: No configuration files.");
     retval=1; goto cleanup;
   }
+  /* n_files > 0 from here on */
 
   /* Get the list of update times.
    * Do this before loading in case they change between obtaining their timestamp
    * and reading the file---this way they will immediately reload if this happens. */
-  new_fstat_list=tr_fstat_get_all(tmp_ctx, config_dir, cfg_files, n_files);
+  new_fstat_list=tr_fstat_get_all(tmp_ctx, config_dir, cfg_files, (unsigned int)n_files);
   if (new_fstat_list==NULL) {
     tr_debug("tr_read_and_apply_config: Could not allocate config file status list.");
     retval=1; goto cleanup;
   }
-  
-  /* allocate a new configuration, dumping an old one if needed */
-  if(cfgwatch->tr->new_cfg != NULL)
-    tr_cfg_free(cfgwatch->tr->new_cfg);
-  cfgwatch->tr->new_cfg=tr_cfg_new(tmp_ctx);
-  if (cfgwatch->tr->new_cfg==NULL) {
-    tr_debug("tr_read_and_apply_config: Error allocating new_cfg.");
+
+  /* get the filenames with their paths */
+  files_with_paths=dirent_to_full_path(tmp_ctx, config_dir, n_files, cfg_files);
+  if (files_with_paths==NULL) {
+    tr_err("tr_read_and_apply_config: Could not append path to filenames.");
     retval=1; goto cleanup;
   }
-  /* now fill it in */
-  if (TR_CFG_SUCCESS != (rc = tr_parse_config(cfgwatch->tr->new_cfg, config_dir, n_files, cfg_files))) {
+
+  /* now fill it in (tr_parse_config allocates space for new config) */
+  if (TR_CFG_SUCCESS != (rc = tr_parse_config(cfgwatch->cfg_mgr, n_files, files_with_paths))) {
     tr_debug("tr_read_and_apply_config: Error parsing configuration information, rc=%d.", rc);
     retval=1; goto cleanup;
   }
 
-  /* apply initial configuration (frees active_cfg and resets new_cfg to NULL) */
-  if (TR_CFG_SUCCESS != (rc = tr_apply_new_config(&cfgwatch->tr->active_cfg,
-                                                 &cfgwatch->tr->new_cfg))) {
+  /* apply new configuration (nulls new, manages context ownership) */
+  if (TR_CFG_SUCCESS != (rc = tr_apply_new_config(cfgwatch->cfg_mgr))) {
     tr_debug("tr_read_and_apply_config: Error applying configuration, rc = %d.", rc);
     retval=1; goto cleanup;
   }
-  talloc_steal(cfgwatch->ctx, cfgwatch->tr->active_cfg); /* hand over ownership */
+
+  /* call callback to notify system of new configuration */
+  tr_debug("tr_read_and_apply_config: calling update callback function.");
+  if (cfgwatch->update_cb!=NULL)
+    cfgwatch->update_cb(cfgwatch->cfg_mgr->active, cfgwatch->update_cookie);
 
   /* give ownership of the new_fstat_list to caller's context */
   if (cfgwatch->fstat_list != NULL) {
@@ -222,13 +295,13 @@ int tr_read_and_apply_config(TR_CFGWATCH *cfgwatch)
   }
   cfgwatch->n_files=n_files;
   cfgwatch->fstat_list=new_fstat_list;
-  talloc_steal(cfgwatch->ctx, new_fstat_list);
+  talloc_steal(cfgwatch, new_fstat_list);
   new_fstat_list=NULL;
 
  cleanup:
   tr_free_config_file_list(n_files, &cfg_files);
   talloc_free(tmp_ctx);
-  cfgwatch->tr->new_cfg=NULL; /* this has been freed, either explicitly or with tmp_ctx */
+  cfgwatch->cfg_mgr->new=NULL; /* this has been freed, either explicitly or with tmp_ctx */
   return retval;
 }
 
@@ -240,12 +313,10 @@ static void tr_cfgwatch_event_cb(int listener, short event, void *arg)
 
   if (tr_cfgwatch_update_needed(cfg_status)) {
     tr_notice("Configuration file change detected, waiting for changes to settle.");
-    /*    if (!cfg_status->change_detected) {*/
-      cfg_status->change_detected=1;
+    cfg_status->change_detected=1;
 
-      if (0 != gettimeofday(&cfg_status->last_change_detected, NULL)) {
-        tr_err("tr_cfgwatch_event_cb: gettimeofday() failed (1).");
-        /*      }*/
+    if (0 != gettimeofday(&cfg_status->last_change_detected, NULL)) {
+      tr_err("tr_cfgwatch_event_cb: gettimeofday() failed (1).");
     }
   }
 
@@ -255,7 +326,7 @@ static void tr_cfgwatch_event_cb(int listener, short event, void *arg)
     }
     timersub(&now, &cfg_status->last_change_detected, &diff);
     if (!timercmp(&diff, &cfg_status->settling_time, <)) {
-      tr_notice("Configuration file change settled, updating configuration.");
+      tr_notice("Configuration file change settled, attempting to update configuration.");
       if (0 != tr_read_and_apply_config(cfg_status))
         tr_warning("Configuration file update failed. Using previous configuration.");
       else