various cleanups, remove support for CVS checkouts
[asterisk-bristuff.git] / dnsmgr.c
blob7115e3ed3e0f9132df73a6b6a36d1913fff036e7
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_log(LOG_NOTICE, "host '%s' changed from %s to %s\n",
176 entry->name,
177 ast_inet_ntoa(iabuf, sizeof(iabuf), entry->last),
178 ast_inet_ntoa(iabuf2, sizeof(iabuf2), tmp));
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 .cmda = { "dnsmgr", "reload", NULL },
316 .handler = handle_cli_reload,
317 .summary = "Reloads the DNS manager configuration",
318 .usage =
319 "Usage: dnsmgr reload\n"
320 " Reloads the DNS manager configuration.\n"
323 static struct ast_cli_entry cli_refresh = {
324 .cmda = { "dnsmgr", "refresh", NULL },
325 .handler = handle_cli_refresh,
326 .summary = "Performs an immediate refresh",
327 .usage =
328 "Usage: dnsmgr refresh [pattern]\n"
329 " Peforms an immediate refresh of the managed DNS entries.\n"
330 " Optional regular expression pattern is used to filter the entries to refresh.\n",
333 static struct ast_cli_entry cli_status = {
334 .cmda = { "dnsmgr", "status", NULL },
335 .handler = handle_cli_status,
336 .summary = "Display the DNS manager status",
337 .usage =
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 return do_reload(1);
353 int dnsmgr_reload(void)
355 return do_reload(0);
358 static int do_reload(int loading)
360 struct ast_config *config;
361 const char *interval_value;
362 const char *enabled_value;
363 int interval;
364 int was_enabled;
365 int res = -1;
367 /* ensure that no refresh cycles run while the reload is in progress */
368 ast_mutex_lock(&refresh_lock);
370 /* reset defaults in preparation for reading config file */
371 refresh_interval = REFRESH_DEFAULT;
372 was_enabled = enabled;
373 enabled = 0;
375 if (refresh_sched > -1)
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 && !was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
399 if (ast_pthread_create(&refresh_thread, NULL, do_refresh, NULL) < 0) {
400 ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
402 else {
403 ast_cli_register(&cli_refresh);
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;
409 /* if this reload disabled the manager and there is a background thread,
410 kill it */
411 else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
412 /* wake up the thread so it will exit */
413 pthread_cancel(refresh_thread);
414 pthread_kill(refresh_thread, SIGURG);
415 pthread_join(refresh_thread, NULL);
416 refresh_thread = AST_PTHREADT_NULL;
417 ast_cli_unregister(&cli_refresh);
418 res = 0;
420 else
421 res = 0;
423 ast_mutex_unlock(&refresh_lock);
425 return res;