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)
89 /* if there is an entry (and not the special flag value), remove/free it */
90 if (entry && (entry != (typeof(entry)) -1)) {
91 AST_LIST_LOCK(&entry_list);
92 AST_LIST_REMOVE(&entry_list, entry, list);
93 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));
122 /* flag value to indicate no manager was allocated */
123 *dnsmgr = (typeof(*dnsmgr)) -1;
125 if (option_verbose > 2)
126 ast_verbose(VERBOSE_PREFIX_2 "adding manager for '%s'\n", name);
127 *dnsmgr = ast_dnsmgr_get(name, result);
133 static void *do_refresh(void *data)
136 pthread_testcancel();
137 usleep(ast_sched_wait(sched));
138 pthread_testcancel();
139 ast_sched_runq(sched);
144 static int refresh_list(void *data)
146 struct refresh_info *info = data;
147 struct ast_dnsmgr_entry *entry;
148 struct ast_hostent ahp;
151 /* if a refresh or reload is already in progress, exit now */
152 if (ast_mutex_trylock(&refresh_lock)) {
154 ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
158 if (option_verbose > 2)
159 ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
160 AST_LIST_LOCK(info->entries);
161 AST_LIST_TRAVERSE(info->entries, entry, list) {
162 if (info->filter.used && regexec(&info->filter, entry->name, 0, NULL, 0))
165 if (info->verbose && (option_verbose > 2))
166 ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name);
168 if ((hp = ast_gethostbyname(entry->name, &ahp))) {
169 /* check to see if it has changed, do callback if requested */
170 memcpy(entry->result, hp->h_addr, sizeof(entry->result));
173 AST_LIST_UNLOCK(info->entries);
175 ast_mutex_unlock(&refresh_lock);
177 /* automatically reschedule */
181 static int do_reload(int loading);
183 static int handle_cli_reload(int fd, int argc, char *argv[])
186 return RESULT_SHOWUSAGE;
192 static int handle_cli_refresh(int fd, int argc, char *argv[])
194 struct refresh_info info = {
195 .entries = &entry_list,
200 return RESULT_SHOWUSAGE;
203 if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
204 return RESULT_SHOWUSAGE;
209 if (info.filter.used)
210 regfree(&info.filter);
215 static int handle_cli_status(int fd, int argc, char *argv[])
218 struct ast_dnsmgr_entry *entry;
221 return RESULT_SHOWUSAGE;
223 ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
224 ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
225 AST_LIST_LOCK(&entry_list);
226 AST_LIST_TRAVERSE(&entry_list, entry, list)
228 AST_LIST_UNLOCK(&entry_list);
229 ast_cli(fd, "Number of entries: %d\n", count);
234 static struct ast_cli_entry cli_reload = {
235 .cmda = { "dnsmgr", "reload", NULL },
236 .handler = handle_cli_reload,
237 .summary = "Reloads the DNS manager configuration",
239 "Usage: dnsmgr reload\n"
240 " Reloads the DNS manager configuration.\n"
243 static struct ast_cli_entry cli_refresh = {
244 .cmda = { "dnsmgr", "refresh", NULL },
245 .handler = handle_cli_refresh,
246 .summary = "Performs an immediate refresh",
248 "Usage: dnsmgr refresh [pattern]\n"
249 " Peforms an immediate refresh of the managed DNS entries.\n"
250 " Optional regular expression pattern is used to filter the entries to refresh.\n",
253 static struct ast_cli_entry cli_status = {
254 .cmda = { "dnsmgr", "status", NULL },
255 .handler = handle_cli_status,
256 .summary = "Display the DNS manager status",
258 "Usage: dnsmgr status\n"
259 " Displays the DNS manager status.\n"
262 int dnsmgr_init(void)
264 sched = sched_context_create();
266 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
269 AST_LIST_HEAD_INIT(&entry_list);
270 ast_cli_register(&cli_reload);
271 ast_cli_register(&cli_status);
275 void dnsmgr_reload(void)
280 static int do_reload(int loading)
282 struct ast_config *config;
283 const char *interval_value;
284 const char *enabled_value;
290 /* ensure that no refresh cycles run while the reload is in progress */
291 ast_mutex_lock(&refresh_lock);
293 /* reset defaults in preparation for reading config file */
294 refresh_interval = REFRESH_DEFAULT;
295 was_enabled = enabled;
298 if (refresh_sched > -1)
299 ast_sched_del(sched, refresh_sched);
301 if ((config = ast_config_load("dnsmgr.conf"))) {
302 if ((enabled_value = ast_variable_retrieve(config, "general", "enabled"))) {
303 enabled = ast_true(enabled_value);
305 if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
306 if (sscanf(interval_value, "%d", &interval) < 1)
307 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
308 else if (interval < 0)
309 ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
311 refresh_interval = interval;
313 ast_config_destroy(config);
316 if (enabled && refresh_interval) {
317 refresh_sched = ast_sched_add(sched, refresh_interval * 1000, refresh_list, &master_refresh_info);
318 ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
321 /* if this reload enabled the manager, create the background thread
322 if it does not exist */
323 if (enabled && !was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
324 pthread_attr_init(&attr);
325 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
326 if (ast_pthread_create(&refresh_thread, &attr, do_refresh, NULL) < 0) {
327 ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
328 ast_sched_del(sched, refresh_sched);
331 ast_cli_register(&cli_refresh);
335 /* if this reload disabled the manager and there is a background thread,
337 else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
338 /* wake up the thread so it will exit */
339 pthread_cancel(refresh_thread);
340 pthread_kill(refresh_thread, SIGURG);
341 pthread_join(refresh_thread, NULL);
342 refresh_thread = AST_PTHREADT_NULL;
343 ast_cli_unregister(&cli_refresh);
349 ast_mutex_unlock(&refresh_lock);