Let's also include aclocal.m4
[asterisk-bristuff.git] / main / dnsmgr.c
blobdeb5c73dbe9b2ec54603d3ac528a75b8633db976
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;
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);
99 memcpy(&entry->last, result, sizeof(entry->last));
101 AST_LIST_LOCK(&entry_list);
102 AST_LIST_INSERT_HEAD(&entry_list, entry, list);
103 AST_LIST_UNLOCK(&entry_list);
105 return entry;
108 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
110 if (!entry)
111 return;
113 AST_LIST_LOCK(&entry_list);
114 AST_LIST_REMOVE(&entry_list, entry, list);
115 AST_LIST_UNLOCK(&entry_list);
116 if (option_verbose > 3)
117 ast_verbose(VERBOSE_PREFIX_4 "removing dns manager for '%s'\n", entry->name);
119 ast_mutex_destroy(&entry->lock);
120 free(entry);
123 int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
125 struct ast_hostent ahp;
126 struct hostent *hp;
128 if (ast_strlen_zero(name) || !result || !dnsmgr)
129 return -1;
131 if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
132 return 0;
134 if (option_verbose > 3)
135 ast_verbose(VERBOSE_PREFIX_4 "doing dnsmgr_lookup for '%s'\n", name);
137 /* if it's actually an IP address and not a name,
138 there's no need for a managed lookup */
139 if (inet_aton(name, result))
140 return 0;
142 /* do a lookup now but add a manager so it will automagically get updated in the background */
143 if ((hp = ast_gethostbyname(name, &ahp)))
144 memcpy(result, hp->h_addr, sizeof(result));
146 /* if dnsmgr is not enable don't bother adding an entry */
147 if (!enabled)
148 return 0;
150 if (option_verbose > 2)
151 ast_verbose(VERBOSE_PREFIX_2 "adding dns manager for '%s'\n", name);
152 *dnsmgr = ast_dnsmgr_get(name, result);
153 return !*dnsmgr;
157 * Refresh a dnsmgr entry
159 static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose)
161 struct ast_hostent ahp;
162 struct hostent *hp;
163 char iabuf[INET_ADDRSTRLEN];
164 char iabuf2[INET_ADDRSTRLEN];
165 struct in_addr tmp;
166 int changed = 0;
168 ast_mutex_lock(&entry->lock);
169 if (verbose && (option_verbose > 2))
170 ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name);
172 if ((hp = ast_gethostbyname(entry->name, &ahp))) {
173 /* check to see if it has changed, do callback if requested (where de callback is defined ????) */
174 memcpy(&tmp, hp->h_addr, sizeof(tmp));
175 if (tmp.s_addr != entry->last.s_addr) {
176 ast_copy_string(iabuf, ast_inet_ntoa(entry->last), sizeof(iabuf));
177 ast_copy_string(iabuf2, ast_inet_ntoa(tmp), sizeof(iabuf2));
178 ast_log(LOG_NOTICE, "host '%s' changed from %s to %s\n",
179 entry->name, iabuf, iabuf2);
180 memcpy(entry->result, hp->h_addr, sizeof(entry->result));
181 memcpy(&entry->last, hp->h_addr, sizeof(entry->last));
182 changed = entry->changed = 1;
186 ast_mutex_unlock(&entry->lock);
187 return changed;
190 int ast_dnsmgr_refresh(struct ast_dnsmgr_entry *entry)
192 return dnsmgr_refresh(entry, 0);
196 * Check if dnsmgr entry has changed from since last call to this function
198 int ast_dnsmgr_changed(struct ast_dnsmgr_entry *entry)
200 int changed;
202 ast_mutex_lock(&entry->lock);
204 changed = entry->changed;
205 entry->changed = 0;
207 ast_mutex_unlock(&entry->lock);
209 return changed;
212 static void *do_refresh(void *data)
214 for (;;) {
215 pthread_testcancel();
216 usleep((ast_sched_wait(sched)*1000));
217 pthread_testcancel();
218 ast_sched_runq(sched);
220 return NULL;
223 static int refresh_list(const void *data)
225 struct refresh_info *info = (struct refresh_info *)data;
226 struct ast_dnsmgr_entry *entry;
228 /* if a refresh or reload is already in progress, exit now */
229 if (ast_mutex_trylock(&refresh_lock)) {
230 if (info->verbose)
231 ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
232 return -1;
235 if (option_verbose > 2)
236 ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
237 AST_LIST_LOCK(info->entries);
238 AST_LIST_TRAVERSE(info->entries, entry, list) {
239 if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
240 continue;
242 dnsmgr_refresh(entry, info->verbose);
244 AST_LIST_UNLOCK(info->entries);
246 ast_mutex_unlock(&refresh_lock);
248 /* automatically reschedule based on the interval */
249 return refresh_interval * 1000;
252 void dnsmgr_start_refresh(void)
254 if (refresh_sched > -1) {
255 AST_SCHED_DEL(sched, refresh_sched);
256 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
260 static int do_reload(int loading);
262 static int handle_cli_reload(int fd, int argc, char *argv[])
264 if (argc > 2)
265 return RESULT_SHOWUSAGE;
267 do_reload(0);
268 return 0;
271 static int handle_cli_refresh(int fd, int argc, char *argv[])
273 struct refresh_info info = {
274 .entries = &entry_list,
275 .verbose = 1,
278 if(!enabled) {
279 ast_cli(fd, "DNS Manager is disabled.\n");
280 return 0;
283 if (argc > 3)
284 return RESULT_SHOWUSAGE;
286 if (argc == 3) {
287 if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
288 return RESULT_SHOWUSAGE;
289 else
290 info.regex_present = 1;
293 refresh_list(&info);
295 if (info.regex_present)
296 regfree(&info.filter);
298 return 0;
301 static int handle_cli_status(int fd, int argc, char *argv[])
303 int count = 0;
304 struct ast_dnsmgr_entry *entry;
306 if (argc > 2)
307 return RESULT_SHOWUSAGE;
309 ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
310 ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
311 AST_LIST_LOCK(&entry_list);
312 AST_LIST_TRAVERSE(&entry_list, entry, list)
313 count++;
314 AST_LIST_UNLOCK(&entry_list);
315 ast_cli(fd, "Number of entries: %d\n", count);
317 return 0;
320 static struct ast_cli_entry cli_reload = {
321 { "dnsmgr", "reload", NULL },
322 handle_cli_reload, "Reloads the DNS manager configuration",
323 "Usage: dnsmgr reload\n"
324 " Reloads the DNS manager configuration.\n"
327 static struct ast_cli_entry cli_refresh = {
328 { "dnsmgr", "refresh", NULL },
329 handle_cli_refresh, "Performs an immediate refresh",
330 "Usage: dnsmgr refresh [pattern]\n"
331 " Peforms an immediate refresh of the managed DNS entries.\n"
332 " Optional regular expression pattern is used to filter the entries to refresh.\n",
335 static struct ast_cli_entry cli_status = {
336 { "dnsmgr", "status", NULL },
337 handle_cli_status, "Display the DNS manager status",
338 "Usage: dnsmgr status\n"
339 " Displays the DNS manager status.\n"
342 int dnsmgr_init(void)
344 if (!(sched = sched_context_create())) {
345 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
346 return -1;
348 ast_cli_register(&cli_reload);
349 ast_cli_register(&cli_status);
350 ast_cli_register(&cli_refresh);
351 return do_reload(1);
354 int dnsmgr_reload(void)
356 return do_reload(0);
359 static int do_reload(int loading)
361 struct ast_config *config;
362 const char *interval_value;
363 const char *enabled_value;
364 int interval;
365 int was_enabled;
366 int res = -1;
368 /* ensure that no refresh cycles run while the reload is in progress */
369 ast_mutex_lock(&refresh_lock);
371 /* reset defaults in preparation for reading config file */
372 refresh_interval = REFRESH_DEFAULT;
373 was_enabled = enabled;
374 enabled = 0;
376 AST_SCHED_DEL(sched, refresh_sched);
378 if ((config = ast_config_load("dnsmgr.conf"))) {
379 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
380 enabled = ast_true(enabled_value);
382 if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
383 if (sscanf(interval_value, "%d", &interval) < 1)
384 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
385 else if (interval < 0)
386 ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
387 else
388 refresh_interval = interval;
390 ast_config_destroy(config);
393 if (enabled && refresh_interval)
394 ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
396 /* if this reload enabled the manager, create the background thread
397 if it does not exist */
398 if (enabled) {
399 if (!was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
400 if (ast_pthread_create_background(&refresh_thread, NULL, do_refresh, NULL) < 0) {
401 ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
404 /* make a background refresh happen right away */
405 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
406 res = 0;
408 /* if this reload disabled the manager and there is a background thread,
409 kill it */
410 else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
411 /* wake up the thread so it will exit */
412 pthread_cancel(refresh_thread);
413 pthread_kill(refresh_thread, SIGURG);
414 pthread_join(refresh_thread, NULL);
415 refresh_thread = AST_PTHREADT_NULL;
416 res = 0;
418 else
419 res = 0;
421 ast_mutex_unlock(&refresh_lock);
423 return res;