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