Thu Jul 9 13:40:33 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 - 2007, 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  *
00024  * \author Mark Spencer <markster@digium.com> 
00025  *
00026  * \arg \ref AstExtState
00027  */
00028 
00029 /*! \page AstExtState Extension and device states in Asterisk
00030  *
00031  * Asterisk has an internal system that reports states
00032  * for an extension. By using the dialplan priority -1,
00033  * also called a \b hint, a connection can be made from an
00034  * extension to one or many devices. The state of the extension
00035  * now depends on the combined state of the devices.
00036  *
00037  * The device state is basically based on the current calls.
00038  * If the devicestate engine can find a call from or to the
00039  * device, it's in use.
00040  * 
00041  * Some channel drivers implement a callback function for 
00042  * a better level of reporting device states. The SIP channel
00043  * has a complicated system for this, which is improved 
00044  * by adding call limits to the configuration.
00045  * 
00046  * Functions that want to check the status of an extension
00047  * register themself as a \b watcher.
00048  * Watchers in this system can subscribe either to all extensions
00049  * or just a specific extensions.
00050  *
00051  * For non-device related states, there's an API called
00052  * devicestate providers. This is an extendible system for
00053  * delivering state information from outside sources or
00054  * functions within Asterisk. Currently we have providers
00055  * for app_meetme.c - the conference bridge - and call
00056  * parking (metermaids).
00057  *
00058  * There are manly three subscribers to extension states 
00059  * within Asterisk:
00060  * - AMI, the manager interface
00061  * - app_queue.c - the Queue dialplan application
00062  * - SIP subscriptions, a.k.a. "blinking lamps" or 
00063  *   "buddy lists"
00064  *
00065  * The CLI command "show hints" show last known state
00066  *
00067  * \note None of these handle user states, like an IM presence
00068  * system. res_jabber.c can subscribe and watch such states
00069  * in jabber/xmpp based systems.
00070  *
00071  * \section AstDevStateArch Architecture for devicestates
00072  *
00073  * When a channel driver or asterisk app changes state for 
00074  * a watched object, it alerts the core. The core queues
00075  * a change. When the change is processed, there's a query
00076  * sent to the channel driver/provider if there's a function
00077  * to handle that, otherwise a channel walk is issued to find
00078  * a channel that involves the object.
00079  * 
00080  * The changes are queued and processed by a separate thread.
00081  * This thread calls the watchers subscribing to status 
00082  * changes for the object. For manager, this results 
00083  * in events. For SIP, NOTIFY requests.
00084  *
00085  * - Device states
00086  *    \arg \ref devicestate.c 
00087  *    \arg \ref devicestate.h 
00088  *
00089  * \section AstExtStateArch Architecture for extension states
00090  * 
00091  * Hints are connected to extension. If an extension changes state
00092  * it checks the hint devices. If there is a hint, the callbacks into
00093  * device states are checked. The aggregated state is set for the hint
00094  * and reported back.
00095  *
00096  * - Extension states
00097  *    \arg \ref AstENUM ast_extension_states
00098  *    \arg \ref pbx.c 
00099  *    \arg \ref pbx.h 
00100  * - Structures
00101  *    - \ref ast_state_cb struct.  Callbacks for watchers
00102  *    - Callback ast_state_cb_type
00103  *    - \ref ast_hint struct.
00104  *    - Functions
00105  *    - ast_extension_state_add()
00106  *    - ast_extension_state_del()
00107  *    - ast_get_hint()
00108  * 
00109  */
00110 
00111 #include "asterisk.h"
00112 
00113 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 133947 $")
00114 
00115 #include "asterisk/_private.h"
00116 #include "asterisk/channel.h"
00117 #include "asterisk/utils.h"
00118 #include "asterisk/lock.h"
00119 #include "asterisk/linkedlists.h"
00120 #include "asterisk/devicestate.h"
00121 #include "asterisk/pbx.h"
00122 #include "asterisk/app.h"
00123 #include "asterisk/event.h"
00124 
00125 /*! \brief Device state strings for printing */
00126 static const char *devstatestring[] = {
00127    /* 0 AST_DEVICE_UNKNOWN */ "Unknown",  /*!< Valid, but unknown state */
00128    /* 1 AST_DEVICE_NOT_INUSE */  "Not in use",  /*!< Not used */
00129    /* 2 AST_DEVICE IN USE */  "In use",   /*!< In use */
00130    /* 3 AST_DEVICE_BUSY */    "Busy",     /*!< Busy */
00131    /* 4 AST_DEVICE_INVALID */ "Invalid",  /*!< Invalid - not known to Asterisk */
00132    /* 5 AST_DEVICE_UNAVAILABLE */   "Unavailable", /*!< Unavailable (not registred) */
00133    /* 6 AST_DEVICE_RINGING */ "Ringing",  /*!< Ring, ring, ring */
00134    /* 7 AST_DEVICE_RINGINUSE */  "Ring+Inuse",  /*!< Ring and in use */
00135    /* 8 AST_DEVICE_ONHOLD */  "On Hold"   /*!< On Hold */
00136 };
00137 
00138 /*! \brief  A device state provider (not a channel) */
00139 struct devstate_prov {
00140    char label[40];
00141    ast_devstate_prov_cb_type callback;
00142    AST_RWLIST_ENTRY(devstate_prov) list;
00143 };
00144 
00145 /*! \brief A list of providers */
00146 static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
00147 
00148 struct state_change {
00149    AST_LIST_ENTRY(state_change) list;
00150    char device[1];
00151 };
00152 
00153 /*! \brief The state change queue. State changes are queued
00154    for processing by a separate thread */
00155 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00156 
00157 /*! \brief The device state change notification thread */
00158 static pthread_t change_thread = AST_PTHREADT_NULL;
00159 
00160 /*! \brief Flag for the queue */
00161 static ast_cond_t change_pending;
00162 
00163 /*! \brief Whether or not to cache this device state value */
00164 enum devstate_cache {
00165    /*! Cache this value as it is coming from a device state provider which is
00166     *  pushing up state change events to us as they happen */
00167    CACHE_ON,
00168    /*! Don't cache this result, since it was pulled from the device state provider.
00169     *  We only want to cache results from device state providers that are being nice
00170     *  and pushing state change events up to us as they happen. */
00171    CACHE_OFF,
00172 };
00173 
00174 /* Forward declarations */
00175 static int getproviderstate(const char *provider, const char *address);
00176 
00177 /*! \brief Find devicestate as text message for output */
00178 const char *devstate2str(enum ast_device_state devstate) 
00179 {
00180    return devstatestring[devstate];
00181 }
00182 
00183 const char *ast_devstate_str(enum ast_device_state state)
00184 {
00185    const char *res = "UNKNOWN";
00186 
00187    switch (state) {
00188    case AST_DEVICE_UNKNOWN:
00189       break;
00190    case AST_DEVICE_NOT_INUSE:
00191       res = "NOT_INUSE";
00192       break;
00193    case AST_DEVICE_INUSE:
00194       res = "INUSE";
00195       break;
00196    case AST_DEVICE_BUSY:
00197       res = "BUSY";
00198       break;
00199    case AST_DEVICE_INVALID:
00200       res = "INVALID";
00201       break;
00202    case AST_DEVICE_UNAVAILABLE:
00203       res = "UNAVAILABLE";
00204       break;
00205    case AST_DEVICE_RINGING:
00206       res = "RINGING";
00207       break;
00208    case AST_DEVICE_RINGINUSE:
00209       res = "RINGINUSE";
00210       break;
00211    case AST_DEVICE_ONHOLD:
00212       res = "ONHOLD";
00213       break;
00214    }
00215 
00216    return res;
00217 }
00218 
00219 enum ast_device_state ast_devstate_val(const char *val)
00220 {
00221    if (!strcasecmp(val, "NOT_INUSE"))
00222       return AST_DEVICE_NOT_INUSE;
00223    else if (!strcasecmp(val, "INUSE"))
00224       return AST_DEVICE_INUSE;
00225    else if (!strcasecmp(val, "BUSY"))
00226       return AST_DEVICE_BUSY;
00227    else if (!strcasecmp(val, "INVALID"))
00228       return AST_DEVICE_INVALID;
00229    else if (!strcasecmp(val, "UNAVAILABLE"))
00230       return AST_DEVICE_UNAVAILABLE;
00231    else if (!strcasecmp(val, "RINGING"))
00232       return AST_DEVICE_RINGING;
00233    else if (!strcasecmp(val, "RINGINUSE"))
00234       return AST_DEVICE_RINGINUSE;
00235    else if (!strcasecmp(val, "ONHOLD"))
00236       return AST_DEVICE_ONHOLD;
00237 
00238    return AST_DEVICE_UNKNOWN;
00239 }
00240 
00241 /*! \brief Find out if device is active in a call or not 
00242    \note find channels with the device's name in it
00243    This function is only used for channels that does not implement 
00244    devicestate natively
00245 */
00246 enum ast_device_state ast_parse_device_state(const char *device)
00247 {
00248    struct ast_channel *chan;
00249    char match[AST_CHANNEL_NAME];
00250    enum ast_device_state res;
00251 
00252    ast_copy_string(match, device, sizeof(match)-1);
00253    strcat(match, "-");
00254    chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
00255 
00256    if (!chan)
00257       return AST_DEVICE_UNKNOWN;
00258 
00259    if (chan->_state == AST_STATE_RINGING)
00260       res = AST_DEVICE_RINGING;
00261    else
00262       res = AST_DEVICE_INUSE;
00263    
00264    ast_channel_unlock(chan);
00265 
00266    return res;
00267 }
00268 
00269 static enum ast_device_state devstate_cached(const char *device)
00270 {
00271    enum ast_device_state res = AST_DEVICE_UNKNOWN;
00272    struct ast_event *event;
00273 
00274    event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
00275       AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00276       AST_EVENT_IE_END);
00277 
00278    if (!event)
00279       return res;
00280 
00281    res = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00282 
00283    ast_event_destroy(event);
00284 
00285    return res;
00286 }
00287 
00288 /*! \brief Check device state through channel specific function or generic function */
00289 static enum ast_device_state _ast_device_state(const char *device, int check_cache)
00290 {
00291    char *buf;
00292    char *number;
00293    const struct ast_channel_tech *chan_tech;
00294    enum ast_device_state res;
00295    /*! \brief Channel driver that provides device state */
00296    char *tech;
00297    /*! \brief Another provider of device state */
00298    char *provider = NULL;
00299 
00300    /* If the last known state is cached, just return that */
00301    if (check_cache) {
00302       res = devstate_cached(device);
00303       if (res != AST_DEVICE_UNKNOWN) {
00304          return res;
00305       }
00306    }
00307 
00308    buf = ast_strdupa(device);
00309    tech = strsep(&buf, "/");
00310    if (!(number = buf)) {
00311       if (!(provider = strsep(&tech, ":")))
00312          return AST_DEVICE_INVALID;
00313       /* We have a provider */
00314       number = tech;
00315       tech = NULL;
00316    }
00317 
00318    if (provider)  {
00319       ast_debug(3, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
00320       return getproviderstate(provider, number);
00321    }
00322 
00323    ast_debug(4, "No provider found, checking channel drivers for %s - %s\n", tech, number);
00324 
00325    if (!(chan_tech = ast_get_channel_tech(tech)))
00326       return AST_DEVICE_INVALID;
00327 
00328    if (!(chan_tech->devicestate)) /* Does the channel driver support device state notification? */
00329       return ast_parse_device_state(device); /* No, try the generic function */
00330 
00331    res = chan_tech->devicestate(number);
00332 
00333    if (res != AST_DEVICE_UNKNOWN)
00334       return res;
00335 
00336    res = ast_parse_device_state(device);
00337 
00338    if (res == AST_DEVICE_UNKNOWN)
00339       return AST_DEVICE_NOT_INUSE;
00340 
00341    return res;
00342 }
00343 
00344 enum ast_device_state ast_device_state(const char *device)
00345 {
00346    /* This function is called from elsewhere in the code to find out the
00347     * current state of a device.  Check the cache, first. */
00348 
00349    return _ast_device_state(device, 1);
00350 }
00351 
00352 /*! \brief Add device state provider */
00353 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
00354 {
00355    struct devstate_prov *devprov;
00356 
00357    if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
00358       return -1;
00359 
00360    devprov->callback = callback;
00361    ast_copy_string(devprov->label, label, sizeof(devprov->label));
00362 
00363    AST_RWLIST_WRLOCK(&devstate_provs);
00364    AST_RWLIST_INSERT_HEAD(&devstate_provs, devprov, list);
00365    AST_RWLIST_UNLOCK(&devstate_provs);
00366 
00367    return 0;
00368 }
00369 
00370 /*! \brief Remove device state provider */
00371 int ast_devstate_prov_del(const char *label)
00372 {
00373    struct devstate_prov *devcb;
00374    int res = -1;
00375 
00376    AST_RWLIST_WRLOCK(&devstate_provs);
00377    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
00378       if (!strcasecmp(devcb->label, label)) {
00379          AST_RWLIST_REMOVE_CURRENT(list);
00380          ast_free(devcb);
00381          res = 0;
00382          break;
00383       }
00384    }
00385    AST_RWLIST_TRAVERSE_SAFE_END;
00386    AST_RWLIST_UNLOCK(&devstate_provs);
00387 
00388    return res;
00389 }
00390 
00391 /*! \brief Get provider device state */
00392 static int getproviderstate(const char *provider, const char *address)
00393 {
00394    struct devstate_prov *devprov;
00395    int res = AST_DEVICE_INVALID;
00396 
00397 
00398    AST_RWLIST_RDLOCK(&devstate_provs);
00399    AST_RWLIST_TRAVERSE(&devstate_provs, devprov, list) {
00400       ast_debug(5, "Checking provider %s with %s\n", devprov->label, provider);
00401 
00402       if (!strcasecmp(devprov->label, provider)) {
00403          res = devprov->callback(address);
00404          break;
00405       }
00406    }
00407    AST_RWLIST_UNLOCK(&devstate_provs);
00408    return res;
00409 }
00410 
00411 static void devstate_event(const char *device, enum ast_device_state state, enum devstate_cache cache)
00412 {
00413    struct ast_event *event;
00414 
00415    if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE,
00416          AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00417          AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
00418          AST_EVENT_IE_END))) {
00419       return;
00420    }
00421 
00422    if (cache == CACHE_ON) {
00423       /* Cache this event, replacing an event in the cache with the same
00424        * device name if it exists. */
00425       ast_event_queue_and_cache(event,
00426          AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
00427          AST_EVENT_IE_END);
00428    } else {
00429       ast_event_queue(event);
00430    }
00431 }
00432 
00433 /*! Called by the state change thread to find out what the state is, and then
00434  *  to queue up the state change event */
00435 static void do_state_change(const char *device)
00436 {
00437    enum ast_device_state state;
00438 
00439    state = _ast_device_state(device, 0);
00440 
00441    ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
00442 
00443    devstate_event(device, state, CACHE_OFF);
00444 }
00445 
00446 int ast_devstate_changed_literal(enum ast_device_state state, const char *device)
00447 {
00448    struct state_change *change;
00449 
00450    ast_debug(3, "Notification of state change to be queued on device/channel %s\n", device);
00451 
00452    if (state != AST_DEVICE_UNKNOWN) {
00453       devstate_event(device, state, CACHE_ON);
00454    } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00455       /* we could not allocate a change struct, or */
00456       /* there is no background thread, so process the change now */
00457       do_state_change(device);
00458    } else {
00459       /* queue the change */
00460       strcpy(change->device, device);
00461       AST_LIST_LOCK(&state_changes);
00462       AST_LIST_INSERT_TAIL(&state_changes, change, list);
00463       ast_cond_signal(&change_pending);
00464       AST_LIST_UNLOCK(&state_changes);
00465    }
00466 
00467    return 1;
00468 }
00469 
00470 int ast_device_state_changed_literal(const char *dev)
00471 {
00472    return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, dev);
00473 }
00474 
00475 int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) 
00476 {
00477    char buf[AST_MAX_EXTENSION];
00478    va_list ap;
00479 
00480    va_start(ap, fmt);
00481    vsnprintf(buf, sizeof(buf), fmt, ap);
00482    va_end(ap);
00483 
00484    return ast_devstate_changed_literal(state, buf);
00485 }
00486 
00487 /*! \brief Accept change notification, add it to change queue */
00488 int ast_device_state_changed(const char *fmt, ...) 
00489 {
00490    char buf[AST_MAX_EXTENSION];
00491    va_list ap;
00492 
00493    va_start(ap, fmt);
00494    vsnprintf(buf, sizeof(buf), fmt, ap);
00495    va_end(ap);
00496 
00497    return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf);
00498 }
00499 
00500 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
00501 static void *do_devstate_changes(void *data)
00502 {
00503    struct state_change *next, *current;
00504 
00505    for (;;) {
00506       /* This basically pops off any state change entries, resets the list back to NULL, unlocks, and processes each state change */
00507       AST_LIST_LOCK(&state_changes);
00508       if (AST_LIST_EMPTY(&state_changes))
00509          ast_cond_wait(&change_pending, &state_changes.lock);
00510       next = AST_LIST_FIRST(&state_changes);
00511       AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
00512       AST_LIST_UNLOCK(&state_changes);
00513 
00514       /* Process each state change */
00515       while ((current = next)) {
00516          next = AST_LIST_NEXT(current, list);
00517          do_state_change(current->device);
00518          ast_free(current);
00519       }
00520    }
00521 
00522    return NULL;
00523 }
00524 
00525 /*! \brief Initialize the device state engine in separate thread */
00526 int ast_device_state_engine_init(void)
00527 {
00528    ast_cond_init(&change_pending, NULL);
00529    if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00530       ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00531       return -1;
00532    }
00533 
00534    return 0;
00535 }

Generated on Thu Jul 9 13:40:33 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7