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;
00384 struct state_change *change;
00385 char *tmp = NULL;
00386
00387 if (option_debug > 2)
00388 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
00389
00390 device = buf;
00391
00392 tmp = strrchr(device, '-');
00393 if (tmp)
00394 *tmp = '\0';
00395
00396 if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00397
00398
00399 do_state_change(device);
00400 } else {
00401
00402 strcpy(change->device, device);
00403 AST_LIST_LOCK(&state_changes);
00404 AST_LIST_INSERT_TAIL(&state_changes, change, list);
00405 if (AST_LIST_FIRST(&state_changes) == change)
00406
00407 ast_cond_signal(&change_pending);
00408 AST_LIST_UNLOCK(&state_changes);
00409 }
00410
00411 return 1;
00412 }
00413
00414 int ast_device_state_changed_literal(const char *dev)
00415 {
00416 char *buf;
00417 buf = ast_strdupa(dev);
00418 return __ast_device_state_changed_literal(buf);
00419 }
00420
00421
00422 int ast_device_state_changed(const char *fmt, ...)
00423 {
00424 char buf[AST_MAX_EXTENSION];
00425 va_list ap;
00426
00427 va_start(ap, fmt);
00428 vsnprintf(buf, sizeof(buf), fmt, ap);
00429 va_end(ap);
00430 return __ast_device_state_changed_literal(buf);
00431 }
00432
00433
00434 static void *do_devstate_changes(void *data)
00435 {
00436 struct state_change *cur;
00437
00438 AST_LIST_LOCK(&state_changes);
00439 for(;;) {
00440
00441 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
00442 if (cur) {
00443
00444 AST_LIST_UNLOCK(&state_changes);
00445 do_state_change(cur->device);
00446 free(cur);
00447 AST_LIST_LOCK(&state_changes);
00448 } else {
00449
00450
00451 ast_cond_wait(&change_pending, &state_changes.lock);
00452 }
00453 }
00454
00455 return NULL;
00456 }
00457
00458
00459 int ast_device_state_engine_init(void)
00460 {
00461 ast_cond_init(&change_pending, NULL);
00462 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00463 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00464 return -1;
00465 }
00466
00467 return 0;
00468 }