Update of /usr/cvsroot/asterisk
In directory mongoose.digium.com:/tmp/cvs-serv17258

Added Files:
        dnsmgr.c 
Log Message:
preliminary "managed DNS lookup" support


--- NEW FILE: dnsmgr.c ---
/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Background DNS update manager
 * 
 * Copyright (C) 2005, Kevin P. Fleming
 *
 * Kevin P. Fleming <[EMAIL PROTECTED]>
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License
 */

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <regex.h>
#include <signal.h>

#include <asterisk/dnsmgr.h>
#include <asterisk/linkedlists.h>
#include <asterisk/utils.h>
#include <asterisk/config.h>
#include <asterisk/logger.h>
#include <asterisk/sched.h>
#include <asterisk/options.h>
#include <asterisk/cli.h>
#include "asterisk.h"

static struct sched_context *sched;
static int refresh_sched = -1;
static pthread_t refresh_thread = AST_PTHREADT_NULL;

struct ast_dnsmgr_entry {
        struct in_addr *result;
        AST_LIST_ENTRY(ast_dnsmgr_entry) list;
        char name[1];
};

static AST_LIST_HEAD(entry_list, ast_dnsmgr_entry) entry_list;

AST_MUTEX_DEFINE_STATIC(refresh_lock);

#define REFRESH_DEFAULT 300

static int enabled = 0;
static int refresh_interval;

struct refresh_info {
        struct entry_list *entries;
        int verbose;
        regex_t filter;
};

static struct refresh_info master_refresh_info = {
        .entries = &entry_list,
        .verbose = 0,
};

struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr 
*result)
{
        struct ast_dnsmgr_entry *entry;

        if (!name || !result || ast_strlen_zero(name))
                return NULL;

        entry = calloc(1, sizeof(*result) + strlen(name));
        if (!entry)
                return NULL;

        entry->result = result;
        strcpy(entry->name, name);

        AST_LIST_LOCK(&entry_list);
        AST_LIST_INSERT_HEAD(&entry_list, entry, list);
        AST_LIST_UNLOCK(&entry_list);

        return entry;
}

void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
{
        /* if there is an entry (and not the special flag value), remove/free 
it */
        if (entry && (entry != (typeof(entry)) -1)) {
                AST_LIST_LOCK(&entry_list);
                AST_LIST_REMOVE(&entry_list, entry, list);
                AST_LIST_UNLOCK(&entry_list);
                free(entry);
        };
}

int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct 
ast_dnsmgr_entry **dnsmgr)
{
        if (!name || ast_strlen_zero(name) || !result || !dnsmgr)
                return -1;

        if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
                return 0;

        if (option_verbose > 3)
                ast_verbose(VERBOSE_PREFIX_3 "doing lookup for '%s'\n", name);

        /* if it's actually an IP address and not a name,
           there's no need for a managed lookup */
        if (inet_aton(name, result))
                return 0;

        /* if the manager is disabled, do a direct lookup and return the result,
           otherwise register a managed lookup for the name */
        if (!enabled) {
                struct ast_hostent ahp;
                struct hostent *hp;

                if ((hp = ast_gethostbyname(name, &ahp)))
                        memcpy(result, hp->h_addr, sizeof(result));
                /* flag value to indicate no manager was allocated */
                *dnsmgr = (typeof(*dnsmgr)) -1;
        } else {
                if (option_verbose > 2)
                        ast_verbose(VERBOSE_PREFIX_2 "adding manager for 
'%s'\n", name);
                *dnsmgr = ast_dnsmgr_get(name, result);
        }

        return !*dnsmgr;
}

static void *do_refresh(void *data)
{
        for (;;) {
                pthread_testcancel();
                usleep(ast_sched_wait(sched));
                pthread_testcancel();
                ast_sched_runq(sched);
        }
        return NULL;
}

static int refresh_list(void *data)
{
        struct refresh_info *info = data;
        struct ast_dnsmgr_entry *entry;
        struct ast_hostent ahp;
        struct hostent *hp;

        /* if a refresh or reload is already in progress, exit now */
        if (ast_mutex_trylock(&refresh_lock)) {
                if (info->verbose)
                        ast_log(LOG_WARNING, "DNS Manager refresh already in 
progress.\n");
                return -1;
        }

        if (option_verbose > 2)
                ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
        AST_LIST_LOCK(info->entries);
        AST_LIST_TRAVERSE(info->entries, entry, list) {
                if (info->filter.used && regexec(&info->filter, entry->name, 0, 
NULL, 0))
                    continue;

                if (info->verbose && (option_verbose > 2))
                        ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", 
entry->name);

                if ((hp = ast_gethostbyname(entry->name, &ahp))) {
                        /* check to see if it has changed, do callback if 
requested */
                        memcpy(entry->result, hp->h_addr, 
sizeof(entry->result));
                }
        }
        AST_LIST_UNLOCK(info->entries);

        ast_mutex_unlock(&refresh_lock);

        /* automatically reschedule */
        return -1;
}

static int do_reload(int loading);

static int handle_cli_reload(int fd, int argc, char *argv[])
{
        if (argc > 2)
                return RESULT_SHOWUSAGE;

        do_reload(0);
        return 0;
}

static int handle_cli_refresh(int fd, int argc, char *argv[])
{
        struct refresh_info info = {
                .entries = &entry_list,
                .verbose = 1,
        };

        if (argc > 3)
                return RESULT_SHOWUSAGE;

        if (argc == 3) {
                if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
                        return RESULT_SHOWUSAGE;
        }

        refresh_list(&info);

        if (info.filter.used)
                regfree(&info.filter);

        return 0;
}

static int handle_cli_status(int fd, int argc, char *argv[])
{
        int count = 0;
        struct ast_dnsmgr_entry *entry;

        if (argc > 2)
                return RESULT_SHOWUSAGE;

        ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
        ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
        AST_LIST_LOCK(&entry_list);
        AST_LIST_TRAVERSE(&entry_list, entry, list)
                count++;
        AST_LIST_UNLOCK(&entry_list);
        ast_cli(fd, "Number of entries: %d\n", count);

        return 0;
}

static struct ast_cli_entry cli_reload = {
        .cmda = { "dnsmgr", "reload", NULL },
        .handler = handle_cli_reload,
        .summary = "Reloads the DNS manager configuration",
        .usage = 
        "Usage: dnsmgr reload\n"
        "       Reloads the DNS manager configuration.\n"
};

static struct ast_cli_entry cli_refresh = {
        .cmda = { "dnsmgr", "refresh", NULL },
        .handler = handle_cli_refresh,
        .summary = "Performs an immediate refresh",
        .usage = 
        "Usage: dnsmgr refresh [pattern]\n"
        "       Peforms an immediate refresh of the managed DNS entries.\n"
        "       Optional regular expression pattern is used to filter the 
entries to refresh.\n",
};

static struct ast_cli_entry cli_status = {
        .cmda = { "dnsmgr", "status", NULL },
        .handler = handle_cli_status,
        .summary = "Display the DNS manager status",
        .usage =
        "Usage: dnsmgr status\n"
        "       Displays the DNS manager status.\n"
};

int dnsmgr_init(void)
{
        sched = sched_context_create();
        if (!sched) {
                ast_log(LOG_ERROR, "Unable to create schedule context.\n");
                return -1;
        }
        AST_LIST_HEAD_INIT(&entry_list);
        ast_cli_register(&cli_reload);
        ast_cli_register(&cli_status);
        return do_reload(1);
}

void dnsmgr_reload(void)
{
        do_reload(0);
}

static int do_reload(int loading)
{
        struct ast_config *config;
        const char *interval_value;
        const char *enabled_value;
        int interval;
        int was_enabled;
        pthread_attr_t attr;
        int res = -1;

        /* ensure that no refresh cycles run while the reload is in progress */
        ast_mutex_lock(&refresh_lock);

        /* reset defaults in preparation for reading config file */
        refresh_interval = REFRESH_DEFAULT;
        was_enabled = enabled;
        enabled = 0;

        if (refresh_sched > -1)
                ast_sched_del(sched, refresh_sched);

        if ((config = ast_config_load("dnsmgr.conf"))) {
                if ((enabled_value = ast_variable_retrieve(config, "general", 
"enabled"))) {
                        enabled = ast_true(enabled_value);
                }
                if ((interval_value = ast_variable_retrieve(config, "general", 
"refreshinterval"))) {
                        if (sscanf(interval_value, "%d", &interval) < 1)
                                ast_log(LOG_WARNING, "Unable to convert '%s' to 
a numeric value.\n", interval_value);
                        else if (interval < 0)
                                ast_log(LOG_WARNING, "Invalid refresh interval 
'%d' specified, using default\n", interval);
                        else
                                refresh_interval = interval;
                }
                ast_config_destroy(config);
        }

        if (enabled && refresh_interval) {
                refresh_sched = ast_sched_add(sched, refresh_interval * 1000, 
refresh_list, &master_refresh_info);
                ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed 
every %d seconds.\n", refresh_interval);
        }

        /* if this reload enabled the manager, create the background thread
           if it does not exist */
        if (enabled && !was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
                pthread_attr_init(&attr);
                pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
                if (ast_pthread_create(&refresh_thread, &attr, do_refresh, 
NULL) < 0) {
                        ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
                        ast_sched_del(sched, refresh_sched);
                }
                else {
                        ast_cli_register(&cli_refresh);
                        res = 0;
                }
        }
        /* if this reload disabled the manager and there is a background thread,
           kill it */
        else if (!enabled && was_enabled && (refresh_thread != 
AST_PTHREADT_NULL)) {
                /* wake up the thread so it will exit */
                pthread_cancel(refresh_thread);
                pthread_kill(refresh_thread, SIGURG);
                pthread_join(refresh_thread, NULL);
                refresh_thread = AST_PTHREADT_NULL;
                ast_cli_unregister(&cli_refresh);
                res = 0;
        }
        else
                res = 0;

        ast_mutex_unlock(&refresh_lock);

        return res;
}

_______________________________________________
Asterisk-Cvs mailing list
[email protected]
http://lists.digium.com/mailman/listinfo/asterisk-cvs

Reply via email to