1 /***** config file watching *****/
9 #include <tr_cfgwatch.h>
11 /* Initialize a new tr_cfgwatch_data struct. Free this with talloc. */
12 TR_CFGWATCH *tr_cfgwatch_create(TALLOC_CTX *mem_ctx)
14 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
17 new_cfg=talloc(tmp_ctx, TR_CFGWATCH);
18 if (new_cfg == NULL) {
19 tr_debug("tr_cfgwatch_create: Allocation failed.");
21 timerclear(&new_cfg->poll_interval);
22 timerclear(&new_cfg->settling_time);
23 new_cfg->config_dir=NULL;
24 new_cfg->fstat_list=NULL;
26 new_cfg->change_detected=0;
27 timerclear(&new_cfg->last_change_detected);
32 talloc_steal(mem_ctx, new_cfg);
37 /* Obtain the file modification time as seconds since epoch. Returns 0 on success. */
38 static int tr_get_mtime(const char *path, struct timespec *ts)
40 struct stat file_status;
42 if (stat(path, &file_status) != 0) {
45 (*ts)=file_status.st_mtim;
50 static char *tr_join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2)
52 return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
55 static int tr_fstat_namecmp(const void *p1_arg, const void *p2_arg)
57 struct tr_fstat *p1=(struct tr_fstat *) p1_arg;
58 struct tr_fstat *p2=(struct tr_fstat *) p2_arg;
60 return strcmp(p1->name, p2->name);
63 static int tr_fstat_mtimecmp(const void *p1_arg, const void *p2_arg)
65 struct tr_fstat *p1=(struct tr_fstat *) p1_arg;
66 struct tr_fstat *p2=(struct tr_fstat *) p2_arg;
68 if (p1->mtime.tv_sec == p2->mtime.tv_sec)
69 return (p1->mtime.tv_nsec) - (p2->mtime.tv_nsec);
71 return (p1->mtime.tv_sec) - (p2->mtime.tv_sec);
74 /* Get status of all files in cfg_files. Returns list, or NULL on error.
75 * Files are sorted by filename.
76 * After success, caller must eventually free result with talloc_free. */
77 static struct tr_fstat *tr_fstat_get_all(TALLOC_CTX *mem_ctx,
78 const char *config_path,
79 struct dirent **cfg_files,
82 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
83 struct tr_fstat *fstat_list=NULL;
86 /* create a new fstat list (may be discarded) */
87 fstat_list=talloc_array(tmp_ctx, struct tr_fstat, n_files);
88 if (fstat_list==NULL) {
89 tr_err("tr_fstat_get_all: Could not allocate fstat list.");
93 for (ii=0; ii<n_files; ii++) {
94 fstat_list[ii].name=talloc_strdup(fstat_list, cfg_files[ii]->d_name);
95 if (0 != tr_get_mtime(tr_join_paths(tmp_ctx, config_path, fstat_list[ii].name),
96 &(fstat_list[ii].mtime))) {
97 tr_warning("tr_fstat_get_all: Could not obtain mtime for file %s", fstat_list[ii].name);
102 qsort(fstat_list, n_files, sizeof(struct tr_fstat), tr_fstat_namecmp);
104 /* put list in the caller's context and return it */
105 talloc_steal(mem_ctx, fstat_list);
107 talloc_free(tmp_ctx);
111 /* Checks whether any config files have appeared/disappeared/modified.
112 * Returns 1 if so, 0 otherwise. */
113 static int tr_cfgwatch_update_needed(TR_CFGWATCH *cfg_status)
115 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
116 struct tr_fstat *fstat_list=NULL;
119 struct dirent **cfg_files=NULL;
120 int update_needed=0; /* return value */
122 /* get the list, must free cfg_files later with tr_free_cfg_file_list */
123 n_files = tr_find_config_files(cfg_status->config_dir, &cfg_files);
125 tr_warning("tr_cfgwatch_update: configuration files disappeared, skipping update.");
129 /* create a new fstat list (will be discarded) */
130 fstat_list=tr_fstat_get_all(tmp_ctx, cfg_status->config_dir, cfg_files, n_files);
131 if (fstat_list==NULL) {
132 tr_err("tr_cfgwatch_update: Error getting fstat list.");
136 /* see if the number of files change, if so need to update */
137 if (n_files != cfg_status->n_files) {
138 tr_debug("tr_cfgwatch_update: Changed number of config files (was %d, now %d).",
142 talloc_free(cfg_status->fstat_list);
143 cfg_status->n_files=n_files;
144 cfg_status->fstat_list=fstat_list;
145 talloc_steal(cfg_status->ctx, fstat_list);
149 /* See if any files have a changed mtime. Both are sorted by name so this is easy. */
150 for (ii=0; ii<n_files; ii++) {
151 if ((0 != tr_fstat_mtimecmp(&fstat_list[ii], &cfg_status->fstat_list[ii]))
152 || (0 != tr_fstat_namecmp(&fstat_list[ii], &cfg_status->fstat_list[ii]))){
154 talloc_free(cfg_status->fstat_list);
155 cfg_status->n_files=n_files;
156 cfg_status->fstat_list=fstat_list;
157 talloc_steal(cfg_status->ctx, fstat_list);
163 tr_free_config_file_list(n_files, &cfg_files);
164 talloc_free(tmp_ctx);
165 return update_needed;
167 /* must specify the ctx and tr in cfgwatch! */
168 int tr_read_and_apply_config(TR_CFGWATCH *cfgwatch)
170 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
171 char *config_dir=cfgwatch->config_dir;
173 struct dirent **cfg_files=NULL;
174 TR_CFG_RC rc = TR_CFG_SUCCESS; /* presume success */
175 struct tr_fstat *new_fstat_list=NULL;
178 /* find the configuration files -- n.b., tr_find_config_files()
179 * allocates memory to cfg_files which we must later free */
180 tr_debug("Reading configuration files from %s/", config_dir);
181 n_files = tr_find_config_files(config_dir, &cfg_files);
183 tr_debug("tr_read_and_apply_config: No configuration files.");
184 retval=1; goto cleanup;
187 /* Get the list of update times.
188 * Do this before loading in case they change between obtaining their timestamp
189 * and reading the file---this way they will immediately reload if this happens. */
190 new_fstat_list=tr_fstat_get_all(tmp_ctx, config_dir, cfg_files, n_files);
191 if (new_fstat_list==NULL) {
192 tr_debug("tr_read_and_apply_config: Could not allocate config file status list.");
193 retval=1; goto cleanup;
196 /* allocate a new configuration, dumping an old one if needed */
197 if(cfgwatch->tr->new_cfg != NULL)
198 tr_cfg_free(cfgwatch->tr->new_cfg);
199 cfgwatch->tr->new_cfg=tr_cfg_new(tmp_ctx);
200 if (cfgwatch->tr->new_cfg==NULL) {
201 tr_debug("tr_read_and_apply_config: Error allocating new_cfg.");
202 retval=1; goto cleanup;
205 if (TR_CFG_SUCCESS != (rc = tr_parse_config(cfgwatch->tr->new_cfg, config_dir, n_files, cfg_files))) {
206 tr_debug("tr_read_and_apply_config: Error parsing configuration information, rc=%d.", rc);
207 retval=1; goto cleanup;
210 /* apply initial configuration (frees active_cfg and resets new_cfg to NULL) */
211 if (TR_CFG_SUCCESS != (rc = tr_apply_new_config(&cfgwatch->tr->active_cfg,
212 &cfgwatch->tr->new_cfg))) {
213 tr_debug("tr_read_and_apply_config: Error applying configuration, rc = %d.", rc);
214 retval=1; goto cleanup;
216 talloc_steal(cfgwatch->ctx, cfgwatch->tr->active_cfg); /* hand over ownership */
218 /* give ownership of the new_fstat_list to caller's context */
219 if (cfgwatch->fstat_list != NULL) {
220 /* free the old one */
221 talloc_free(cfgwatch->fstat_list);
223 cfgwatch->n_files=n_files;
224 cfgwatch->fstat_list=new_fstat_list;
225 talloc_steal(cfgwatch->ctx, new_fstat_list);
229 tr_free_config_file_list(n_files, &cfg_files);
230 talloc_free(tmp_ctx);
231 cfgwatch->tr->new_cfg=NULL; /* this has been freed, either explicitly or with tmp_ctx */
236 static void tr_cfgwatch_event_cb(int listener, short event, void *arg)
238 TR_CFGWATCH *cfg_status=(TR_CFGWATCH *) arg;
239 struct timeval now, diff;;
241 if (tr_cfgwatch_update_needed(cfg_status)) {
242 tr_notice("Configuration file change detected, waiting for changes to settle.");
243 /* if (!cfg_status->change_detected) {*/
244 cfg_status->change_detected=1;
246 if (0 != gettimeofday(&cfg_status->last_change_detected, NULL)) {
247 tr_err("tr_cfgwatch_event_cb: gettimeofday() failed (1).");
252 if (cfg_status->change_detected) {
253 if (0 != gettimeofday(&now, NULL)) {
254 tr_err("tr_cfgwatch_event_cb: gettimeofday() failed (2).");
256 timersub(&now, &cfg_status->last_change_detected, &diff);
257 if (!timercmp(&diff, &cfg_status->settling_time, <)) {
258 tr_notice("Configuration file change settled, updating configuration.");
259 if (0 != tr_read_and_apply_config(cfg_status))
260 tr_warning("Configuration file update failed. Using previous configuration.");
262 tr_notice("Configuration updated successfully.");
263 cfg_status->change_detected=0;
269 /* Configure the cfgwatch instance and set up its event handler.
270 * Returns 0 on success, nonzero on failure. Points
271 * *cfgwatch_ev to the event struct. */
272 int tr_cfgwatch_event_init(struct event_base *base,
273 TR_CFGWATCH *cfg_status,
274 struct event **cfgwatch_ev)
276 if (cfgwatch_ev == NULL) {
277 tr_debug("tr_cfgwatch_event_init: Null cfgwatch_ev.");
281 /* zero out the change detection fields */
282 cfg_status->change_detected=0;
283 cfg_status->last_change_detected.tv_sec=0;
284 cfg_status->last_change_detected.tv_usec=0;
286 /* create the event and enable it */
287 *cfgwatch_ev=event_new(base, -1, EV_TIMEOUT|EV_PERSIST, tr_cfgwatch_event_cb, (void *)cfg_status);
288 event_add(*cfgwatch_ev, &(cfg_status->poll_interval));
290 tr_info("tr_cfgwatch_event_init: Added configuration file watcher with %0d.%06d second poll interval.",
291 cfg_status->poll_interval.tv_sec,
292 cfg_status->poll_interval.tv_usec);