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
00112
00113
00114
00115
00116
00117
00118
00119
00120 #include "asterisk.h"
00121
00122 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369001 $")
00123
00124 #include "asterisk/_private.h"
00125 #include "asterisk/channel.h"
00126 #include "asterisk/utils.h"
00127 #include "asterisk/lock.h"
00128 #include "asterisk/linkedlists.h"
00129 #include "asterisk/devicestate.h"
00130 #include "asterisk/pbx.h"
00131 #include "asterisk/app.h"
00132 #include "asterisk/event.h"
00133
00134
00135 static const char * const devstatestring[][2] = {
00136 { "Unknown", "UNKNOWN" },
00137 { "Not in use", "NOT_INUSE" },
00138 { "In use", "INUSE" },
00139 { "Busy", "BUSY" },
00140 { "Invalid", "INVALID" },
00141 { "Unavailable", "UNAVAILABLE" },
00142 { "Ringing", "RINGING" },
00143 { "Ring+Inuse", "RINGINUSE" },
00144 { "On Hold", "ONHOLD" },
00145 };
00146
00147
00148 static const struct chan2dev {
00149 enum ast_channel_state chan;
00150 enum ast_device_state dev;
00151 } chan2dev[] = {
00152 { AST_STATE_DOWN, AST_DEVICE_NOT_INUSE },
00153 { AST_STATE_RESERVED, AST_DEVICE_INUSE },
00154 { AST_STATE_OFFHOOK, AST_DEVICE_INUSE },
00155 { AST_STATE_DIALING, AST_DEVICE_INUSE },
00156 { AST_STATE_RING, AST_DEVICE_INUSE },
00157 { AST_STATE_RINGING, AST_DEVICE_RINGING },
00158 { AST_STATE_UP, AST_DEVICE_INUSE },
00159 { AST_STATE_BUSY, AST_DEVICE_BUSY },
00160 { AST_STATE_DIALING_OFFHOOK, AST_DEVICE_INUSE },
00161 { AST_STATE_PRERING, AST_DEVICE_RINGING },
00162 { -100, -100 },
00163 };
00164
00165
00166 struct devstate_prov {
00167 char label[40];
00168 ast_devstate_prov_cb_type callback;
00169 AST_RWLIST_ENTRY(devstate_prov) list;
00170 };
00171
00172
00173 static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
00174
00175 struct state_change {
00176 AST_LIST_ENTRY(state_change) list;
00177 char device[1];
00178 };
00179
00180
00181
00182 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00183
00184
00185 static pthread_t change_thread = AST_PTHREADT_NULL;
00186
00187
00188 static ast_cond_t change_pending;
00189
00190 struct devstate_change {
00191 AST_LIST_ENTRY(devstate_change) entry;
00192 uint32_t state;
00193 struct ast_eid eid;
00194 char device[1];
00195 };
00196
00197 static struct {
00198 pthread_t thread;
00199 struct ast_event_sub *event_sub;
00200 ast_cond_t cond;
00201 ast_mutex_t lock;
00202 AST_LIST_HEAD_NOLOCK(, devstate_change) devstate_change_q;
00203 unsigned int enabled:1;
00204 } devstate_collector = {
00205 .thread = AST_PTHREADT_NULL,
00206 .enabled = 0,
00207 };
00208
00209
00210 static int getproviderstate(const char *provider, const char *address);
00211
00212
00213 const char *ast_devstate2str(enum ast_device_state devstate)
00214 {
00215 return devstatestring[devstate][0];
00216 }
00217
00218
00219 const char *devstate2str(enum ast_device_state devstate)
00220 {
00221 return devstatestring[devstate][0];
00222 }
00223
00224 enum ast_device_state ast_state_chan2dev(enum ast_channel_state chanstate)
00225 {
00226 int i;
00227 chanstate &= 0xFFFF;
00228 for (i = 0; chan2dev[i].chan != -100; i++) {
00229 if (chan2dev[i].chan == chanstate) {
00230 return chan2dev[i].dev;
00231 }
00232 }
00233 return AST_DEVICE_UNKNOWN;
00234 }
00235
00236
00237 const char *ast_devstate_str(enum ast_device_state state)
00238 {
00239 return devstatestring[state][1];
00240 }
00241
00242 enum ast_device_state ast_devstate_val(const char *val)
00243 {
00244 if (!strcasecmp(val, "NOT_INUSE"))
00245 return AST_DEVICE_NOT_INUSE;
00246 else if (!strcasecmp(val, "INUSE"))
00247 return AST_DEVICE_INUSE;
00248 else if (!strcasecmp(val, "BUSY"))
00249 return AST_DEVICE_BUSY;
00250 else if (!strcasecmp(val, "INVALID"))
00251 return AST_DEVICE_INVALID;
00252 else if (!strcasecmp(val, "UNAVAILABLE"))
00253 return AST_DEVICE_UNAVAILABLE;
00254 else if (!strcasecmp(val, "RINGING"))
00255 return AST_DEVICE_RINGING;
00256 else if (!strcasecmp(val, "RINGINUSE"))
00257 return AST_DEVICE_RINGINUSE;
00258 else if (!strcasecmp(val, "ONHOLD"))
00259 return AST_DEVICE_ONHOLD;
00260
00261 return AST_DEVICE_UNKNOWN;
00262 }
00263
00264
00265
00266
00267
00268
00269 enum ast_device_state ast_parse_device_state(const char *device)
00270 {
00271 struct ast_channel *chan;
00272 char match[AST_CHANNEL_NAME];
00273 enum ast_device_state res;
00274
00275 snprintf(match, sizeof(match), "%s-", device);
00276
00277 if (!(chan = ast_channel_get_by_name_prefix(match, strlen(match)))) {
00278 return AST_DEVICE_UNKNOWN;
00279 }
00280
00281 res = (chan->_state == AST_STATE_RINGING) ? AST_DEVICE_RINGING : AST_DEVICE_INUSE;
00282
00283 chan = ast_channel_unref(chan);
00284
00285 return res;
00286 }
00287
00288 static enum ast_device_state devstate_cached(const char *device)
00289 {
00290 enum ast_device_state res = AST_DEVICE_UNKNOWN;
00291 struct ast_event *event;
00292
00293 event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
00294 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00295 AST_EVENT_IE_END);
00296
00297 if (!event)
00298 return res;
00299
00300 res = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00301
00302 ast_event_destroy(event);
00303
00304 return res;
00305 }
00306
00307
00308 static enum ast_device_state _ast_device_state(const char *device, int check_cache)
00309 {
00310 char *buf;
00311 char *number;
00312 const struct ast_channel_tech *chan_tech;
00313 enum ast_device_state res;
00314
00315 char *tech;
00316
00317 char *provider = NULL;
00318
00319
00320 if (check_cache) {
00321 res = devstate_cached(device);
00322 if (res != AST_DEVICE_UNKNOWN) {
00323 return res;
00324 }
00325 }
00326
00327 buf = ast_strdupa(device);
00328 tech = strsep(&buf, "/");
00329 if (!(number = buf)) {
00330 provider = strsep(&tech, ":");
00331 if (!tech) {
00332 return AST_DEVICE_INVALID;
00333 }
00334
00335 number = tech;
00336 tech = NULL;
00337
00338 ast_debug(3, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
00339 return getproviderstate(provider, number);
00340 }
00341
00342 ast_debug(4, "No provider found, checking channel drivers for %s - %s\n", tech, number);
00343
00344 if (!(chan_tech = ast_get_channel_tech(tech)))
00345 return AST_DEVICE_INVALID;
00346
00347 if (!(chan_tech->devicestate))
00348 return ast_parse_device_state(device);
00349
00350 res = chan_tech->devicestate(number);
00351
00352 if (res != AST_DEVICE_UNKNOWN)
00353 return res;
00354
00355 res = ast_parse_device_state(device);
00356
00357 return res;
00358 }
00359
00360 enum ast_device_state ast_device_state(const char *device)
00361 {
00362
00363
00364
00365 return _ast_device_state(device, 1);
00366 }
00367
00368
00369 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
00370 {
00371 struct devstate_prov *devprov;
00372
00373 if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
00374 return -1;
00375
00376 devprov->callback = callback;
00377 ast_copy_string(devprov->label, label, sizeof(devprov->label));
00378
00379 AST_RWLIST_WRLOCK(&devstate_provs);
00380 AST_RWLIST_INSERT_HEAD(&devstate_provs, devprov, list);
00381 AST_RWLIST_UNLOCK(&devstate_provs);
00382
00383 return 0;
00384 }
00385
00386
00387 int ast_devstate_prov_del(const char *label)
00388 {
00389 struct devstate_prov *devcb;
00390 int res = -1;
00391
00392 AST_RWLIST_WRLOCK(&devstate_provs);
00393 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
00394 if (!strcasecmp(devcb->label, label)) {
00395 AST_RWLIST_REMOVE_CURRENT(list);
00396 ast_free(devcb);
00397 res = 0;
00398 break;
00399 }
00400 }
00401 AST_RWLIST_TRAVERSE_SAFE_END;
00402 AST_RWLIST_UNLOCK(&devstate_provs);
00403
00404 return res;
00405 }
00406
00407
00408 static int getproviderstate(const char *provider, const char *address)
00409 {
00410 struct devstate_prov *devprov;
00411 int res = AST_DEVICE_INVALID;
00412
00413 AST_RWLIST_RDLOCK(&devstate_provs);
00414 AST_RWLIST_TRAVERSE(&devstate_provs, devprov, list) {
00415 ast_debug(5, "Checking provider %s with %s\n", devprov->label, provider);
00416
00417 if (!strcasecmp(devprov->label, provider)) {
00418 res = devprov->callback(address);
00419 break;
00420 }
00421 }
00422 AST_RWLIST_UNLOCK(&devstate_provs);
00423
00424 return res;
00425 }
00426
00427 static void devstate_event(const char *device, enum ast_device_state state)
00428 {
00429 struct ast_event *event;
00430 enum ast_event_type event_type;
00431
00432 if (devstate_collector.enabled) {
00433
00434
00435 event_type = AST_EVENT_DEVICE_STATE_CHANGE;
00436 } else {
00437 event_type = AST_EVENT_DEVICE_STATE;
00438 }
00439
00440 ast_debug(3, "device '%s' state '%d'\n", device, state);
00441
00442 if (!(event = ast_event_new(event_type,
00443 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00444 AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
00445 AST_EVENT_IE_END))) {
00446 return;
00447 }
00448
00449 ast_event_queue_and_cache(event);
00450 }
00451
00452
00453
00454 static void do_state_change(const char *device)
00455 {
00456 enum ast_device_state state;
00457
00458 state = _ast_device_state(device, 0);
00459
00460 ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, ast_devstate2str(state));
00461
00462 devstate_event(device, state);
00463 }
00464
00465 int ast_devstate_changed_literal(enum ast_device_state state, const char *device)
00466 {
00467 struct state_change *change;
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485 if (state != AST_DEVICE_UNKNOWN) {
00486 devstate_event(device, state);
00487 } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00488
00489
00490 do_state_change(device);
00491 } else {
00492
00493 strcpy(change->device, device);
00494 AST_LIST_LOCK(&state_changes);
00495 AST_LIST_INSERT_TAIL(&state_changes, change, list);
00496 ast_cond_signal(&change_pending);
00497 AST_LIST_UNLOCK(&state_changes);
00498 }
00499
00500 return 1;
00501 }
00502
00503 int ast_device_state_changed_literal(const char *dev)
00504 {
00505 return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, dev);
00506 }
00507
00508 int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
00509 {
00510 char buf[AST_MAX_EXTENSION];
00511 va_list ap;
00512
00513 va_start(ap, fmt);
00514 vsnprintf(buf, sizeof(buf), fmt, ap);
00515 va_end(ap);
00516
00517 return ast_devstate_changed_literal(state, buf);
00518 }
00519
00520 int ast_device_state_changed(const char *fmt, ...)
00521 {
00522 char buf[AST_MAX_EXTENSION];
00523 va_list ap;
00524
00525 va_start(ap, fmt);
00526 vsnprintf(buf, sizeof(buf), fmt, ap);
00527 va_end(ap);
00528
00529 return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf);
00530 }
00531
00532
00533 static void *do_devstate_changes(void *data)
00534 {
00535 struct state_change *next, *current;
00536
00537 for (;;) {
00538
00539 AST_LIST_LOCK(&state_changes);
00540 if (AST_LIST_EMPTY(&state_changes))
00541 ast_cond_wait(&change_pending, &state_changes.lock);
00542 next = AST_LIST_FIRST(&state_changes);
00543 AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
00544 AST_LIST_UNLOCK(&state_changes);
00545
00546
00547 while ((current = next)) {
00548 next = AST_LIST_NEXT(current, list);
00549 do_state_change(current->device);
00550 ast_free(current);
00551 }
00552 }
00553
00554 return NULL;
00555 }
00556
00557 static void destroy_devstate_change(struct devstate_change *sc)
00558 {
00559 ast_free(sc);
00560 }
00561
00562 #define MAX_SERVERS 64
00563 struct change_collection {
00564 struct devstate_change states[MAX_SERVERS];
00565 size_t num_states;
00566 };
00567
00568 static void devstate_cache_cb(const struct ast_event *event, void *data)
00569 {
00570 struct change_collection *collection = data;
00571 int i;
00572 const struct ast_eid *eid;
00573
00574 if (collection->num_states == ARRAY_LEN(collection->states)) {
00575 ast_log(LOG_ERROR, "More per-server state values than we have room for (MAX_SERVERS is %d)\n",
00576 MAX_SERVERS);
00577 return;
00578 }
00579
00580 if (!(eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) {
00581 ast_log(LOG_ERROR, "Device state change event with no EID\n");
00582 return;
00583 }
00584
00585 i = collection->num_states;
00586
00587 collection->states[i].state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00588 collection->states[i].eid = *eid;
00589
00590 collection->num_states++;
00591 }
00592
00593 static void process_collection(const char *device, struct change_collection *collection)
00594 {
00595 int i;
00596 struct ast_devstate_aggregate agg;
00597 enum ast_device_state state;
00598 struct ast_event *event;
00599
00600 ast_devstate_aggregate_init(&agg);
00601
00602 for (i = 0; i < collection->num_states; i++) {
00603 ast_debug(1, "Adding per-server state of '%s' for '%s'\n",
00604 ast_devstate2str(collection->states[i].state), device);
00605 ast_devstate_aggregate_add(&agg, collection->states[i].state);
00606 }
00607
00608 state = ast_devstate_aggregate_result(&agg);
00609
00610 ast_debug(1, "Aggregate devstate result is '%s' for '%s'\n",
00611 ast_devstate2str(state), device);
00612
00613 event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
00614 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00615 AST_EVENT_IE_END);
00616
00617 if (event) {
00618 enum ast_device_state old_state;
00619
00620 old_state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00621
00622 ast_event_destroy(event);
00623
00624 if (state == old_state) {
00625
00626 ast_debug(1, "Aggregate state for device '%s' has not changed from '%s'\n",
00627 device, ast_devstate2str(state));
00628 return;
00629 }
00630 }
00631
00632 ast_debug(1, "Aggregate state for device '%s' has changed to '%s'\n",
00633 device, ast_devstate2str(state));
00634
00635 event = ast_event_new(AST_EVENT_DEVICE_STATE,
00636 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00637 AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
00638 AST_EVENT_IE_END);
00639
00640 if (!event) {
00641 return;
00642 }
00643
00644 ast_event_queue_and_cache(event);
00645 }
00646
00647 static void handle_devstate_change(struct devstate_change *sc)
00648 {
00649 struct ast_event_sub *tmp_sub;
00650 struct change_collection collection = {
00651 .num_states = 0,
00652 };
00653
00654 ast_debug(1, "Processing device state change for '%s'\n", sc->device);
00655
00656 if (!(tmp_sub = ast_event_subscribe_new(AST_EVENT_DEVICE_STATE_CHANGE, devstate_cache_cb, &collection))) {
00657 ast_log(LOG_ERROR, "Failed to create subscription\n");
00658 return;
00659 }
00660
00661 if (ast_event_sub_append_ie_str(tmp_sub, AST_EVENT_IE_DEVICE, sc->device)) {
00662 ast_log(LOG_ERROR, "Failed to append device IE\n");
00663 ast_event_sub_destroy(tmp_sub);
00664 return;
00665 }
00666
00667
00668 ast_event_dump_cache(tmp_sub);
00669
00670 process_collection(sc->device, &collection);
00671
00672 ast_event_sub_destroy(tmp_sub);
00673 }
00674
00675 static void *run_devstate_collector(void *data)
00676 {
00677 for (;;) {
00678 struct devstate_change *sc;
00679
00680 ast_mutex_lock(&devstate_collector.lock);
00681 while (!(sc = AST_LIST_REMOVE_HEAD(&devstate_collector.devstate_change_q, entry)))
00682 ast_cond_wait(&devstate_collector.cond, &devstate_collector.lock);
00683 ast_mutex_unlock(&devstate_collector.lock);
00684
00685 handle_devstate_change(sc);
00686
00687 destroy_devstate_change(sc);
00688 }
00689
00690 return NULL;
00691 }
00692
00693 static void devstate_change_collector_cb(const struct ast_event *event, void *data)
00694 {
00695 struct devstate_change *sc;
00696 const char *device;
00697 const struct ast_eid *eid;
00698 uint32_t state;
00699
00700 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00701 eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID);
00702 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00703
00704 if (ast_strlen_zero(device) || !eid) {
00705 ast_log(LOG_ERROR, "Invalid device state change event received\n");
00706 return;
00707 }
00708
00709 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(device))))
00710 return;
00711
00712 strcpy(sc->device, device);
00713 sc->eid = *eid;
00714 sc->state = state;
00715
00716 ast_mutex_lock(&devstate_collector.lock);
00717 AST_LIST_INSERT_TAIL(&devstate_collector.devstate_change_q, sc, entry);
00718 ast_cond_signal(&devstate_collector.cond);
00719 ast_mutex_unlock(&devstate_collector.lock);
00720 }
00721
00722
00723 int ast_device_state_engine_init(void)
00724 {
00725 ast_cond_init(&change_pending, NULL);
00726 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00727 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00728 return -1;
00729 }
00730
00731 return 0;
00732 }
00733
00734 void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg)
00735 {
00736 memset(agg, 0, sizeof(*agg));
00737 agg->state = AST_DEVICE_INVALID;
00738 }
00739
00740 void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state)
00741 {
00742 static enum ast_device_state state_order[] = {
00743 1,
00744 3,
00745 6,
00746 7,
00747 0,
00748 2,
00749 5,
00750 8,
00751 4,
00752 };
00753
00754 if (state == AST_DEVICE_RINGING) {
00755 agg->ringing = 1;
00756 } else if (state == AST_DEVICE_INUSE || state == AST_DEVICE_ONHOLD || state == AST_DEVICE_BUSY) {
00757 agg->inuse = 1;
00758 }
00759
00760 if (agg->ringing && agg->inuse) {
00761 agg->state = AST_DEVICE_RINGINUSE;
00762 } else if (state_order[state] > state_order[agg->state]) {
00763 agg->state = state;
00764 }
00765 }
00766
00767 enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg)
00768 {
00769 return agg->state;
00770 }
00771
00772 int ast_enable_distributed_devstate(void)
00773 {
00774 if (devstate_collector.enabled) {
00775 return 0;
00776 }
00777
00778 devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
00779 devstate_change_collector_cb, "devicestate_engine_enable_distributed", NULL, AST_EVENT_IE_END);
00780
00781 if (!devstate_collector.event_sub) {
00782 ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n");
00783 return -1;
00784 }
00785
00786 ast_mutex_init(&devstate_collector.lock);
00787 ast_cond_init(&devstate_collector.cond, NULL);
00788 if (ast_pthread_create_background(&devstate_collector.thread, NULL, run_devstate_collector, NULL) < 0) {
00789 ast_log(LOG_ERROR, "Unable to start device state collector thread.\n");
00790 return -1;
00791 }
00792
00793 devstate_collector.enabled = 1;
00794
00795 return 0;
00796 }