add the other two files that should have been on this target... oops
[asterisk-bristuff.git] / dnsmgr.c
blobfac0c8dafcc3753afe96331b226ff307e681e2e7
1 /*
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.
19 /*! \file
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>
30 #include <resolv.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <regex.h>
36 #include <signal.h>
38 #include "asterisk.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;
58 char name[1];
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;
70 struct refresh_info {
71 struct entry_list *entries;
72 int verbose;
73 unsigned int regex_present:1;
74 regex_t filter;
77 static struct refresh_info master_refresh_info = {
78 .entries = &entry_list,
79 .verbose = 0,
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))))
87 return NULL;
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);
96 return entry;
99 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
101 if (!entry)
102 return;
104 AST_LIST_LOCK(&entry_list);
105 AST_LIST_REMOVE(&entry_list, entry, list);
106 AST_LIST_UNLOCK(&entry_list);
107 free(entry);
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)
113 return -1;
115 if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
116 return 0;
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))
124 return 0;
126 /* if the manager is disabled, do a direct lookup and return the result,
127 otherwise register a managed lookup for the name */
128 if (!enabled) {
129 struct ast_hostent ahp;
130 struct hostent *hp;
132 if ((hp = ast_gethostbyname(name, &ahp)))
133 memcpy(result, hp->h_addr, sizeof(result));
134 return 0;
135 } else {
136 if (option_verbose > 2)
137 ast_verbose(VERBOSE_PREFIX_2 "adding manager for '%s'\n", name);
138 *dnsmgr = ast_dnsmgr_get(name, result);
139 return !*dnsmgr;
143 static void *do_refresh(void *data)
145 for (;;) {
146 pthread_testcancel();
147 usleep(ast_sched_wait(sched));
148 pthread_testcancel();
149 ast_sched_runq(sched);
151 return NULL;
154 static int refresh_list(void *data)
156 struct refresh_info *info = data;
157 struct ast_dnsmgr_entry *entry;
158 struct ast_hostent ahp;
159 struct hostent *hp;
161 /* if a refresh or reload is already in progress, exit now */
162 if (ast_mutex_trylock(&refresh_lock)) {
163 if (info->verbose)
164 ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
165 return -1;
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))
173 continue;
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[])
203 if (argc > 2)
204 return RESULT_SHOWUSAGE;
206 do_reload(0);
207 return 0;
210 static int handle_cli_refresh(int fd, int argc, char *argv[])
212 struct refresh_info info = {
213 .entries = &entry_list,
214 .verbose = 1,
217 if (argc > 3)
218 return RESULT_SHOWUSAGE;
220 if (argc == 3) {
221 if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
222 return RESULT_SHOWUSAGE;
223 else
224 info.regex_present = 1;
227 refresh_list(&info);
229 if (info.regex_present)
230 regfree(&info.filter);
232 return 0;
235 static int handle_cli_status(int fd, int argc, char *argv[])
237 int count = 0;
238 struct ast_dnsmgr_entry *entry;
240 if (argc > 2)
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)
247 count++;
248 AST_LIST_UNLOCK(&entry_list);
249 ast_cli(fd, "Number of entries: %d\n", count);
251 return 0;
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",
258 .usage =
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",
267 .usage =
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",
277 .usage =
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");
286 return -1;
288 AST_LIST_HEAD_INIT(&entry_list);
289 ast_cli_register(&cli_reload);
290 ast_cli_register(&cli_status);
291 return do_reload(1);
294 int dnsmgr_reload(void)
296 return do_reload(0);
299 static int do_reload(int loading)
301 struct ast_config *config;
302 const char *interval_value;
303 const char *enabled_value;
304 int interval;
305 int was_enabled;
306 int res = -1;
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;
314 enabled = 0;
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);
328 else
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");
343 else {
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);
347 res = 0;
350 /* if this reload disabled the manager and there is a background thread,
351 kill it */
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);
359 res = 0;
361 else
362 res = 0;
364 ast_mutex_unlock(&refresh_lock);
366 return res;