-/***** 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>
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;
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;
}
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;
}
}
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()
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) {
}
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;
}
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).");
}
}
}
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