00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
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
00126 static const char *devstatestring[] = {
00127 "Unknown",
00128 "Not in use",
00129 "In use",
00130 "Busy",
00131 "Invalid",
00132 "Unavailable",
00133 "Ringing",
00134 "Ring+Inuse",
00135 "On Hold"
00136 };
00137
00138
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
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
00154
00155 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00156
00157
00158 static pthread_t change_thread = AST_PTHREADT_NULL;
00159
00160
00161 static ast_cond_t change_pending;
00162
00163
00164 enum devstate_cache {
00165
00166
00167 CACHE_ON,
00168
00169
00170
00171 CACHE_OFF,
00172 };
00173
00174
00175 static int getproviderstate(const char *provider, const char *address);
00176
00177
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
00242
00243
00244
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
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
00296 char *tech;
00297
00298 char *provider = NULL;
00299
00300
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
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))
00329 return ast_parse_device_state(device);
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
00347
00348
00349 return _ast_device_state(device, 1);
00350 }
00351
00352
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
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
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
00424
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
00434
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
00456
00457 do_state_change(device);
00458 } else {
00459
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
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
00501 static void *do_devstate_changes(void *data)
00502 {
00503 struct state_change *next, *current;
00504
00505 for (;;) {
00506
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
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
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 }