2 * Asterisk -- A telephony toolkit for Linux.
4 * Background DNS update manager
6 * Copyright (C) 2005, Kevin P. Fleming
8 * Kevin P. Fleming <kpfleming@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <sys/types.h>
15 #include <netinet/in.h>
16 #include <sys/socket.h>
17 #include <arpa/inet.h>
26 #include <asterisk/dnsmgr.h>
27 #include <asterisk/linkedlists.h>
28 #include <asterisk/utils.h>
29 #include <asterisk/config.h>
30 #include <asterisk/logger.h>
31 #include <asterisk/sched.h>
32 #include <asterisk/options.h>
33 #include <asterisk/cli.h>
36 static struct sched_context *sched;
37 static int refresh_sched = -1;
38 static pthread_t refresh_thread = AST_PTHREADT_NULL;
40 struct ast_dnsmgr_entry {
41 struct in_addr *result;
42 AST_LIST_ENTRY(ast_dnsmgr_entry) list;
46 static AST_LIST_HEAD(entry_list, ast_dnsmgr_entry) entry_list;
48 AST_MUTEX_DEFINE_STATIC(refresh_lock);
50 #define REFRESH_DEFAULT 300
52 static int enabled = 0;
53 static int refresh_interval;
56 struct entry_list *entries;
58 unsigned int regex_present:1;
62 static struct refresh_info master_refresh_info = {
63 .entries = &entry_list,
67 struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result)
69 struct ast_dnsmgr_entry *entry;
71 if (!name || !result || ast_strlen_zero(name))
74 entry = calloc(1, sizeof(*result) + strlen(name));
78 entry->result = result;
79 strcpy(entry->name, name);
81 AST_LIST_LOCK(&entry_list);
82 AST_LIST_INSERT_HEAD(&entry_list, entry, list);
83 AST_LIST_UNLOCK(&entry_list);
88 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
93 AST_LIST_LOCK(&entry_list);
94 AST_LIST_REMOVE(&entry_list, entry, list);
95 AST_LIST_UNLOCK(&entry_list);
99 int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
101 if (!name || ast_strlen_zero(name) || !result || !dnsmgr)
104 if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
107 if (option_verbose > 3)
108 ast_verbose(VERBOSE_PREFIX_3 "doing lookup for '%s'\n", name);
110 /* if it's actually an IP address and not a name,
111 there's no need for a managed lookup */
112 if (inet_aton(name, result))
115 /* if the manager is disabled, do a direct lookup and return the result,
116 otherwise register a managed lookup for the name */
118 struct ast_hostent ahp;
121 if ((hp = ast_gethostbyname(name, &ahp)))
122 memcpy(result, hp->h_addr, sizeof(result));
125 if (option_verbose > 2)
126 ast_verbose(VERBOSE_PREFIX_2 "adding manager for '%s'\n", name);
127 *dnsmgr = ast_dnsmgr_get(name, result);
132 static void *do_refresh(void *data)
135 pthread_testcancel();
136 usleep(ast_sched_wait(sched));
137 pthread_testcancel();
138 ast_sched_runq(sched);
143 static int refresh_list(void *data)
145 struct refresh_info *info = data;
146 struct ast_dnsmgr_entry *entry;
147 struct ast_hostent ahp;
150 /* if a refresh or reload is already in progress, exit now */
151 if (ast_mutex_trylock(&refresh_lock)) {
153 ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
157 if (option_verbose > 2)
158 ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
159 AST_LIST_LOCK(info->entries);
160 AST_LIST_TRAVERSE(info->entries, entry, list) {
161 if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
164 if (info->verbose && (option_verbose > 2))
165 ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name);
167 if ((hp = ast_gethostbyname(entry->name, &ahp))) {
168 /* check to see if it has changed, do callback if requested */
169 memcpy(entry->result, hp->h_addr, sizeof(entry->result));
172 AST_LIST_UNLOCK(info->entries);
174 ast_mutex_unlock(&refresh_lock);
176 /* automatically reschedule */
180 static int do_reload(int loading);
182 static int handle_cli_reload(int fd, int argc, char *argv[])
185 return RESULT_SHOWUSAGE;
191 static int handle_cli_refresh(int fd, int argc, char *argv[])
193 struct refresh_info info = {
194 .entries = &entry_list,
199 return RESULT_SHOWUSAGE;
202 if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
203 return RESULT_SHOWUSAGE;
205 info.regex_present = 1;
210 if (info.regex_present)
211 regfree(&info.filter);
216 static int handle_cli_status(int fd, int argc, char *argv[])
219 struct ast_dnsmgr_entry *entry;
222 return RESULT_SHOWUSAGE;
224 ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
225 ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
226 AST_LIST_LOCK(&entry_list);
227 AST_LIST_TRAVERSE(&entry_list, entry, list)
229 AST_LIST_UNLOCK(&entry_list);
230 ast_cli(fd, "Number of entries: %d\n", count);
235 static struct ast_cli_entry cli_reload = {
236 .cmda = { "dnsmgr", "reload", NULL },
237 .handler = handle_cli_reload,
238 .summary = "Reloads the DNS manager configuration",
240 "Usage: dnsmgr reload\n"
241 " Reloads the DNS manager configuration.\n"
244 static struct ast_cli_entry cli_refresh = {
245 .cmda = { "dnsmgr", "refresh", NULL },
246 .handler = handle_cli_refresh,
247 .summary = "Performs an immediate refresh",
249 "Usage: dnsmgr refresh [pattern]\n"
250 " Peforms an immediate refresh of the managed DNS entries.\n"
251 " Optional regular expression pattern is used to filter the entries to refresh.\n",
254 static struct ast_cli_entry cli_status = {
255 .cmda = { "dnsmgr", "status", NULL },
256 .handler = handle_cli_status,
257 .summary = "Display the DNS manager status",
259 "Usage: dnsmgr status\n"
260 " Displays the DNS manager status.\n"
263 int dnsmgr_init(void)
265 sched = sched_context_create();
267 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
270 AST_LIST_HEAD_INIT(&entry_list);
271 ast_cli_register(&cli_reload);
272 ast_cli_register(&cli_status);
276 void dnsmgr_reload(void)
281 static int do_reload(int loading)
283 struct ast_config *config;
284 const char *interval_value;
285 const char *enabled_value;
291 /* ensure that no refresh cycles run while the reload is in progress */
292 ast_mutex_lock(&refresh_lock);
294 /* reset defaults in preparation for reading config file */
295 refresh_interval = REFRESH_DEFAULT;
296 was_enabled = enabled;
299 if (refresh_sched > -1)
300 ast_sched_del(sched, refresh_sched);
302 if ((config = ast_config_load("dnsmgr.conf"))) {
303 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
304 enabled = ast_true(enabled_value);
306 if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
307 if (sscanf(interval_value, "%d", &interval) < 1)
308 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
309 else if (interval < 0)
310 ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
312 refresh_interval = interval;
314 ast_config_destroy(config);
317 if (enabled && refresh_interval) {
318 refresh_sched = ast_sched_add(sched, refresh_interval * 1000, refresh_list, &master_refresh_info);
319 ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
322 /* if this reload enabled the manager, create the background thread
323 if it does not exist */
324 if (enabled && !was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
325 pthread_attr_init(&attr);
326 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
327 if (ast_pthread_create(&refresh_thread, &attr, do_refresh, NULL) < 0) {
328 ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
329 ast_sched_del(sched, refresh_sched);
332 ast_cli_register(&cli_refresh);
336 /* if this reload disabled the manager and there is a background thread,
338 else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
339 /* wake up the thread so it will exit */
340 pthread_cancel(refresh_thread);
341 pthread_kill(refresh_thread, SIGURG);
342 pthread_join(refresh_thread, NULL);
343 refresh_thread = AST_PTHREADT_NULL;
344 ast_cli_unregister(&cli_refresh);
350 ast_mutex_unlock(&refresh_lock);