merge qwell's CLI verbification work
[asterisk-bristuff.git] / main / dnsmgr.c
blob0cc0d63f8ef6c6afcdf5546037abb2858909968c
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005-2006, Kevin P. Fleming
6 * Kevin P. Fleming <kpfleming@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Background DNS update manager
23 * \author Kevin P. Fleming <kpfleming@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <sys/types.h>
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <resolv.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <regex.h>
40 #include <signal.h>
42 #include "asterisk/dnsmgr.h"
43 #include "asterisk/linkedlists.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/config.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/sched.h"
48 #include "asterisk/options.h"
49 #include "asterisk/cli.h"
51 static struct sched_context *sched;
52 static int refresh_sched = -1;
53 static pthread_t refresh_thread = AST_PTHREADT_NULL;
55 struct ast_dnsmgr_entry {
56 /*! where we will store the resulting address */
57 struct in_addr *result;
58 /*! the last result, used to check if address has changed */
59 struct in_addr last;
60 /*! Set to 1 if the entry changes */
61 int changed:1;
62 ast_mutex_t lock;
63 AST_LIST_ENTRY(ast_dnsmgr_entry) list;
64 /*! just 1 here, but we use calloc to allocate the correct size */
65 char name[1];
68 static AST_LIST_HEAD_STATIC(entry_list, ast_dnsmgr_entry);
70 AST_MUTEX_DEFINE_STATIC(refresh_lock);
72 #define REFRESH_DEFAULT 300
74 static int enabled = 0;
75 static int refresh_interval;
77 struct refresh_info {
78 struct entry_list *entries;
79 int verbose;
80 unsigned int regex_present:1;
81 regex_t filter;
84 static struct refresh_info master_refresh_info = {
85 .entries = &entry_list,
86 .verbose = 0,
89 struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result)
91 struct ast_dnsmgr_entry *entry;
93 if (!result || ast_strlen_zero(name) || !(entry = ast_calloc(1, sizeof(*entry) + strlen(name))))
94 return NULL;
96 entry->result = result;
97 ast_mutex_init(&entry->lock);
98 strcpy(entry->name, name);
100 AST_LIST_LOCK(&entry_list);
101 AST_LIST_INSERT_HEAD(&entry_list, entry, list);
102 AST_LIST_UNLOCK(&entry_list);
104 return entry;
107 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
109 if (!entry)
110 return;
112 AST_LIST_LOCK(&entry_list);
113 AST_LIST_REMOVE(&entry_list, entry, list);
114 AST_LIST_UNLOCK(&entry_list);
115 if (option_verbose > 3)
116 ast_verbose(VERBOSE_PREFIX_4 "removing dns manager for '%s'\n", entry->name);
118 ast_mutex_destroy(&entry->lock);
119 free(entry);
122 int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
124 if (ast_strlen_zero(name) || !result || !dnsmgr)
125 return -1;
127 if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
128 return 0;
130 if (option_verbose > 3)
131 ast_verbose(VERBOSE_PREFIX_4 "doing dnsmgr_lookup for '%s'\n", name);
133 /* if it's actually an IP address and not a name,
134 there's no need for a managed lookup */
135 if (inet_aton(name, result))
136 return 0;
138 /* if the manager is disabled, do a direct lookup and return the result,
139 otherwise register a managed lookup for the name */
140 if (!enabled) {
141 struct ast_hostent ahp;
142 struct hostent *hp;
144 if ((hp = ast_gethostbyname(name, &ahp)))
145 memcpy(result, hp->h_addr, sizeof(result));
146 return 0;
147 } else {
148 if (option_verbose > 2)
149 ast_verbose(VERBOSE_PREFIX_2 "adding dns manager for '%s'\n", name);
150 *dnsmgr = ast_dnsmgr_get(name, result);
151 return !*dnsmgr;
156 * Refresh a dnsmgr entry
158 static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose)
160 struct ast_hostent ahp;
161 struct hostent *hp;
162 char iabuf[INET_ADDRSTRLEN];
163 char iabuf2[INET_ADDRSTRLEN];
164 struct in_addr tmp;
165 int changed = 0;
167 ast_mutex_lock(&entry->lock);
168 if (verbose && (option_verbose > 2))
169 ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name);
171 if ((hp = ast_gethostbyname(entry->name, &ahp))) {
172 /* check to see if it has changed, do callback if requested (where de callback is defined ????) */
173 memcpy(&tmp, hp->h_addr, sizeof(tmp));
174 if (tmp.s_addr != entry->last.s_addr) {
175 ast_copy_string(iabuf, ast_inet_ntoa(entry->last), sizeof(iabuf));
176 ast_copy_string(iabuf2, ast_inet_ntoa(tmp), sizeof(iabuf2));
177 ast_log(LOG_NOTICE, "host '%s' changed from %s to %s\n",
178 entry->name, iabuf, iabuf2);
179 memcpy(entry->result, hp->h_addr, sizeof(entry->result));
180 memcpy(&entry->last, hp->h_addr, sizeof(entry->last));
181 changed = entry->changed = 1;
185 ast_mutex_unlock(&entry->lock);
186 return changed;
189 int ast_dnsmgr_refresh(struct ast_dnsmgr_entry *entry)
191 return dnsmgr_refresh(entry, 0);
195 * Check if dnsmgr entry has changed from since last call to this function
197 int ast_dnsmgr_changed(struct ast_dnsmgr_entry *entry)
199 int changed;
201 ast_mutex_lock(&entry->lock);
203 changed = entry->changed;
204 entry->changed = 0;
206 ast_mutex_unlock(&entry->lock);
208 return changed;
211 static void *do_refresh(void *data)
213 for (;;) {
214 pthread_testcancel();
215 usleep(ast_sched_wait(sched));
216 pthread_testcancel();
217 ast_sched_runq(sched);
219 return NULL;
222 static int refresh_list(void *data)
224 struct refresh_info *info = data;
225 struct ast_dnsmgr_entry *entry;
227 /* if a refresh or reload is already in progress, exit now */
228 if (ast_mutex_trylock(&refresh_lock)) {
229 if (info->verbose)
230 ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
231 return -1;
234 if (option_verbose > 2)
235 ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
236 AST_LIST_LOCK(info->entries);
237 AST_LIST_TRAVERSE(info->entries, entry, list) {
238 if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
239 continue;
241 dnsmgr_refresh(entry, info->verbose);
243 AST_LIST_UNLOCK(info->entries);
245 ast_mutex_unlock(&refresh_lock);
247 /* automatically reschedule based on the interval */
248 return refresh_interval * 1000;
251 void dnsmgr_start_refresh(void)
253 if (refresh_sched > -1) {
254 ast_sched_del(sched, refresh_sched);
255 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
259 static int do_reload(int loading);
261 static int handle_cli_reload(int fd, int argc, char *argv[])
263 if (argc > 2)
264 return RESULT_SHOWUSAGE;
266 do_reload(0);
267 return 0;
270 static int handle_cli_refresh(int fd, int argc, char *argv[])
272 struct refresh_info info = {
273 .entries = &entry_list,
274 .verbose = 1,
277 if (argc > 3)
278 return RESULT_SHOWUSAGE;
280 if (argc == 3) {
281 if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
282 return RESULT_SHOWUSAGE;
283 else
284 info.regex_present = 1;
287 refresh_list(&info);
289 if (info.regex_present)
290 regfree(&info.filter);
292 return 0;
295 static int handle_cli_status(int fd, int argc, char *argv[])
297 int count = 0;
298 struct ast_dnsmgr_entry *entry;
300 if (argc > 2)
301 return RESULT_SHOWUSAGE;
303 ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
304 ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
305 AST_LIST_LOCK(&entry_list);
306 AST_LIST_TRAVERSE(&entry_list, entry, list)
307 count++;
308 AST_LIST_UNLOCK(&entry_list);
309 ast_cli(fd, "Number of entries: %d\n", count);
311 return 0;
314 static struct ast_cli_entry cli_reload = {
315 { "dnsmgr", "reload", NULL },
316 handle_cli_reload, "Reloads the DNS manager configuration",
317 "Usage: dnsmgr reload\n"
318 " Reloads the DNS manager configuration.\n"
321 static struct ast_cli_entry cli_refresh = {
322 { "dnsmgr", "refresh", NULL },
323 handle_cli_refresh, "Performs an immediate refresh",
324 "Usage: dnsmgr refresh [pattern]\n"
325 " Peforms an immediate refresh of the managed DNS entries.\n"
326 " Optional regular expression pattern is used to filter the entries to refresh.\n",
329 static struct ast_cli_entry cli_status = {
330 { "dnsmgr", "status", NULL },
331 handle_cli_status, "Display the DNS manager status",
332 "Usage: dnsmgr status\n"
333 " Displays the DNS manager status.\n"
336 int dnsmgr_init(void)
338 if (!(sched = sched_context_create())) {
339 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
340 return -1;
342 ast_cli_register(&cli_reload);
343 ast_cli_register(&cli_status);
344 return do_reload(1);
347 int dnsmgr_reload(void)
349 return do_reload(0);
352 static int do_reload(int loading)
354 struct ast_config *config;
355 const char *interval_value;
356 const char *enabled_value;
357 int interval;
358 int was_enabled;
359 int res = -1;
361 /* ensure that no refresh cycles run while the reload is in progress */
362 ast_mutex_lock(&refresh_lock);
364 /* reset defaults in preparation for reading config file */
365 refresh_interval = REFRESH_DEFAULT;
366 was_enabled = enabled;
367 enabled = 0;
369 if (refresh_sched > -1)
370 ast_sched_del(sched, refresh_sched);
372 if ((config = ast_config_load("dnsmgr.conf"))) {
373 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
374 enabled = ast_true(enabled_value);
376 if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
377 if (sscanf(interval_value, "%d", &interval) < 1)
378 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
379 else if (interval < 0)
380 ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
381 else
382 refresh_interval = interval;
384 ast_config_destroy(config);
387 if (enabled && refresh_interval)
388 ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
390 /* if this reload enabled the manager, create the background thread
391 if it does not exist */
392 if (enabled && !was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
393 if (ast_pthread_create(&refresh_thread, NULL, do_refresh, NULL) < 0) {
394 ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
396 else {
397 ast_cli_register(&cli_refresh);
398 /* make a background refresh happen right away */
399 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
400 res = 0;
403 /* if this reload disabled the manager and there is a background thread,
404 kill it */
405 else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
406 /* wake up the thread so it will exit */
407 pthread_cancel(refresh_thread);
408 pthread_kill(refresh_thread, SIGURG);
409 pthread_join(refresh_thread, NULL);
410 refresh_thread = AST_PTHREADT_NULL;
411 ast_cli_unregister(&cli_refresh);
412 res = 0;
414 else
415 res = 0;
417 ast_mutex_unlock(&refresh_lock);
419 return res;