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
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
);
99 memcpy(&entry
->last
, result
, sizeof(entry
->last
));
101 AST_LIST_LOCK(&entry_list
);
102 AST_LIST_INSERT_HEAD(&entry_list
, entry
, list
);
103 AST_LIST_UNLOCK(&entry_list
);
108 void ast_dnsmgr_release(struct ast_dnsmgr_entry
*entry
)
113 AST_LIST_LOCK(&entry_list
);
114 AST_LIST_REMOVE(&entry_list
, entry
, list
);
115 AST_LIST_UNLOCK(&entry_list
);
116 if (option_verbose
> 3)
117 ast_verbose(VERBOSE_PREFIX_4
"removing dns manager for '%s'\n", entry
->name
);
119 ast_mutex_destroy(&entry
->lock
);
123 int ast_dnsmgr_lookup(const char *name
, struct in_addr
*result
, struct ast_dnsmgr_entry
**dnsmgr
)
125 struct ast_hostent ahp
;
128 if (ast_strlen_zero(name
) || !result
|| !dnsmgr
)
131 if (*dnsmgr
&& !strcasecmp((*dnsmgr
)->name
, name
))
134 if (option_verbose
> 3)
135 ast_verbose(VERBOSE_PREFIX_4
"doing dnsmgr_lookup for '%s'\n", name
);
137 /* if it's actually an IP address and not a name,
138 there's no need for a managed lookup */
139 if (inet_aton(name
, result
))
142 /* do a lookup now but add a manager so it will automagically get updated in the background */
143 if ((hp
= ast_gethostbyname(name
, &ahp
)))
144 memcpy(result
, hp
->h_addr
, sizeof(result
));
146 /* if dnsmgr is not enable don't bother adding an entry */
150 if (option_verbose
> 2)
151 ast_verbose(VERBOSE_PREFIX_2
"adding dns manager for '%s'\n", name
);
152 *dnsmgr
= ast_dnsmgr_get(name
, result
);
157 * Refresh a dnsmgr entry
159 static int dnsmgr_refresh(struct ast_dnsmgr_entry
*entry
, int verbose
)
161 struct ast_hostent ahp
;
163 char iabuf
[INET_ADDRSTRLEN
];
164 char iabuf2
[INET_ADDRSTRLEN
];
168 ast_mutex_lock(&entry
->lock
);
169 if (verbose
&& (option_verbose
> 2))
170 ast_verbose(VERBOSE_PREFIX_2
"refreshing '%s'\n", entry
->name
);
172 if ((hp
= ast_gethostbyname(entry
->name
, &ahp
))) {
173 /* check to see if it has changed, do callback if requested (where de callback is defined ????) */
174 memcpy(&tmp
, hp
->h_addr
, sizeof(tmp
));
175 if (tmp
.s_addr
!= entry
->last
.s_addr
) {
176 ast_copy_string(iabuf
, ast_inet_ntoa(entry
->last
), sizeof(iabuf
));
177 ast_copy_string(iabuf2
, ast_inet_ntoa(tmp
), sizeof(iabuf2
));
178 ast_log(LOG_NOTICE
, "host '%s' changed from %s to %s\n",
179 entry
->name
, iabuf
, iabuf2
);
180 memcpy(entry
->result
, hp
->h_addr
, sizeof(entry
->result
));
181 memcpy(&entry
->last
, hp
->h_addr
, sizeof(entry
->last
));
182 changed
= entry
->changed
= 1;
186 ast_mutex_unlock(&entry
->lock
);
190 int ast_dnsmgr_refresh(struct ast_dnsmgr_entry
*entry
)
192 return dnsmgr_refresh(entry
, 0);
196 * Check if dnsmgr entry has changed from since last call to this function
198 int ast_dnsmgr_changed(struct ast_dnsmgr_entry
*entry
)
202 ast_mutex_lock(&entry
->lock
);
204 changed
= entry
->changed
;
207 ast_mutex_unlock(&entry
->lock
);
212 static void *do_refresh(void *data
)
215 pthread_testcancel();
216 usleep((ast_sched_wait(sched
)*1000));
217 pthread_testcancel();
218 ast_sched_runq(sched
);
223 static int refresh_list(const void *data
)
225 struct refresh_info
*info
= (struct refresh_info
*)data
;
226 struct ast_dnsmgr_entry
*entry
;
228 /* if a refresh or reload is already in progress, exit now */
229 if (ast_mutex_trylock(&refresh_lock
)) {
231 ast_log(LOG_WARNING
, "DNS Manager refresh already in progress.\n");
235 if (option_verbose
> 2)
236 ast_verbose(VERBOSE_PREFIX_2
"Refreshing DNS lookups.\n");
237 AST_LIST_LOCK(info
->entries
);
238 AST_LIST_TRAVERSE(info
->entries
, entry
, list
) {
239 if (info
->regex_present
&& regexec(&info
->filter
, entry
->name
, 0, NULL
, 0))
242 dnsmgr_refresh(entry
, info
->verbose
);
244 AST_LIST_UNLOCK(info
->entries
);
246 ast_mutex_unlock(&refresh_lock
);
248 /* automatically reschedule based on the interval */
249 return refresh_interval
* 1000;
252 void dnsmgr_start_refresh(void)
254 if (refresh_sched
> -1) {
255 AST_SCHED_DEL(sched
, refresh_sched
);
256 refresh_sched
= ast_sched_add_variable(sched
, 100, refresh_list
, &master_refresh_info
, 1);
260 static int do_reload(int loading
);
262 static int handle_cli_reload(int fd
, int argc
, char *argv
[])
265 return RESULT_SHOWUSAGE
;
271 static int handle_cli_refresh(int fd
, int argc
, char *argv
[])
273 struct refresh_info info
= {
274 .entries
= &entry_list
,
279 return RESULT_SHOWUSAGE
;
282 if (regcomp(&info
.filter
, argv
[2], REG_EXTENDED
| REG_NOSUB
))
283 return RESULT_SHOWUSAGE
;
285 info
.regex_present
= 1;
290 if (info
.regex_present
)
291 regfree(&info
.filter
);
296 static int handle_cli_status(int fd
, int argc
, char *argv
[])
299 struct ast_dnsmgr_entry
*entry
;
302 return RESULT_SHOWUSAGE
;
304 ast_cli(fd
, "DNS Manager: %s\n", enabled
? "enabled" : "disabled");
305 ast_cli(fd
, "Refresh Interval: %d seconds\n", refresh_interval
);
306 AST_LIST_LOCK(&entry_list
);
307 AST_LIST_TRAVERSE(&entry_list
, entry
, list
)
309 AST_LIST_UNLOCK(&entry_list
);
310 ast_cli(fd
, "Number of entries: %d\n", count
);
315 static struct ast_cli_entry cli_reload
= {
316 { "dnsmgr", "reload", NULL
},
317 handle_cli_reload
, "Reloads the DNS manager configuration",
318 "Usage: dnsmgr reload\n"
319 " Reloads the DNS manager configuration.\n"
322 static struct ast_cli_entry cli_refresh
= {
323 { "dnsmgr", "refresh", NULL
},
324 handle_cli_refresh
, "Performs an immediate refresh",
325 "Usage: dnsmgr refresh [pattern]\n"
326 " Peforms an immediate refresh of the managed DNS entries.\n"
327 " Optional regular expression pattern is used to filter the entries to refresh.\n",
330 static struct ast_cli_entry cli_status
= {
331 { "dnsmgr", "status", NULL
},
332 handle_cli_status
, "Display the DNS manager status",
333 "Usage: dnsmgr status\n"
334 " Displays the DNS manager status.\n"
337 int dnsmgr_init(void)
339 if (!(sched
= sched_context_create())) {
340 ast_log(LOG_ERROR
, "Unable to create schedule context.\n");
343 ast_cli_register(&cli_reload
);
344 ast_cli_register(&cli_status
);
348 int dnsmgr_reload(void)
353 static int do_reload(int loading
)
355 struct ast_config
*config
;
356 const char *interval_value
;
357 const char *enabled_value
;
362 /* ensure that no refresh cycles run while the reload is in progress */
363 ast_mutex_lock(&refresh_lock
);
365 /* reset defaults in preparation for reading config file */
366 refresh_interval
= REFRESH_DEFAULT
;
367 was_enabled
= enabled
;
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 */
393 if (!was_enabled
&& (refresh_thread
== AST_PTHREADT_NULL
)) {
394 if (ast_pthread_create_background(&refresh_thread
, NULL
, do_refresh
, NULL
) < 0) {
395 ast_log(LOG_ERROR
, "Unable to start refresh thread.\n");
397 ast_cli_register(&cli_refresh
);
399 /* make a background refresh happen right away */
400 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
);