Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:53 2009

## Asterisk developer's documentation

- Main Page
- Related Pages
- Modules
- Data Structures
- Files
- Directories

# dnsmgr.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 2005-2006, Kevin P. Fleming
    00005  *
    00006  * Kevin P. Fleming <kpfleming@digium.com>
    00007  *
    00008  * See http://www.asterisk.org for more information about
    00009  * the Asterisk project. Please do not directly contact
    00010  * any of the maintainers of this project for assistance;
    00011  * the project provides a web site, mailing lists and IRC
    00012  * channels for your use.
    00013  *
    00014  * This program is free software, distributed under the terms of
    00015  * the GNU General Public License Version 2. See the LICENSE file
    00016  * at the top of the source tree.
    00017  */
    00018
    00019 /*! \file
    00020  *
    00021  * \brief Background DNS update manager
    00022  *
    00023  */
    00024
    00025 #include <sys/types.h>
    00026 #include <netinet/in.h>
    00027 #include <sys/socket.h>
    00028 #include <arpa/inet.h>
    00029 #include <resolv.h>
    00030 #include <stdio.h>
    00031 #include <string.h>
    00032 #include <unistd.h>
    00033 #include <stdlib.h>
    00034 #include <regex.h>
    00035 #include <signal.h>
    00036
    00037 #include "asterisk.h"
    00038
    00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211526 $")
    00040
    00041 #include "asterisk/dnsmgr.h"
    00042 #include "asterisk/linkedlists.h"
    00043 #include "asterisk/utils.h"
    00044 #include "asterisk/config.h"
    00045 #include "asterisk/logger.h"
    00046 #include "asterisk/sched.h"
    00047 #include "asterisk/options.h"
    00048 #include "asterisk/cli.h"
    00049
    00050 static struct sched_context *sched;
    00051 static int refresh_sched = -1;
    00052 static pthread_t refresh_thread = AST_PTHREADT_NULL;
    00053
    00054 struct ast_dnsmgr_entry {
    00055    struct in_addr *result;
    00056    AST_LIST_ENTRY(ast_dnsmgr_entry) list;
    00057    char name[1];
    00058 };
    00059
    00060 static AST_LIST_HEAD_STATIC(entry_list, ast_dnsmgr_entry);
    00061
    00062 AST_MUTEX_DEFINE_STATIC(refresh_lock);
    00063
    00064 #define REFRESH_DEFAULT 300
    00065
    00066 static int enabled = 0;
    00067 static int refresh_interval;
    00068
    00069 struct refresh_info {
    00070    struct entry_list *entries;
    00071    int verbose;
    00072    unsigned int regex_present:1;
    00073    regex_t filter;
    00074 };
    00075
    00076 static struct refresh_info master_refresh_info = {
    00077    .entries = &entry_list,
    00078    .verbose = 0,
    00079 };
    00080
    00081 struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result)
    00082 {
    00083    struct ast_dnsmgr_entry *entry;
    00084
    00085    if (!result || ast_strlen_zero(name))
    00086       return NULL;
    00087
    00088    entry = calloc(1, sizeof(*entry) + strlen(name));
    00089    if (!entry)
    00090       return NULL;
    00091
    00092    entry->result = result;
    00093    strcpy(entry->name, name);
    00094
    00095    AST_LIST_LOCK(&entry_list);
    00096    AST_LIST_INSERT_HEAD(&entry_list, entry, list);
    00097    AST_LIST_UNLOCK(&entry_list);
    00098
    00099    return entry;
    00100 }
    00101
    00102 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
    00103 {
    00104    if (!entry)
    00105       return;
    00106
    00107    AST_LIST_LOCK(&entry_list);
    00108    AST_LIST_REMOVE(&entry_list, entry, list);
    00109    AST_LIST_UNLOCK(&entry_list);
    00110    free(entry);
    00111 }
    00112
    00113 int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
    00114 {
    00115    struct ast_hostent ahp;
    00116    struct hostent *hp;
    00117
    00118    if (ast_strlen_zero(name) || !result || !dnsmgr)
    00119       return -1;
    00120
    00121    if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
    00122       return 0;
    00123
    00124    if (option_verbose > 3)
    00125       ast_verbose(VERBOSE_PREFIX_3 "doing lookup for '%s'\n", name);
    00126
    00127    /* if it's actually an IP address and not a name,
    00128       there's no need for a managed lookup */
    00129    if (inet_aton(name, result))
    00130       return 0;
    00131
    00132    /* do a lookup now but add a manager so it will automagically get updated in the background */
    00133    if ((hp = ast_gethostbyname(name, &ahp)))
    00134       memcpy(result, hp->h_addr, sizeof(result));
    00135
    00136    /* if dnsmgr is not enable don't bother adding an entry */
    00137    if (!enabled)
    00138       return 0;
    00139
    00140    if (option_verbose > 2)
    00141       ast_verbose(VERBOSE_PREFIX_2 "adding manager for '%s'\n", name);
    00142    *dnsmgr = ast_dnsmgr_get(name, result);
    00143    return !*dnsmgr;
    00144 }
    00145
    00146 static void *do_refresh(void *data)
    00147 {
    00148    for (;;) {
    00149       pthread_testcancel();
    00150       usleep((ast_sched_wait(sched)*1000));
    00151       pthread_testcancel();
    00152       ast_sched_runq(sched);
    00153    }
    00154    return NULL;
    00155 }
    00156
    00157 static int refresh_list(void *data)
    00158 {
    00159    struct refresh_info *info = data;
    00160    struct ast_dnsmgr_entry *entry;
    00161    struct ast_hostent ahp;
    00162    struct hostent *hp;
    00163
    00164    /* if a refresh or reload is already in progress, exit now */
    00165    if (ast_mutex_trylock(&refresh_lock)) {
    00166       if (info->verbose)
    00167          ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
    00168       return -1;
    00169    }
    00170
    00171    if (option_verbose > 2)
    00172       ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
    00173    AST_LIST_LOCK(info->entries);
    00174    AST_LIST_TRAVERSE(info->entries, entry, list) {
    00175       if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
    00176           continue;
    00177
    00178       if (info->verbose && (option_verbose > 2))
    00179          ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name);
    00180
    00181       if ((hp = ast_gethostbyname(entry->name, &ahp))) {
    00182          /* check to see if it has changed, do callback if requested */
    00183          memcpy(entry->result, hp->h_addr, sizeof(entry->result));
    00184       }
    00185    }
    00186    AST_LIST_UNLOCK(info->entries);
    00187
    00188    ast_mutex_unlock(&refresh_lock);
    00189
    00190    /* automatically reschedule based on the interval */
    00191    return refresh_interval * 1000;
    00192 }
    00193
    00194 void dnsmgr_start_refresh(void)
    00195 {
    00196    if (refresh_sched > -1) {
    00197       ast_sched_del(sched, refresh_sched);
    00198       refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
    00199    }
    00200 }
    00201
    00202 static int do_reload(int loading);
    00203
    00204 static int handle_cli_reload(int fd, int argc, char *argv[])
    00205 {
    00206    if (argc > 2)
    00207       return RESULT_SHOWUSAGE;
    00208
    00209    do_reload(0);
    00210    return 0;
    00211 }
    00212
    00213 static int handle_cli_refresh(int fd, int argc, char *argv[])
    00214 {
    00215    struct refresh_info info = {
    00216       .entries = &entry_list,
    00217       .verbose = 1,
    00218    };
    00219
    00220    if (argc > 3)
    00221       return RESULT_SHOWUSAGE;
    00222
    00223    if (argc == 3) {
    00224       if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
    00225          return RESULT_SHOWUSAGE;
    00226       else
    00227          info.regex_present = 1;
    00228    }
    00229
    00230    refresh_list(&info);
    00231
    00232    if (info.regex_present)
    00233       regfree(&info.filter);
    00234
    00235    return 0;
    00236 }
    00237
    00238 static int handle_cli_status(int fd, int argc, char *argv[])
    00239 {
    00240    int count = 0;
    00241    struct ast_dnsmgr_entry *entry;
    00242
    00243    if (argc > 2)
    00244       return RESULT_SHOWUSAGE;
    00245
    00246    ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
    00247    ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
    00248    AST_LIST_LOCK(&entry_list);
    00249    AST_LIST_TRAVERSE(&entry_list, entry, list)
    00250       count++;
    00251    AST_LIST_UNLOCK(&entry_list);
    00252    ast_cli(fd, "Number of entries: %d\n", count);
    00253
    00254    return 0;
    00255 }
    00256
    00257 static struct ast_cli_entry cli_reload = {
    00258    .cmda = { "dnsmgr", "reload", NULL },
    00259    .handler = handle_cli_reload,
    00260    .summary = "Reloads the DNS manager configuration",
    00261    .usage =
    00262    "Usage: dnsmgr reload\n"
    00263    "       Reloads the DNS manager configuration.\n"
    00264 };
    00265
    00266 static struct ast_cli_entry cli_refresh = {
    00267    .cmda = { "dnsmgr", "refresh", NULL },
    00268    .handler = handle_cli_refresh,
    00269    .summary = "Performs an immediate refresh",
    00270    .usage =
    00271    "Usage: dnsmgr refresh [pattern]\n"
    00272    "       Peforms an immediate refresh of the managed DNS entries.\n"
    00273    "       Optional regular expression pattern is used to filter the entries to refresh.\n",
    00274 };
    00275
    00276 static struct ast_cli_entry cli_status = {
    00277    .cmda = { "dnsmgr", "status", NULL },
    00278    .handler = handle_cli_status,
    00279    .summary = "Display the DNS manager status",
    00280    .usage =
    00281    "Usage: dnsmgr status\n"
    00282    "       Displays the DNS manager status.\n"
    00283 };
    00284
    00285 int dnsmgr_init(void)
    00286 {
    00287    sched = sched_context_create();
    00288    if (!sched) {
    00289       ast_log(LOG_ERROR, "Unable to create schedule context.\n");
    00290       return -1;
    00291    }
    00292    ast_cli_register(&cli_reload);
    00293    ast_cli_register(&cli_status);
    00294    return do_reload(1);
    00295 }
    00296
    00297 void dnsmgr_reload(void)
    00298 {
    00299    do_reload(0);
    00300 }
    00301
    00302 static int do_reload(int loading)
    00303 {
    00304    struct ast_config *config;
    00305    const char *interval_value;
    00306    const char *enabled_value;
    00307    int interval;
    00308    int was_enabled;
    00309    int res = -1;
    00310
    00311    /* ensure that no refresh cycles run while the reload is in progress */
    00312    ast_mutex_lock(&refresh_lock);
    00313
    00314    /* reset defaults in preparation for reading config file */
    00315    refresh_interval = REFRESH_DEFAULT;
    00316    was_enabled = enabled;
    00317    enabled = 0;
    00318
    00319    if (refresh_sched > -1)
    00320       ast_sched_del(sched, refresh_sched);
    00321
    00322    if ((config = ast_config_load("dnsmgr.conf"))) {
    00323       if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
    00324          enabled = ast_true(enabled_value);
    00325       }
    00326       if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
    00327          if (sscanf(interval_value, "%30d", &interval) < 1)
    00328             ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
    00329          else if (interval < 0)
    00330             ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
    00331          else
    00332             refresh_interval = interval;
    00333       }
    00334       ast_config_destroy(config);
    00335    }
    00336
    00337    if (enabled && refresh_interval)
    00338       ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
    00339
    00340    /* if this reload enabled the manager, create the background thread
    00341       if it does not exist */
    00342    if (enabled) {
    00343       if (!was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
    00344          if (ast_pthread_create(&refresh_thread, NULL, do_refresh, NULL) < 0) {
    00345             ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
    00346          }
    00347          ast_cli_register(&cli_refresh);
    00348       }
    00349       /* make a background refresh happen right away */
    00350       refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
    00351       res = 0;
    00352    }
    00353    /* if this reload disabled the manager and there is a background thread,
    00354       kill it */
    00355    else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
    00356       /* wake up the thread so it will exit */
    00357       pthread_cancel(refresh_thread);
    00358       pthread_kill(refresh_thread, SIGURG);
    00359       pthread_join(refresh_thread, NULL);
    00360       refresh_thread = AST_PTHREADT_NULL;
    00361       ast_cli_unregister(&cli_refresh);
    00362       res = 0;
    00363    }
    00364    else
    00365       res = 0;
    00366
    00367    ast_mutex_unlock(&refresh_lock);
    00368
    00369    return res;
    00370 }