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 #include "asterisk.h"
00111
00112 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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
00131 static const char *devstatestring[] = {
00132 "Unknown",
00133 "Not in use",
00134 "In use",
00135 "Busy",
00136 "Invalid",
00137 "Unavailable",
00138 "Ringing",
00139 "Ring+Inuse",
00140 "On Hold"
00141 };
00142
00143
00144 struct devstate_prov {
00145 char label[40];
00146 ast_devstate_prov_cb_type callback;
00147 AST_LIST_ENTRY(devstate_prov) list;
00148 };
00149
00150
00151 static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov);
00152
00153
00154 struct devstate_cb {
00155 void *data;
00156 ast_devstate_cb_type callback;
00157 AST_LIST_ENTRY(devstate_cb) list;
00158 };
00159
00160
00161 static AST_LIST_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
00169
00170 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00171
00172
00173 static pthread_t change_thread = AST_PTHREADT_NULL;
00174
00175
00176 static ast_cond_t change_pending;
00177
00178
00179 static int getproviderstate(const char *provider, const char *address);
00180
00181
00182 const char *devstate2str(int devstate)
00183 {
00184 return devstatestring[devstate];
00185 }
00186
00187
00188
00189
00190
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
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
00223 char *tech;
00224
00225 char *provider = NULL;
00226
00227 buf = ast_strdupa(device);
00228 tech = strsep(&buf, "/");
00229 number = buf;
00230 if (!number) {
00231 provider = strsep(&tech, ":");
00232 if (!provider)
00233 return AST_DEVICE_INVALID;
00234
00235 number = tech;
00236 tech = NULL;
00237 }
00238
00239 if (provider) {
00240 if(option_debug > 2)
00241 ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
00242 return getproviderstate(provider, number);
00243 }
00244 if (option_debug > 3)
00245 ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
00246
00247 chan_tech = ast_get_channel_tech(tech);
00248 if (!chan_tech)
00249 return AST_DEVICE_INVALID;
00250
00251 if (!chan_tech->devicestate)
00252 return ast_parse_device_state(device);
00253 else {
00254 res = chan_tech->devicestate(number);
00255 if (res == AST_DEVICE_UNKNOWN) {
00256 res = ast_parse_device_state(device);
00257
00258
00259
00260
00261 if (res == AST_DEVICE_UNKNOWN)
00262 res = AST_DEVICE_NOT_INUSE;
00263 return res;
00264 } else
00265 return res;
00266 }
00267 }
00268
00269
00270 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
00271 {
00272 struct devstate_prov *devprov;
00273
00274 if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
00275 return -1;
00276
00277 devprov->callback = callback;
00278 ast_copy_string(devprov->label, label, sizeof(devprov->label));
00279
00280 AST_LIST_LOCK(&devstate_provs);
00281 AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
00282 AST_LIST_UNLOCK(&devstate_provs);
00283
00284 return 0;
00285 }
00286
00287
00288 void ast_devstate_prov_del(const char *label)
00289 {
00290 struct devstate_prov *devcb;
00291
00292 AST_LIST_LOCK(&devstate_provs);
00293 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
00294 if (!strcasecmp(devcb->label, label)) {
00295 AST_LIST_REMOVE_CURRENT(&devstate_provs, list);
00296 free(devcb);
00297 break;
00298 }
00299 }
00300 AST_LIST_TRAVERSE_SAFE_END;
00301 AST_LIST_UNLOCK(&devstate_provs);
00302 }
00303
00304
00305 static int getproviderstate(const char *provider, const char *address)
00306 {
00307 struct devstate_prov *devprov;
00308 int res = AST_DEVICE_INVALID;
00309
00310
00311 AST_LIST_LOCK(&devstate_provs);
00312 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) {
00313 if(option_debug > 4)
00314 ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
00315
00316 if (!strcasecmp(devprov->label, provider)) {
00317 res = devprov->callback(address);
00318 break;
00319 }
00320 }
00321 AST_LIST_TRAVERSE_SAFE_END;
00322 AST_LIST_UNLOCK(&devstate_provs);
00323 return res;
00324 }
00325
00326
00327 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
00328 {
00329 struct devstate_cb *devcb;
00330
00331 if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
00332 return -1;
00333
00334 devcb->data = data;
00335 devcb->callback = callback;
00336
00337 AST_LIST_LOCK(&devstate_cbs);
00338 AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
00339 AST_LIST_UNLOCK(&devstate_cbs);
00340
00341 return 0;
00342 }
00343
00344
00345 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
00346 {
00347 struct devstate_cb *devcb;
00348
00349 AST_LIST_LOCK(&devstate_cbs);
00350 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
00351 if ((devcb->callback == callback) && (devcb->data == data)) {
00352 AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
00353 free(devcb);
00354 break;
00355 }
00356 }
00357 AST_LIST_TRAVERSE_SAFE_END;
00358 AST_LIST_UNLOCK(&devstate_cbs);
00359 }
00360
00361
00362
00363
00364 static void do_state_change(const char *device)
00365 {
00366 int state;
00367 struct devstate_cb *devcb;
00368
00369 state = ast_device_state(device);
00370 if (option_debug > 2)
00371 ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
00372
00373 AST_LIST_LOCK(&devstate_cbs);
00374 AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
00375 devcb->callback(device, state, devcb->data);
00376 AST_LIST_UNLOCK(&devstate_cbs);
00377
00378 ast_hint_state_changed(device);
00379 }
00380
00381 static int __ast_device_state_changed_literal(char *buf)
00382 {
00383 char *device, *tmp;
00384 struct state_change *change;
00385
00386 if (option_debug > 2)
00387 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
00388
00389 device = buf;
00390 if ((tmp = strrchr(device, '-')))
00391 *tmp = '\0';
00392
00393
00394
00395 if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00396
00397
00398 do_state_change(device);
00399 } else {
00400
00401 strcpy(change->device, device);
00402 AST_LIST_LOCK(&state_changes);
00403 AST_LIST_INSERT_TAIL(&state_changes, change, list);
00404 if (AST_LIST_FIRST(&state_changes) == change)
00405
00406 ast_cond_signal(&change_pending);
00407 AST_LIST_UNLOCK(&state_changes);
00408 }
00409
00410 return 1;
00411 }
00412
00413 int ast_device_state_changed_literal(const char *dev)
00414 {
00415 char *buf;
00416 buf = ast_strdupa(dev);
00417 return __ast_device_state_changed_literal(buf);
00418 }
00419
00420
00421 int ast_device_state_changed(const char *fmt, ...)
00422 {
00423 char buf[AST_MAX_EXTENSION];
00424 va_list ap;
00425
00426 va_start(ap, fmt);
00427 vsnprintf(buf, sizeof(buf), fmt, ap);
00428 va_end(ap);
00429 return __ast_device_state_changed_literal(buf);
00430 }
00431
00432
00433 static void *do_devstate_changes(void *data)
00434 {
00435 struct state_change *cur;
00436
00437 AST_LIST_LOCK(&state_changes);
00438 for(;;) {
00439
00440 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
00441 if (cur) {
00442
00443 AST_LIST_UNLOCK(&state_changes);
00444 do_state_change(cur->device);
00445 free(cur);
00446 AST_LIST_LOCK(&state_changes);
00447 } else {
00448
00449
00450 ast_cond_wait(&change_pending, &state_changes.lock);
00451 }
00452 }
00453
00454 return NULL;
00455 }
00456
00457
00458 int ast_device_state_engine_init(void)
00459 {
00460 ast_cond_init(&change_pending, NULL);
00461 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00462 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00463 return -1;
00464 }
00465
00466 return 0;
00467 }