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>
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <sys/socket.h>
29 #include <arpa/inet.h>
40 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
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 struct in_addr
*result
;
57 AST_LIST_ENTRY(ast_dnsmgr_entry
) list
;
61 static AST_LIST_HEAD(entry_list
, ast_dnsmgr_entry
) entry_list
;
63 AST_MUTEX_DEFINE_STATIC(refresh_lock
);
65 #define REFRESH_DEFAULT 300
67 static int enabled
= 0;
68 static int refresh_interval
;
71 struct entry_list
*entries
;
73 unsigned int regex_present
:1;
77 static struct refresh_info master_refresh_info
= {
78 .entries
= &entry_list
,
82 struct ast_dnsmgr_entry
*ast_dnsmgr_get(const char *name
, struct in_addr
*result
)
84 struct ast_dnsmgr_entry
*entry
;
86 if (!result
|| ast_strlen_zero(name
) || !(entry
= ast_calloc(1, sizeof(*entry
) + strlen(name
))))
89 entry
->result
= result
;
90 strcpy(entry
->name
, name
);
92 AST_LIST_LOCK(&entry_list
);
93 AST_LIST_INSERT_HEAD(&entry_list
, entry
, list
);
94 AST_LIST_UNLOCK(&entry_list
);
99 void ast_dnsmgr_release(struct ast_dnsmgr_entry
*entry
)
104 AST_LIST_LOCK(&entry_list
);
105 AST_LIST_REMOVE(&entry_list
, entry
, list
);
106 AST_LIST_UNLOCK(&entry_list
);
110 int ast_dnsmgr_lookup(const char *name
, struct in_addr
*result
, struct ast_dnsmgr_entry
**dnsmgr
)
112 if (ast_strlen_zero(name
) || !result
|| !dnsmgr
)
115 if (*dnsmgr
&& !strcasecmp((*dnsmgr
)->name
, name
))
118 if (option_verbose
> 3)
119 ast_verbose(VERBOSE_PREFIX_3
"doing lookup for '%s'\n", name
);
121 /* if it's actually an IP address and not a name,
122 there's no need for a managed lookup */
123 if (inet_aton(name
, result
))
126 /* if the manager is disabled, do a direct lookup and return the result,
127 otherwise register a managed lookup for the name */
129 struct ast_hostent ahp
;
132 if ((hp
= ast_gethostbyname(name
, &ahp
)))
133 memcpy(result
, hp
->h_addr
, sizeof(result
));
136 if (option_verbose
> 2)
137 ast_verbose(VERBOSE_PREFIX_2
"adding manager for '%s'\n", name
);
138 *dnsmgr
= ast_dnsmgr_get(name
, result
);
143 static void *do_refresh(void *data
)
146 pthread_testcancel();
147 usleep(ast_sched_wait(sched
));
148 pthread_testcancel();
149 ast_sched_runq(sched
);
154 static int refresh_list(void *data
)
156 struct refresh_info
*info
= data
;
157 struct ast_dnsmgr_entry
*entry
;
158 struct ast_hostent ahp
;
161 /* if a refresh or reload is already in progress, exit now */
162 if (ast_mutex_trylock(&refresh_lock
)) {
164 ast_log(LOG_WARNING
, "DNS Manager refresh already in progress.\n");
168 if (option_verbose
> 2)
169 ast_verbose(VERBOSE_PREFIX_2
"Refreshing DNS lookups.\n");
170 AST_LIST_LOCK(info
->entries
);
171 AST_LIST_TRAVERSE(info
->entries
, entry
, list
) {
172 if (info
->regex_present
&& regexec(&info
->filter
, entry
->name
, 0, NULL
, 0))
175 if (info
->verbose
&& (option_verbose
> 2))
176 ast_verbose(VERBOSE_PREFIX_2
"refreshing '%s'\n", entry
->name
);
178 if ((hp
= ast_gethostbyname(entry
->name
, &ahp
))) {
179 /* check to see if it has changed, do callback if requested */
180 memcpy(entry
->result
, hp
->h_addr
, sizeof(entry
->result
));
183 AST_LIST_UNLOCK(info
->entries
);
185 ast_mutex_unlock(&refresh_lock
);
187 /* automatically reschedule based on the interval */
188 return refresh_interval
* 1000;
191 void dnsmgr_start_refresh(void)
193 if (refresh_sched
> -1) {
194 ast_sched_del(sched
, refresh_sched
);
195 refresh_sched
= ast_sched_add_variable(sched
, 100, refresh_list
, &master_refresh_info
, 1);
199 static int do_reload(int loading
);
201 static int handle_cli_reload(int fd
, int argc
, char *argv
[])
204 return RESULT_SHOWUSAGE
;
210 static int handle_cli_refresh(int fd
, int argc
, char *argv
[])
212 struct refresh_info info
= {
213 .entries
= &entry_list
,
218 return RESULT_SHOWUSAGE
;
221 if (regcomp(&info
.filter
, argv
[2], REG_EXTENDED
| REG_NOSUB
))
222 return RESULT_SHOWUSAGE
;
224 info
.regex_present
= 1;
229 if (info
.regex_present
)
230 regfree(&info
.filter
);
235 static int handle_cli_status(int fd
, int argc
, char *argv
[])
238 struct ast_dnsmgr_entry
*entry
;
241 return RESULT_SHOWUSAGE
;
243 ast_cli(fd
, "DNS Manager: %s\n", enabled
? "enabled" : "disabled");
244 ast_cli(fd
, "Refresh Interval: %d seconds\n", refresh_interval
);
245 AST_LIST_LOCK(&entry_list
);
246 AST_LIST_TRAVERSE(&entry_list
, entry
, list
)
248 AST_LIST_UNLOCK(&entry_list
);
249 ast_cli(fd
, "Number of entries: %d\n", count
);
254 static struct ast_cli_entry cli_reload
= {
255 .cmda
= { "dnsmgr", "reload", NULL
},
256 .handler
= handle_cli_reload
,
257 .summary
= "Reloads the DNS manager configuration",
259 "Usage: dnsmgr reload\n"
260 " Reloads the DNS manager configuration.\n"
263 static struct ast_cli_entry cli_refresh
= {
264 .cmda
= { "dnsmgr", "refresh", NULL
},
265 .handler
= handle_cli_refresh
,
266 .summary
= "Performs an immediate refresh",
268 "Usage: dnsmgr refresh [pattern]\n"
269 " Peforms an immediate refresh of the managed DNS entries.\n"
270 " Optional regular expression pattern is used to filter the entries to refresh.\n",
273 static struct ast_cli_entry cli_status
= {
274 .cmda
= { "dnsmgr", "status", NULL
},
275 .handler
= handle_cli_status
,
276 .summary
= "Display the DNS manager status",
278 "Usage: dnsmgr status\n"
279 " Displays the DNS manager status.\n"
282 int dnsmgr_init(void)
284 if (!(sched
= sched_context_create())) {
285 ast_log(LOG_ERROR
, "Unable to create schedule context.\n");
288 AST_LIST_HEAD_INIT(&entry_list
);
289 ast_cli_register(&cli_reload
);
290 ast_cli_register(&cli_status
);
294 int dnsmgr_reload(void)
299 static int do_reload(int loading
)
301 struct ast_config
*config
;
302 const char *interval_value
;
303 const char *enabled_value
;
308 /* ensure that no refresh cycles run while the reload is in progress */
309 ast_mutex_lock(&refresh_lock
);
311 /* reset defaults in preparation for reading config file */
312 refresh_interval
= REFRESH_DEFAULT
;
313 was_enabled
= enabled
;
316 if (refresh_sched
> -1)
317 ast_sched_del(sched
, refresh_sched
);
319 if ((config
= ast_config_load("dnsmgr.conf"))) {
320 if ((enabled_value
= ast_variable_retrieve(config
, "general", "enable"))) {
321 enabled
= ast_true(enabled_value
);
323 if ((interval_value
= ast_variable_retrieve(config
, "general", "refreshinterval"))) {
324 if (sscanf(interval_value
, "%d", &interval
) < 1)
325 ast_log(LOG_WARNING
, "Unable to convert '%s' to a numeric value.\n", interval_value
);
326 else if (interval
< 0)
327 ast_log(LOG_WARNING
, "Invalid refresh interval '%d' specified, using default\n", interval
);
329 refresh_interval
= interval
;
331 ast_config_destroy(config
);
334 if (enabled
&& refresh_interval
)
335 ast_log(LOG_NOTICE
, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval
);
337 /* if this reload enabled the manager, create the background thread
338 if it does not exist */
339 if (enabled
&& !was_enabled
&& (refresh_thread
== AST_PTHREADT_NULL
)) {
340 if (ast_pthread_create(&refresh_thread
, NULL
, do_refresh
, NULL
) < 0) {
341 ast_log(LOG_ERROR
, "Unable to start refresh thread.\n");
344 ast_cli_register(&cli_refresh
);
345 /* make a background refresh happen right away */
346 refresh_sched
= ast_sched_add_variable(sched
, 100, refresh_list
, &master_refresh_info
, 1);
350 /* if this reload disabled the manager and there is a background thread,
352 else if (!enabled
&& was_enabled
&& (refresh_thread
!= AST_PTHREADT_NULL
)) {
353 /* wake up the thread so it will exit */
354 pthread_cancel(refresh_thread
);
355 pthread_kill(refresh_thread
, SIGURG
);
356 pthread_join(refresh_thread
, NULL
);
357 refresh_thread
= AST_PTHREADT_NULL
;
358 ast_cli_unregister(&cli_refresh
);
364 ast_mutex_unlock(&refresh_lock
);