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;
61 static struct refresh_info master_refresh_info = {
62 .entries = &entry_list,
66 struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result)
68 struct ast_dnsmgr_entry *entry;
70 if (!name || !result || ast_strlen_zero(name))
73 entry = calloc(1, sizeof(*result) + strlen(name));
77 entry->result = result;
78 strcpy(entry->name, name);
80 AST_LIST_LOCK(&entry_list);
81 AST_LIST_INSERT_HEAD(&entry_list, entry, list);
82 AST_LIST_UNLOCK(&entry_list);
87 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
92 AST_LIST_LOCK(&entry_list);
93 AST_LIST_REMOVE(&entry_list, entry, list);
94 AST_LIST_UNLOCK(&entry_list);
98 int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
100 if (!name || ast_strlen_zero(name) || !result || !dnsmgr)
103 if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
106 if (option_verbose > 3)
107 ast_verbose(VERBOSE_PREFIX_3 "doing lookup for '%s'\n", name);
109 /* if it's actually an IP address and not a name,
110 there's no need for a managed lookup */
111 if (inet_aton(name, result))
114 /* if the manager is disabled, do a direct lookup and return the result,
115 otherwise register a managed lookup for the name */
117 struct ast_hostent ahp;
120 if ((hp = ast_gethostbyname(name, &ahp)))
121 memcpy(result, hp->h_addr, sizeof(result));
124 if (option_verbose > 2)
125 ast_verbose(VERBOSE_PREFIX_2 "adding manager for '%s'\n", name);
126 *dnsmgr = ast_dnsmgr_get(name, result);
131 static void *do_refresh(void *data)
134 pthread_testcancel();
135 usleep(ast_sched_wait(sched));
136 pthread_testcancel();
137 ast_sched_runq(sched);
142 static int refresh_list(void *data)
144 struct refresh_info *info = data;
145 struct ast_dnsmgr_entry *entry;
146 struct ast_hostent ahp;
149 /* if a refresh or reload is already in progress, exit now */
150 if (ast_mutex_trylock(&refresh_lock)) {
152 ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
156 if (option_verbose > 2)
157 ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
158 AST_LIST_LOCK(info->entries);
159 AST_LIST_TRAVERSE(info->entries, entry, list) {
160 if (info->filter.used && regexec(&info->filter, entry->name, 0, NULL, 0))
163 if (info->verbose && (option_verbose > 2))
164 ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name);
166 if ((hp = ast_gethostbyname(entry->name, &ahp))) {
167 /* check to see if it has changed, do callback if requested */
168 memcpy(entry->result, hp->h_addr, sizeof(entry->result));
171 AST_LIST_UNLOCK(info->entries);
173 ast_mutex_unlock(&refresh_lock);
175 /* automatically reschedule */
179 static int do_reload(int loading);
181 static int handle_cli_reload(int fd, int argc, char *argv[])
184 return RESULT_SHOWUSAGE;
190 static int handle_cli_refresh(int fd, int argc, char *argv[])
192 struct refresh_info info = {
193 .entries = &entry_list,
198 return RESULT_SHOWUSAGE;
201 if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
202 return RESULT_SHOWUSAGE;
207 if (info.filter.used)
208 regfree(&info.filter);
213 static int handle_cli_status(int fd, int argc, char *argv[])
216 struct ast_dnsmgr_entry *entry;
219 return RESULT_SHOWUSAGE;
221 ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
222 ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
223 AST_LIST_LOCK(&entry_list);
224 AST_LIST_TRAVERSE(&entry_list, entry, list)
226 AST_LIST_UNLOCK(&entry_list);
227 ast_cli(fd, "Number of entries: %d\n", count);
232 static struct ast_cli_entry cli_reload = {
233 .cmda = { "dnsmgr", "reload", NULL },
234 .handler = handle_cli_reload,
235 .summary = "Reloads the DNS manager configuration",
237 "Usage: dnsmgr reload\n"
238 " Reloads the DNS manager configuration.\n"
241 static struct ast_cli_entry cli_refresh = {
242 .cmda = { "dnsmgr", "refresh", NULL },
243 .handler = handle_cli_refresh,
244 .summary = "Performs an immediate refresh",
246 "Usage: dnsmgr refresh [pattern]\n"
247 " Peforms an immediate refresh of the managed DNS entries.\n"
248 " Optional regular expression pattern is used to filter the entries to refresh.\n",
251 static struct ast_cli_entry cli_status = {
252 .cmda = { "dnsmgr", "status", NULL },
253 .handler = handle_cli_status,
254 .summary = "Display the DNS manager status",
256 "Usage: dnsmgr status\n"
257 " Displays the DNS manager status.\n"
260 int dnsmgr_init(void)
262 sched = sched_context_create();
264 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
267 AST_LIST_HEAD_INIT(&entry_list);
268 ast_cli_register(&cli_reload);
269 ast_cli_register(&cli_status);
273 void dnsmgr_reload(void)
278 static int do_reload(int loading)
280 struct ast_config *config;
281 const char *interval_value;
282 const char *enabled_value;
288 /* ensure that no refresh cycles run while the reload is in progress */
289 ast_mutex_lock(&refresh_lock);
291 /* reset defaults in preparation for reading config file */
292 refresh_interval = REFRESH_DEFAULT;
293 was_enabled = enabled;
296 if (refresh_sched > -1)
297 ast_sched_del(sched, refresh_sched);
299 if ((config = ast_config_load("dnsmgr.conf"))) {
300 if ((enabled_value = ast_variable_retrieve(config, "general", "enabled"))) {
301 enabled = ast_true(enabled_value);
303 if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
304 if (sscanf(interval_value, "%d", &interval) < 1)
305 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
306 else if (interval < 0)
307 ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
309 refresh_interval = interval;
311 ast_config_destroy(config);
314 if (enabled && refresh_interval) {
315 refresh_sched = ast_sched_add(sched, refresh_interval * 1000, refresh_list, &master_refresh_info);
316 ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
319 /* if this reload enabled the manager, create the background thread
320 if it does not exist */
321 if (enabled && !was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
322 pthread_attr_init(&attr);
323 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
324 if (ast_pthread_create(&refresh_thread, &attr, do_refresh, NULL) < 0) {
325 ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
326 ast_sched_del(sched, refresh_sched);
329 ast_cli_register(&cli_refresh);
333 /* if this reload disabled the manager and there is a background thread,
335 else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
336 /* wake up the thread so it will exit */
337 pthread_cancel(refresh_thread);
338 pthread_kill(refresh_thread, SIGURG);
339 pthread_join(refresh_thread, NULL);
340 refresh_thread = AST_PTHREADT_NULL;
341 ast_cli_unregister(&cli_refresh);
347 ast_mutex_unlock(&refresh_lock);