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 #include <sys/types.h>
00026 #include <unistd.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <stdio.h>
00030
00031 #include "asterisk.h"
00032
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 9404 $")
00034
00035 #include "asterisk/channel.h"
00036 #include "asterisk/utils.h"
00037 #include "asterisk/lock.h"
00038 #include "asterisk/linkedlists.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/devicestate.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/options.h"
00043
00044 static const char *devstatestring[] = {
00045 "Unknown",
00046 "Not in use",
00047 "In use",
00048 "Busy",
00049 "Invalid",
00050 "Unavailable",
00051 "Ringing"
00052 };
00053
00054
00055 struct devstate_cb {
00056 void *data;
00057 ast_devstate_cb_type callback;
00058 AST_LIST_ENTRY(devstate_cb) list;
00059 };
00060
00061 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
00062
00063 struct state_change {
00064 AST_LIST_ENTRY(state_change) list;
00065 char device[1];
00066 };
00067
00068 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00069
00070 static pthread_t change_thread = AST_PTHREADT_NULL;
00071 static ast_cond_t change_pending;
00072
00073
00074 const char *devstate2str(int devstate)
00075 {
00076 return devstatestring[devstate];
00077 }
00078
00079
00080 int ast_parse_device_state(const char *device)
00081 {
00082 struct ast_channel *chan;
00083 char match[AST_CHANNEL_NAME];
00084 int res;
00085
00086 ast_copy_string(match, device, sizeof(match)-1);
00087 strcat(match, "-");
00088 chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
00089
00090 if (!chan)
00091 return AST_DEVICE_UNKNOWN;
00092
00093 if (chan->_state == AST_STATE_RINGING)
00094 res = AST_DEVICE_RINGING;
00095 else
00096 res = AST_DEVICE_INUSE;
00097
00098 ast_mutex_unlock(&chan->lock);
00099
00100 return res;
00101 }
00102
00103
00104 int ast_device_state(const char *device)
00105 {
00106 char *buf;
00107 char *tech;
00108 char *number;
00109 const struct ast_channel_tech *chan_tech;
00110 int res = 0;
00111
00112 buf = ast_strdupa(device);
00113 tech = strsep(&buf, "/");
00114 number = buf;
00115 if (!number)
00116 return AST_DEVICE_INVALID;
00117
00118 chan_tech = ast_get_channel_tech(tech);
00119 if (!chan_tech)
00120 return AST_DEVICE_INVALID;
00121
00122 if (!chan_tech->devicestate)
00123 return ast_parse_device_state(device);
00124 else {
00125 res = chan_tech->devicestate(number);
00126 if (res == AST_DEVICE_UNKNOWN) {
00127 res = ast_parse_device_state(device);
00128
00129
00130
00131
00132 if (res == AST_DEVICE_UNKNOWN)
00133 res = AST_DEVICE_NOT_INUSE;
00134 return res;
00135 } else
00136 return res;
00137 }
00138 }
00139
00140
00141 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
00142 {
00143 struct devstate_cb *devcb;
00144
00145 if (!callback)
00146 return -1;
00147
00148 devcb = calloc(1, sizeof(*devcb));
00149 if (!devcb)
00150 return -1;
00151
00152 devcb->data = data;
00153 devcb->callback = callback;
00154
00155 AST_LIST_LOCK(&devstate_cbs);
00156 AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
00157 AST_LIST_UNLOCK(&devstate_cbs);
00158
00159 return 0;
00160 }
00161
00162
00163 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
00164 {
00165 struct devstate_cb *devcb;
00166
00167 AST_LIST_LOCK(&devstate_cbs);
00168 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
00169 if ((devcb->callback == callback) && (devcb->data == data)) {
00170 AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
00171 free(devcb);
00172 break;
00173 }
00174 }
00175 AST_LIST_TRAVERSE_SAFE_END;
00176 AST_LIST_UNLOCK(&devstate_cbs);
00177 }
00178
00179
00180 static void do_state_change(const char *device)
00181 {
00182 int state;
00183 struct devstate_cb *devcb;
00184
00185 state = ast_device_state(device);
00186 if (option_debug > 2)
00187 ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
00188
00189 AST_LIST_LOCK(&devstate_cbs);
00190 AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
00191 devcb->callback(device, state, devcb->data);
00192 AST_LIST_UNLOCK(&devstate_cbs);
00193
00194 ast_hint_state_changed(device);
00195 }
00196
00197 static int __ast_device_state_changed_literal(char *buf)
00198 {
00199 char *device, *tmp;
00200 struct state_change *change = NULL;
00201
00202 device = buf;
00203 tmp = strrchr(device, '-');
00204 if (tmp)
00205 *tmp = '\0';
00206 if (change_thread != AST_PTHREADT_NULL)
00207 change = calloc(1, sizeof(*change) + strlen(device));
00208
00209 if (!change) {
00210
00211
00212 do_state_change(device);
00213 } else {
00214
00215 strcpy(change->device, device);
00216 AST_LIST_LOCK(&state_changes);
00217 AST_LIST_INSERT_TAIL(&state_changes, change, list);
00218 if (AST_LIST_FIRST(&state_changes) == change)
00219
00220 ast_cond_signal(&change_pending);
00221 AST_LIST_UNLOCK(&state_changes);
00222 }
00223
00224 return 1;
00225 }
00226
00227 int ast_device_state_changed_literal(const char *dev)
00228 {
00229 char *buf;
00230 buf = ast_strdupa(dev);
00231 return __ast_device_state_changed_literal(buf);
00232 }
00233
00234
00235 int ast_device_state_changed(const char *fmt, ...)
00236 {
00237 char buf[AST_MAX_EXTENSION];
00238 va_list ap;
00239
00240 va_start(ap, fmt);
00241 vsnprintf(buf, sizeof(buf), fmt, ap);
00242 va_end(ap);
00243 return __ast_device_state_changed_literal(buf);
00244 }
00245
00246
00247 static void *do_devstate_changes(void *data)
00248 {
00249 struct state_change *cur;
00250
00251 AST_LIST_LOCK(&state_changes);
00252 for(;;) {
00253
00254 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
00255 if (cur) {
00256
00257 AST_LIST_UNLOCK(&state_changes);
00258 do_state_change(cur->device);
00259 free(cur);
00260 AST_LIST_LOCK(&state_changes);
00261 } else {
00262
00263
00264 ast_cond_wait(&change_pending, &state_changes.lock);
00265 }
00266 }
00267
00268 return NULL;
00269 }
00270
00271
00272 int ast_device_state_engine_init(void)
00273 {
00274 ast_cond_init(&change_pending, NULL);
00275 if (ast_pthread_create(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00276 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00277 return -1;
00278 }
00279
00280 return 0;
00281 }