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: 263639 $")
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 device[1];
00082 };
00083
00084
00085
00086 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00087
00088
00089 static pthread_t change_thread = AST_PTHREADT_NULL;
00090
00091
00092 static ast_cond_t change_pending;
00093
00094
00095 static int getproviderstate(const char *provider, const char *address);
00096
00097
00098 const char *devstate2str(enum ast_device_state devstate)
00099 {
00100 return devstatestring[devstate];
00101 }
00102
00103
00104
00105
00106
00107
00108 int ast_parse_device_state(const char *device)
00109 {
00110 struct ast_channel *chan;
00111 char match[AST_CHANNEL_NAME];
00112 int res;
00113
00114 ast_copy_string(match, device, sizeof(match)-1);
00115 strcat(match, "-");
00116 chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
00117
00118 if (!chan)
00119 return AST_DEVICE_UNKNOWN;
00120
00121 if (chan->_state == AST_STATE_RINGING)
00122 res = AST_DEVICE_RINGING;
00123 else
00124 res = AST_DEVICE_INUSE;
00125
00126 ast_channel_unlock(chan);
00127
00128 return res;
00129 }
00130
00131
00132 int ast_device_state(const char *device)
00133 {
00134 char *buf;
00135 char *number;
00136 const struct ast_channel_tech *chan_tech;
00137 int res = 0;
00138
00139 char *tech;
00140
00141 char *provider = NULL;
00142
00143 buf = ast_strdupa(device);
00144 tech = strsep(&buf, "/");
00145 number = buf;
00146 if (!number) {
00147 provider = strsep(&tech, ":");
00148 if (!tech)
00149 return AST_DEVICE_INVALID;
00150
00151 number = tech;
00152 tech = NULL;
00153 }
00154
00155 if (provider) {
00156 if(option_debug > 2)
00157 ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
00158 return getproviderstate(provider, number);
00159 }
00160 if (option_debug > 3)
00161 ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
00162
00163 chan_tech = ast_get_channel_tech(tech);
00164 if (!chan_tech)
00165 return AST_DEVICE_INVALID;
00166
00167 if (!chan_tech->devicestate)
00168 return ast_parse_device_state(device);
00169 else {
00170 res = chan_tech->devicestate(number);
00171 if (res == AST_DEVICE_UNKNOWN) {
00172 res = ast_parse_device_state(device);
00173
00174
00175
00176
00177 if (res == AST_DEVICE_UNKNOWN)
00178 res = AST_DEVICE_NOT_INUSE;
00179 return res;
00180 } else
00181 return res;
00182 }
00183 }
00184
00185
00186 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
00187 {
00188 struct devstate_prov *devprov;
00189
00190 if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
00191 return -1;
00192
00193 devprov->callback = callback;
00194 ast_copy_string(devprov->label, label, sizeof(devprov->label));
00195
00196 AST_LIST_LOCK(&devstate_provs);
00197 AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
00198 AST_LIST_UNLOCK(&devstate_provs);
00199
00200 return 0;
00201 }
00202
00203
00204 void ast_devstate_prov_del(const char *label)
00205 {
00206 struct devstate_prov *devcb;
00207
00208 AST_LIST_LOCK(&devstate_provs);
00209 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
00210 if (!strcasecmp(devcb->label, label)) {
00211 AST_LIST_REMOVE_CURRENT(&devstate_provs, list);
00212 free(devcb);
00213 break;
00214 }
00215 }
00216 AST_LIST_TRAVERSE_SAFE_END;
00217 AST_LIST_UNLOCK(&devstate_provs);
00218 }
00219
00220
00221 static int getproviderstate(const char *provider, const char *address)
00222 {
00223 struct devstate_prov *devprov;
00224 int res = AST_DEVICE_INVALID;
00225
00226
00227 AST_LIST_LOCK(&devstate_provs);
00228 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) {
00229 if(option_debug > 4)
00230 ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
00231
00232 if (!strcasecmp(devprov->label, provider)) {
00233 res = devprov->callback(address);
00234 break;
00235 }
00236 }
00237 AST_LIST_TRAVERSE_SAFE_END;
00238 AST_LIST_UNLOCK(&devstate_provs);
00239 return res;
00240 }
00241
00242
00243 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
00244 {
00245 struct devstate_cb *devcb;
00246
00247 if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
00248 return -1;
00249
00250 devcb->data = data;
00251 devcb->callback = callback;
00252
00253 AST_LIST_LOCK(&devstate_cbs);
00254 AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
00255 AST_LIST_UNLOCK(&devstate_cbs);
00256
00257 return 0;
00258 }
00259
00260
00261 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
00262 {
00263 struct devstate_cb *devcb;
00264
00265 AST_LIST_LOCK(&devstate_cbs);
00266 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
00267 if ((devcb->callback == callback) && (devcb->data == data)) {
00268 AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
00269 free(devcb);
00270 break;
00271 }
00272 }
00273 AST_LIST_TRAVERSE_SAFE_END;
00274 AST_LIST_UNLOCK(&devstate_cbs);
00275 }
00276
00277
00278
00279
00280 static void do_state_change(const char *device)
00281 {
00282 int state;
00283 struct devstate_cb *devcb;
00284
00285 state = ast_device_state(device);
00286 if (option_debug > 2)
00287 ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
00288
00289 AST_LIST_LOCK(&devstate_cbs);
00290 AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
00291 devcb->callback(device, state, devcb->data);
00292 AST_LIST_UNLOCK(&devstate_cbs);
00293
00294 ast_hint_state_changed(device);
00295 }
00296
00297 int ast_device_state_changed_literal(const char *device)
00298 {
00299 struct state_change *change;
00300
00301 if (option_debug > 2)
00302 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", device);
00303
00304 if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00305
00306
00307 do_state_change(device);
00308 } else {
00309
00310 strcpy(change->device, device);
00311 AST_LIST_LOCK(&state_changes);
00312 AST_LIST_INSERT_TAIL(&state_changes, change, list);
00313 if (AST_LIST_FIRST(&state_changes) == change)
00314
00315 ast_cond_signal(&change_pending);
00316 AST_LIST_UNLOCK(&state_changes);
00317 }
00318
00319 return 1;
00320 }
00321
00322
00323 int ast_device_state_changed(const char *fmt, ...)
00324 {
00325 char buf[AST_MAX_EXTENSION];
00326 va_list ap;
00327
00328 va_start(ap, fmt);
00329 vsnprintf(buf, sizeof(buf), fmt, ap);
00330 va_end(ap);
00331 return ast_device_state_changed_literal(buf);
00332 }
00333
00334
00335 static void *do_devstate_changes(void *data)
00336 {
00337 struct state_change *cur;
00338
00339 AST_LIST_LOCK(&state_changes);
00340 for(;;) {
00341
00342 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
00343 if (cur) {
00344
00345 AST_LIST_UNLOCK(&state_changes);
00346 do_state_change(cur->device);
00347 free(cur);
00348 AST_LIST_LOCK(&state_changes);
00349 } else {
00350
00351
00352 ast_cond_wait(&change_pending, &state_changes.lock);
00353 }
00354 }
00355
00356 return NULL;
00357 }
00358
00359
00360 int ast_device_state_engine_init(void)
00361 {
00362 ast_cond_init(&change_pending, NULL);
00363 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00364 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00365 return -1;
00366 }
00367
00368 return 0;
00369 }
00370
00371 void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg)
00372 {
00373 memset(agg, 0, sizeof(*agg));
00374 agg->all_unknown = 1;
00375 agg->all_unavail = 1;
00376 agg->all_busy = 1;
00377 agg->all_free = 1;
00378 }
00379
00380 void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state)
00381 {
00382 switch (state) {
00383 case AST_DEVICE_NOT_INUSE:
00384 agg->all_unknown = 0;
00385 agg->all_unavail = 0;
00386 agg->all_busy = 0;
00387 break;
00388 case AST_DEVICE_INUSE:
00389 agg->in_use = 1;
00390 agg->all_unavail = 0;
00391 agg->all_free = 0;
00392 agg->all_unknown = 0;
00393 break;
00394 case AST_DEVICE_RINGING:
00395 agg->ring = 1;
00396 agg->all_unavail = 0;
00397 agg->all_free = 0;
00398 agg->all_unknown = 0;
00399 break;
00400 case AST_DEVICE_RINGINUSE:
00401 agg->in_use = 1;
00402 agg->ring = 1;
00403 agg->all_unavail = 0;
00404 agg->all_free = 0;
00405 agg->all_unknown = 0;
00406 break;
00407 case AST_DEVICE_ONHOLD:
00408 agg->all_unknown = 0;
00409 agg->all_unavail = 0;
00410 agg->all_free = 0;
00411 agg->on_hold = 1;
00412 break;
00413 case AST_DEVICE_BUSY:
00414 agg->all_unknown = 0;
00415 agg->all_unavail = 0;
00416 agg->all_free = 0;
00417 agg->busy = 1;
00418 agg->in_use = 1;
00419 break;
00420 case AST_DEVICE_UNAVAILABLE:
00421 agg->all_unknown = 0;
00422 case AST_DEVICE_INVALID:
00423 agg->all_busy = 0;
00424 agg->all_free = 0;
00425 break;
00426 case AST_DEVICE_UNKNOWN:
00427 agg->all_busy = 0;
00428 agg->all_free = 0;
00429 break;
00430 case AST_DEVICE_TOTAL:
00431 break;
00432 }
00433 }
00434
00435 enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg)
00436 {
00437 if (agg->all_free)
00438 return AST_DEVICE_NOT_INUSE;
00439 if ((agg->in_use || agg->on_hold) && agg->ring)
00440 return AST_DEVICE_RINGINUSE;
00441 if (agg->ring)
00442 return AST_DEVICE_RINGING;
00443 if (agg->busy)
00444 return AST_DEVICE_BUSY;
00445 if (agg->in_use)
00446 return AST_DEVICE_INUSE;
00447 if (agg->on_hold)
00448 return AST_DEVICE_ONHOLD;
00449 if (agg->all_busy)
00450 return AST_DEVICE_BUSY;
00451 if (agg->all_unknown)
00452 return AST_DEVICE_UNKNOWN;
00453 if (agg->all_unavail)
00454 return AST_DEVICE_UNAVAILABLE;
00455
00456 return AST_DEVICE_NOT_INUSE;
00457 }