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 #include "asterisk.h"
00027
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 96644 $")
00029
00030 #include <sys/types.h>
00031 #include <unistd.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <stdio.h>
00035
00036 #include "asterisk/channel.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/lock.h"
00039 #include "asterisk/linkedlists.h"
00040 #include "asterisk/logger.h"
00041 #include "asterisk/devicestate.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/options.h"
00045
00046
00047 static const char *devstatestring[] = {
00048 "Unknown",
00049 "Not in use",
00050 "In use",
00051 "Busy",
00052 "Invalid",
00053 "Unavailable",
00054 "Ringing",
00055 "Ring+Inuse",
00056 "On Hold"
00057 };
00058
00059
00060 struct devstate_prov {
00061 char label[40];
00062 ast_devstate_prov_cb_type callback;
00063 AST_LIST_ENTRY(devstate_prov) list;
00064 };
00065
00066
00067 static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov);
00068
00069
00070 struct devstate_cb {
00071 void *data;
00072 ast_devstate_cb_type callback;
00073 AST_LIST_ENTRY(devstate_cb) list;
00074 };
00075
00076
00077 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
00078
00079 struct state_change {
00080 AST_LIST_ENTRY(state_change) list;
00081 char cid_num[AST_MAX_EXTENSION];
00082 char cid_name[AST_MAX_EXTENSION];
00083 char device[1];
00084 };
00085
00086
00087
00088 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00089
00090
00091 static pthread_t change_thread = AST_PTHREADT_NULL;
00092
00093
00094 static ast_cond_t change_pending;
00095
00096
00097 static int getproviderstate(const char *provider, const char *address);
00098
00099
00100 const char *devstate2str(int devstate)
00101 {
00102 return devstatestring[devstate];
00103 }
00104
00105
00106
00107
00108
00109
00110 int ast_parse_device_state(const char *device)
00111 {
00112 struct ast_channel *chan;
00113 char match[AST_CHANNEL_NAME];
00114 int res;
00115
00116 ast_copy_string(match, device, sizeof(match)-1);
00117 strcat(match, "-");
00118 chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
00119
00120 if (!chan)
00121 return AST_DEVICE_UNKNOWN;
00122
00123 if (chan->_state == AST_STATE_RINGING)
00124 res = AST_DEVICE_RINGING;
00125 else
00126 res = AST_DEVICE_INUSE;
00127
00128 ast_channel_unlock(chan);
00129
00130 return res;
00131 }
00132
00133
00134 int ast_device_state(const char *device)
00135 {
00136 char *buf;
00137 char *number;
00138 const struct ast_channel_tech *chan_tech;
00139 int res = 0;
00140
00141 char *tech;
00142
00143 char *provider = NULL;
00144
00145 buf = ast_strdupa(device);
00146 tech = strsep(&buf, "/");
00147 number = buf;
00148 if (!number) {
00149 provider = strsep(&tech, ":");
00150 if (!provider)
00151 return AST_DEVICE_INVALID;
00152
00153 number = tech;
00154 tech = NULL;
00155 }
00156
00157 if (provider) {
00158 if(option_debug > 2)
00159 ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
00160 return getproviderstate(provider, number);
00161 }
00162 if (option_debug > 3)
00163 ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
00164
00165 chan_tech = ast_get_channel_tech(tech);
00166 if (!chan_tech)
00167 return AST_DEVICE_INVALID;
00168
00169 if (!chan_tech->devicestate)
00170 return ast_parse_device_state(device);
00171 else {
00172 res = chan_tech->devicestate(number);
00173 if (res == AST_DEVICE_UNKNOWN) {
00174 res = ast_parse_device_state(device);
00175
00176
00177
00178
00179 if (res == AST_DEVICE_UNKNOWN)
00180 res = AST_DEVICE_NOT_INUSE;
00181 return res;
00182 } else
00183 return res;
00184 }
00185 }
00186
00187
00188 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
00189 {
00190 struct devstate_prov *devprov;
00191
00192 if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
00193 return -1;
00194
00195 devprov->callback = callback;
00196 ast_copy_string(devprov->label, label, sizeof(devprov->label));
00197
00198 AST_LIST_LOCK(&devstate_provs);
00199 AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
00200 AST_LIST_UNLOCK(&devstate_provs);
00201
00202 return 0;
00203 }
00204
00205
00206 void ast_devstate_prov_del(const char *label)
00207 {
00208 struct devstate_prov *devcb;
00209
00210 AST_LIST_LOCK(&devstate_provs);
00211 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
00212 if (!strcasecmp(devcb->label, label)) {
00213 AST_LIST_REMOVE_CURRENT(&devstate_provs, list);
00214 free(devcb);
00215 break;
00216 }
00217 }
00218 AST_LIST_TRAVERSE_SAFE_END;
00219 AST_LIST_UNLOCK(&devstate_provs);
00220 }
00221
00222
00223 static int getproviderstate(const char *provider, const char *address)
00224 {
00225 struct devstate_prov *devprov;
00226 int res = AST_DEVICE_INVALID;
00227
00228
00229 AST_LIST_LOCK(&devstate_provs);
00230 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) {
00231 if(option_debug > 4)
00232 ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
00233
00234 if (!strcasecmp(devprov->label, provider)) {
00235 res = devprov->callback(address);
00236 break;
00237 }
00238 }
00239 AST_LIST_TRAVERSE_SAFE_END;
00240 AST_LIST_UNLOCK(&devstate_provs);
00241 return res;
00242 }
00243
00244
00245 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
00246 {
00247 struct devstate_cb *devcb;
00248
00249 if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
00250 return -1;
00251
00252 devcb->data = data;
00253 devcb->callback = callback;
00254
00255 AST_LIST_LOCK(&devstate_cbs);
00256 AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
00257 AST_LIST_UNLOCK(&devstate_cbs);
00258
00259 return 0;
00260 }
00261
00262
00263 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
00264 {
00265 struct devstate_cb *devcb;
00266
00267 AST_LIST_LOCK(&devstate_cbs);
00268 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
00269 if ((devcb->callback == callback) && (devcb->data == data)) {
00270 AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
00271 free(devcb);
00272 break;
00273 }
00274 }
00275 AST_LIST_TRAVERSE_SAFE_END;
00276 AST_LIST_UNLOCK(&devstate_cbs);
00277 }
00278
00279
00280
00281
00282 static void do_state_change(const char *device, char *cid_num, char *cid_name)
00283 {
00284 int state;
00285 struct devstate_cb *devcb;
00286
00287 state = ast_device_state(device);
00288 if (option_debug > 2)
00289 ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
00290
00291 AST_LIST_LOCK(&devstate_cbs);
00292 AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
00293 devcb->callback(device, state, devcb->data, cid_num, cid_name);
00294 AST_LIST_UNLOCK(&devstate_cbs);
00295
00296 ast_hint_state_changed(device, cid_num, cid_name);
00297 }
00298
00299 static int __ast_device_state_changed_literal(char *buf, int norecurse, char *cid_num, char *cid_name)
00300 {
00301 char *device;
00302 struct state_change *change;
00303 char *tmp = NULL;
00304
00305 if (option_debug > 2)
00306 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
00307
00308 device = buf;
00309
00310 if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00311
00312
00313 do_state_change(device, cid_num, cid_name);
00314 } else {
00315
00316 strcpy(change->device, device);
00317 if (cid_num && (!ast_strlen_zero(cid_num))) {
00318 strncpy(change->cid_num, cid_num, sizeof(change->cid_num) - 1);
00319 }
00320 if (cid_name && (!ast_strlen_zero(cid_name))) {
00321 strncpy(change->cid_name, cid_name, sizeof(change->cid_name) - 1);
00322 }
00323 AST_LIST_LOCK(&state_changes);
00324 AST_LIST_INSERT_TAIL(&state_changes, change, list);
00325 if (AST_LIST_FIRST(&state_changes) == change)
00326
00327 ast_cond_signal(&change_pending);
00328 AST_LIST_UNLOCK(&state_changes);
00329 }
00330
00331
00332
00333
00334
00335
00336
00337
00338 if (!norecurse && (tmp = strrchr(device, '-'))) {
00339 *tmp = '\0';
00340 __ast_device_state_changed_literal(device, 1, cid_num, cid_name);
00341 }
00342
00343 return 1;
00344 }
00345
00346 int ast_device_state_changed_literal(const char *dev, const char *cid_num, const char *cid_name)
00347 {
00348 char *buf;
00349 char *buf2 = NULL;
00350 char *buf3 = NULL;
00351 buf = ast_strdupa(dev);
00352 if (cid_num)
00353 buf2 = ast_strdupa(cid_num);
00354 if (cid_name)
00355 buf3 = ast_strdupa(cid_name);
00356 return __ast_device_state_changed_literal(buf, 0, buf2, buf3);
00357 }
00358
00359
00360 int ast_device_state_changed(const char *fmt, ...)
00361 {
00362 char buf[AST_MAX_EXTENSION];
00363 va_list ap;
00364
00365 va_start(ap, fmt);
00366 vsnprintf(buf, sizeof(buf), fmt, ap);
00367 va_end(ap);
00368 return __ast_device_state_changed_literal(buf, 0, NULL, NULL);
00369 }
00370
00371
00372 static void *do_devstate_changes(void *data)
00373 {
00374 struct state_change *cur;
00375
00376 AST_LIST_LOCK(&state_changes);
00377 for(;;) {
00378
00379 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
00380 if (cur) {
00381
00382 AST_LIST_UNLOCK(&state_changes);
00383 do_state_change(cur->device, cur->cid_num, cur->cid_name);
00384 free(cur);
00385 AST_LIST_LOCK(&state_changes);
00386 } else {
00387
00388
00389 ast_cond_wait(&change_pending, &state_changes.lock);
00390 }
00391 }
00392
00393 return NULL;
00394 }
00395
00396
00397 int ast_device_state_engine_init(void)
00398 {
00399 ast_cond_init(&change_pending, NULL);
00400 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00401 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00402 return -1;
00403 }
00404
00405 return 0;
00406 }