Thu Oct 8 21:57:25 2009

Asterisk developer's documentation


devicestate.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@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 Device state management
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 96644 $")
00029 
00030 #include <sys/types.h>
00031 #include <unistd.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <stdio.h>
00035 
00036 #include "asterisk/channel.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/lock.h"
00039 #include "asterisk/linkedlists.h"
00040 #include "asterisk/logger.h"
00041 #include "asterisk/devicestate.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/options.h"
00045 
00046 /*! \brief Device state strings for printing */
00047 static const char *devstatestring[] = {
00048    /* 0 AST_DEVICE_UNKNOWN */ "Unknown",  /*!< Valid, but unknown state */
00049    /* 1 AST_DEVICE_NOT_INUSE */  "Not in use",  /*!< Not used */
00050    /* 2 AST_DEVICE IN USE */  "In use",   /*!< In use */
00051    /* 3 AST_DEVICE_BUSY */    "Busy",     /*!< Busy */
00052    /* 4 AST_DEVICE_INVALID */ "Invalid",  /*!< Invalid - not known to Asterisk */
00053    /* 5 AST_DEVICE_UNAVAILABLE */   "Unavailable", /*!< Unavailable (not registred) */
00054    /* 6 AST_DEVICE_RINGING */ "Ringing",  /*!< Ring, ring, ring */
00055    /* 7 AST_DEVICE_RINGINUSE */  "Ring+Inuse",  /*!< Ring and in use */
00056    /* 8 AST_DEVICE_ONHOLD */  "On Hold"   /*!< On Hold */
00057 };
00058 
00059 /*! \brief  A device state provider (not a channel) */
00060 struct devstate_prov {
00061    char label[40];
00062    ast_devstate_prov_cb_type callback;
00063    AST_LIST_ENTRY(devstate_prov) list;
00064 };
00065 
00066 /*! \brief A list of providers */
00067 static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov);
00068 
00069 /*! \brief  A device state watcher (callback) */
00070 struct devstate_cb {
00071    void *data;
00072    ast_devstate_cb_type callback;
00073    AST_LIST_ENTRY(devstate_cb) list;
00074 };
00075 
00076 /*! \brief A device state watcher list */
00077 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
00078 
00079 struct state_change {
00080    AST_LIST_ENTRY(state_change) list;
00081    char cid_num[AST_MAX_EXTENSION];
00082    char cid_name[AST_MAX_EXTENSION];
00083    char device[1];
00084 };
00085 
00086 /*! \brief The state change queue. State changes are queued
00087    for processing by a separate thread */
00088 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00089 
00090 /*! \brief The device state change notification thread */
00091 static pthread_t change_thread = AST_PTHREADT_NULL;
00092 
00093 /*! \brief Flag for the queue */
00094 static ast_cond_t change_pending;
00095 
00096 /* Forward declarations */
00097 static int getproviderstate(const char *provider, const char *address);
00098 
00099 /*! \brief Find devicestate as text message for output */
00100 const char *devstate2str(int devstate) 
00101 {
00102    return devstatestring[devstate];
00103 }
00104 
00105 /*! \brief Find out if device is active in a call or not 
00106    \note find channels with the device's name in it
00107    This function is only used for channels that does not implement 
00108    devicestate natively
00109 */
00110 int ast_parse_device_state(const char *device)
00111 {
00112    struct ast_channel *chan;
00113    char match[AST_CHANNEL_NAME];
00114    int res;
00115 
00116    ast_copy_string(match, device, sizeof(match)-1);
00117    strcat(match, "-");
00118    chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
00119 
00120    if (!chan)
00121       return AST_DEVICE_UNKNOWN;
00122 
00123    if (chan->_state == AST_STATE_RINGING)
00124       res = AST_DEVICE_RINGING;
00125    else
00126       res = AST_DEVICE_INUSE;
00127    
00128    ast_channel_unlock(chan);
00129 
00130    return res;
00131 }
00132 
00133 /*! \brief Check device state through channel specific function or generic function */
00134 int ast_device_state(const char *device)
00135 {
00136    char *buf;
00137    char *number;
00138    const struct ast_channel_tech *chan_tech;
00139    int res = 0;
00140    /*! \brief Channel driver that provides device state */
00141    char *tech;
00142    /*! \brief Another provider of device state */
00143    char *provider = NULL;
00144    
00145    buf = ast_strdupa(device);
00146    tech = strsep(&buf, "/");
00147    number = buf;
00148    if (!number) {
00149       provider = strsep(&tech, ":");
00150       if (!provider)
00151          return AST_DEVICE_INVALID;
00152       /* We have a provider */
00153       number = tech;
00154       tech = NULL;
00155    }
00156 
00157    if (provider)  {
00158       if(option_debug > 2)
00159          ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
00160       return getproviderstate(provider, number);
00161    }
00162    if (option_debug > 3)
00163       ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
00164 
00165    chan_tech = ast_get_channel_tech(tech);
00166    if (!chan_tech)
00167       return AST_DEVICE_INVALID;
00168 
00169    if (!chan_tech->devicestate)  /* Does the channel driver support device state notification? */
00170       return ast_parse_device_state(device); /* No, try the generic function */
00171    else {
00172       res = chan_tech->devicestate(number);  /* Ask the channel driver for device state */
00173       if (res == AST_DEVICE_UNKNOWN) {
00174          res = ast_parse_device_state(device);
00175          /* at this point we know the device exists, but the channel driver
00176             could not give us a state; if there is no channel state available,
00177             it must be 'not in use'
00178          */
00179          if (res == AST_DEVICE_UNKNOWN)
00180             res = AST_DEVICE_NOT_INUSE;
00181          return res;
00182       } else
00183          return res;
00184    }
00185 }
00186 
00187 /*! \brief Add device state provider */
00188 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
00189 {
00190    struct devstate_prov *devprov;
00191 
00192    if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
00193       return -1;
00194 
00195    devprov->callback = callback;
00196    ast_copy_string(devprov->label, label, sizeof(devprov->label));
00197 
00198    AST_LIST_LOCK(&devstate_provs);
00199    AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
00200    AST_LIST_UNLOCK(&devstate_provs);
00201 
00202    return 0;
00203 }
00204 
00205 /*! \brief Remove device state provider */
00206 void ast_devstate_prov_del(const char *label)
00207 {
00208    struct devstate_prov *devcb;
00209 
00210    AST_LIST_LOCK(&devstate_provs);
00211    AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
00212       if (!strcasecmp(devcb->label, label)) {
00213          AST_LIST_REMOVE_CURRENT(&devstate_provs, list);
00214          free(devcb);
00215          break;
00216       }
00217    }
00218    AST_LIST_TRAVERSE_SAFE_END;
00219    AST_LIST_UNLOCK(&devstate_provs);
00220 }
00221 
00222 /*! \brief Get provider device state */
00223 static int getproviderstate(const char *provider, const char *address)
00224 {
00225    struct devstate_prov *devprov;
00226    int res = AST_DEVICE_INVALID;
00227 
00228 
00229    AST_LIST_LOCK(&devstate_provs);
00230    AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) {
00231       if(option_debug > 4)
00232          ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
00233 
00234       if (!strcasecmp(devprov->label, provider)) {
00235          res = devprov->callback(address);
00236          break;
00237       }
00238    }
00239    AST_LIST_TRAVERSE_SAFE_END;
00240    AST_LIST_UNLOCK(&devstate_provs);
00241    return res;
00242 }
00243 
00244 /*! \brief Add device state watcher */
00245 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
00246 {
00247    struct devstate_cb *devcb;
00248 
00249    if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
00250       return -1;
00251 
00252    devcb->data = data;
00253    devcb->callback = callback;
00254 
00255    AST_LIST_LOCK(&devstate_cbs);
00256    AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
00257    AST_LIST_UNLOCK(&devstate_cbs);
00258 
00259    return 0;
00260 }
00261 
00262 /*! \brief Remove device state watcher */
00263 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
00264 {
00265    struct devstate_cb *devcb;
00266 
00267    AST_LIST_LOCK(&devstate_cbs);
00268    AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
00269       if ((devcb->callback == callback) && (devcb->data == data)) {
00270          AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
00271          free(devcb);
00272          break;
00273       }
00274    }
00275    AST_LIST_TRAVERSE_SAFE_END;
00276    AST_LIST_UNLOCK(&devstate_cbs);
00277 }
00278 
00279 /*! \brief Notify callback watchers of change, and notify PBX core for hint updates
00280    Normally executed within a separate thread
00281 */
00282 static void do_state_change(const char *device, char *cid_num, char *cid_name)
00283 {
00284    int state;
00285    struct devstate_cb *devcb;
00286 
00287    state = ast_device_state(device);
00288    if (option_debug > 2)
00289       ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
00290 
00291    AST_LIST_LOCK(&devstate_cbs);
00292    AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
00293       devcb->callback(device, state, devcb->data, cid_num, cid_name);
00294    AST_LIST_UNLOCK(&devstate_cbs);
00295 
00296    ast_hint_state_changed(device, cid_num, cid_name);
00297 }
00298 
00299 static int __ast_device_state_changed_literal(char *buf, int norecurse, char *cid_num, char *cid_name)
00300 {
00301    char *device;
00302    struct state_change *change;
00303    char *tmp = NULL;
00304 
00305    if (option_debug > 2)
00306       ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
00307 
00308    device = buf;
00309    
00310    if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00311       /* we could not allocate a change struct, or */
00312       /* there is no background thread, so process the change now */
00313       do_state_change(device, cid_num, cid_name);
00314    } else {
00315       /* queue the change */
00316       strcpy(change->device, device);
00317       if (cid_num && (!ast_strlen_zero(cid_num))) {
00318           strncpy(change->cid_num, cid_num, sizeof(change->cid_num) - 1);
00319       }
00320       if (cid_name && (!ast_strlen_zero(cid_name))) {
00321           strncpy(change->cid_name, cid_name, sizeof(change->cid_name) - 1);
00322       }
00323       AST_LIST_LOCK(&state_changes);
00324       AST_LIST_INSERT_TAIL(&state_changes, change, list);
00325       if (AST_LIST_FIRST(&state_changes) == change)
00326          /* the list was empty, signal the thread */
00327          ast_cond_signal(&change_pending);
00328       AST_LIST_UNLOCK(&state_changes);
00329    }
00330 
00331    /* The problem with this API is that a device may be called with the unique
00332     * identifier appended or not, but it's separated from the channel name
00333     * with a '-', which is also a legitimate character in a channel name.  So,
00334     * we have to force both names to get their names checked for state changes
00335     * to ensure that the right one gets notified.  Not a huge performance hit,
00336     * but it might could be fixed by an enterprising programmer in trunk.
00337     */
00338    if (!norecurse && (tmp = strrchr(device, '-'))) {
00339       *tmp = '\0';
00340       __ast_device_state_changed_literal(device, 1, cid_num, cid_name);
00341    }
00342    
00343    return 1;
00344 }
00345 
00346 int ast_device_state_changed_literal(const char *dev, const char *cid_num, const char *cid_name)
00347 {
00348    char *buf;
00349    char *buf2 = NULL;
00350    char *buf3 = NULL;
00351    buf = ast_strdupa(dev);
00352    if (cid_num)
00353        buf2 = ast_strdupa(cid_num);
00354    if (cid_name)
00355        buf3 = ast_strdupa(cid_name);
00356    return __ast_device_state_changed_literal(buf, 0, buf2, buf3);
00357 }
00358 
00359 /*! \brief Accept change notification, add it to change queue */
00360 int ast_device_state_changed(const char *fmt, ...) 
00361 {
00362    char buf[AST_MAX_EXTENSION];
00363    va_list ap;
00364 
00365    va_start(ap, fmt);
00366    vsnprintf(buf, sizeof(buf), fmt, ap);
00367    va_end(ap);
00368    return __ast_device_state_changed_literal(buf, 0, NULL, NULL);
00369 }
00370 
00371 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
00372 static void *do_devstate_changes(void *data)
00373 {
00374    struct state_change *cur;
00375 
00376    AST_LIST_LOCK(&state_changes);
00377    for(;;) {
00378       /* the list lock will _always_ be held at this point in the loop */
00379       cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
00380       if (cur) {
00381          /* we got an entry, so unlock the list while we process it */
00382          AST_LIST_UNLOCK(&state_changes);
00383          do_state_change(cur->device, cur->cid_num, cur->cid_name);
00384          free(cur);
00385          AST_LIST_LOCK(&state_changes);
00386       } else {
00387          /* there was no entry, so atomically unlock the list and wait for
00388             the condition to be signalled (returns with the lock held) */
00389          ast_cond_wait(&change_pending, &state_changes.lock);
00390       }
00391    }
00392 
00393    return NULL;
00394 }
00395 
00396 /*! \brief Initialize the device state engine in separate thread */
00397 int ast_device_state_engine_init(void)
00398 {
00399    ast_cond_init(&change_pending, NULL);
00400    if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00401       ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00402       return -1;
00403    }
00404 
00405    return 0;
00406 }

Generated on Thu Oct 8 21:57:25 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.8