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.
21 * \brief Background DNS update manager
23 * \author Kevin P. Fleming <kpfleming@digium.com>
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>
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 */
60 /*! Set to 1 if the entry changes */
63 AST_LIST_ENTRY(ast_dnsmgr_entry
) list
;
64 /*! just 1 here, but we use calloc to allocate the correct size */
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
;
78 struct entry_list
*entries
;
80 unsigned int regex_present
:1;
84 static struct refresh_info master_refresh_info
= {
85 .entries
= &entry_list
,
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
))))
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
);
107 void ast_dnsmgr_release(struct ast_dnsmgr_entry
*entry
)
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
);
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
)
127 if (*dnsmgr
&& !strcasecmp((*dnsmgr
)->name
, name
))
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
))
138 /* if the manager is disabled, do a direct lookup and return the result,
139 otherwise register a managed lookup for the name */
141 struct ast_hostent ahp
;
144 if ((hp
= ast_gethostbyname(name
, &ahp
)))
145 memcpy(result
, hp
->h_addr
, sizeof(result
));
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
);
156 * Refresh a dnsmgr entry
158 static int dnsmgr_refresh(struct ast_dnsmgr_entry
*entry
, int verbose
)
160 struct ast_hostent ahp
;
162 char iabuf
[INET_ADDRSTRLEN
];
163 char iabuf2
[INET_ADDRSTRLEN
];
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_copy_string(iabuf
, ast_inet_ntoa(entry
->last
), sizeof(iabuf
));
176 ast_copy_string(iabuf2
, ast_inet_ntoa(tmp
), sizeof(iabuf2
));
177 ast_log(LOG_NOTICE
, "host '%s' changed from %s to %s\n",
178 entry
->name
, iabuf
, iabuf2
);
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
);
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
)
201 ast_mutex_lock(&entry
->lock
);
203 changed
= entry
->changed
;
206 ast_mutex_unlock(&entry
->lock
);
211 static void *do_refresh(void *data
)
214 pthread_testcancel();
215 usleep(ast_sched_wait(sched
));
216 pthread_testcancel();
217 ast_sched_runq(sched
);
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
)) {
230 ast_log(LOG_WARNING
, "DNS Manager refresh already in progress.\n");
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))
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
[])
264 return RESULT_SHOWUSAGE
;
270 static int handle_cli_refresh(int fd
, int argc
, char *argv
[])
272 struct refresh_info info
= {
273 .entries
= &entry_list
,
278 return RESULT_SHOWUSAGE
;
281 if (regcomp(&info
.filter
, argv
[2], REG_EXTENDED
| REG_NOSUB
))
282 return RESULT_SHOWUSAGE
;
284 info
.regex_present
= 1;
289 if (info
.regex_present
)
290 regfree(&info
.filter
);
295 static int handle_cli_status(int fd
, int argc
, char *argv
[])
298 struct ast_dnsmgr_entry
*entry
;
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
)
308 AST_LIST_UNLOCK(&entry_list
);
309 ast_cli(fd
, "Number of entries: %d\n", count
);
314 static struct ast_cli_entry cli_reload
= {
315 { "dnsmgr", "reload", NULL
},
316 handle_cli_reload
, "Reloads the DNS manager configuration",
317 "Usage: dnsmgr reload\n"
318 " Reloads the DNS manager configuration.\n"
321 static struct ast_cli_entry cli_refresh
= {
322 { "dnsmgr", "refresh", NULL
},
323 handle_cli_refresh
, "Performs an immediate refresh",
324 "Usage: dnsmgr refresh [pattern]\n"
325 " Peforms an immediate refresh of the managed DNS entries.\n"
326 " Optional regular expression pattern is used to filter the entries to refresh.\n",
329 static struct ast_cli_entry cli_status
= {
330 { "dnsmgr", "status", NULL
},
331 handle_cli_status
, "Display the DNS manager status",
332 "Usage: dnsmgr status\n"
333 " Displays the DNS manager status.\n"
336 int dnsmgr_init(void)
338 if (!(sched
= sched_context_create())) {
339 ast_log(LOG_ERROR
, "Unable to create schedule context.\n");
342 ast_cli_register(&cli_reload
);
343 ast_cli_register(&cli_status
);
347 int dnsmgr_reload(void)
352 static int do_reload(int loading
)
354 struct ast_config
*config
;
355 const char *interval_value
;
356 const char *enabled_value
;
361 /* ensure that no refresh cycles run while the reload is in progress */
362 ast_mutex_lock(&refresh_lock
);
364 /* reset defaults in preparation for reading config file */
365 refresh_interval
= REFRESH_DEFAULT
;
366 was_enabled
= enabled
;
369 if (refresh_sched
> -1)
370 ast_sched_del(sched
, refresh_sched
);
372 if ((config
= ast_config_load("dnsmgr.conf"))) {
373 if ((enabled_value
= ast_variable_retrieve(config
, "general", "enable"))) {
374 enabled
= ast_true(enabled_value
);
376 if ((interval_value
= ast_variable_retrieve(config
, "general", "refreshinterval"))) {
377 if (sscanf(interval_value
, "%d", &interval
) < 1)
378 ast_log(LOG_WARNING
, "Unable to convert '%s' to a numeric value.\n", interval_value
);
379 else if (interval
< 0)
380 ast_log(LOG_WARNING
, "Invalid refresh interval '%d' specified, using default\n", interval
);
382 refresh_interval
= interval
;
384 ast_config_destroy(config
);
387 if (enabled
&& refresh_interval
)
388 ast_log(LOG_NOTICE
, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval
);
390 /* if this reload enabled the manager, create the background thread
391 if it does not exist */
392 if (enabled
&& !was_enabled
&& (refresh_thread
== AST_PTHREADT_NULL
)) {
393 if (ast_pthread_create(&refresh_thread
, NULL
, do_refresh
, NULL
) < 0) {
394 ast_log(LOG_ERROR
, "Unable to start refresh thread.\n");
397 ast_cli_register(&cli_refresh
);
398 /* make a background refresh happen right away */
399 refresh_sched
= ast_sched_add_variable(sched
, 100, refresh_list
, &master_refresh_info
, 1);
403 /* if this reload disabled the manager and there is a background thread,
405 else if (!enabled
&& was_enabled
&& (refresh_thread
!= AST_PTHREADT_NULL
)) {
406 /* wake up the thread so it will exit */
407 pthread_cancel(refresh_thread
);
408 pthread_kill(refresh_thread
, SIGURG
);
409 pthread_join(refresh_thread
, NULL
);
410 refresh_thread
= AST_PTHREADT_NULL
;
411 ast_cli_unregister(&cli_refresh
);
417 ast_mutex_unlock(&refresh_lock
);