Let's also include aclocal.m4
[asterisk-bristuff.git] / main / devicestate.c
blobf84c87df74d80d3150941e8c03dee961c159db6b
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Device state management
23 * \author Mark Spencer <markster@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdio.h>
36 #include "asterisk/channel.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/lock.h"
39 #include "asterisk/linkedlists.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/devicestate.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/app.h"
44 #include "asterisk/options.h"
46 /*! \brief Device state strings for printing */
47 static const char *devstatestring[] = {
48 /* 0 AST_DEVICE_UNKNOWN */ "Unknown", /*!< Valid, but unknown state */
49 /* 1 AST_DEVICE_NOT_INUSE */ "Not in use", /*!< Not used */
50 /* 2 AST_DEVICE IN USE */ "In use", /*!< In use */
51 /* 3 AST_DEVICE_BUSY */ "Busy", /*!< Busy */
52 /* 4 AST_DEVICE_INVALID */ "Invalid", /*!< Invalid - not known to Asterisk */
53 /* 5 AST_DEVICE_UNAVAILABLE */ "Unavailable", /*!< Unavailable (not registred) */
54 /* 6 AST_DEVICE_RINGING */ "Ringing", /*!< Ring, ring, ring */
55 /* 7 AST_DEVICE_RINGINUSE */ "Ring+Inuse", /*!< Ring and in use */
56 /* 8 AST_DEVICE_ONHOLD */ "On Hold" /*!< On Hold */
59 /*! \brief A device state provider (not a channel) */
60 struct devstate_prov {
61 char label[40];
62 ast_devstate_prov_cb_type callback;
63 AST_LIST_ENTRY(devstate_prov) list;
66 /*! \brief A list of providers */
67 static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov);
69 /*! \brief A device state watcher (callback) */
70 struct devstate_cb {
71 void *data;
72 ast_devstate_cb_type callback;
73 AST_LIST_ENTRY(devstate_cb) list;
76 /*! \brief A device state watcher list */
77 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
79 struct state_change {
80 AST_LIST_ENTRY(state_change) list;
81 char device[1];
84 /*! \brief The state change queue. State changes are queued
85 for processing by a separate thread */
86 static AST_LIST_HEAD_STATIC(state_changes, state_change);
88 /*! \brief The device state change notification thread */
89 static pthread_t change_thread = AST_PTHREADT_NULL;
91 /*! \brief Flag for the queue */
92 static ast_cond_t change_pending;
94 /* Forward declarations */
95 static int getproviderstate(const char *provider, const char *address);
97 /*! \brief Find devicestate as text message for output */
98 const char *devstate2str(int devstate)
100 return devstatestring[devstate];
103 /*! \brief Find out if device is active in a call or not
104 \note find channels with the device's name in it
105 This function is only used for channels that does not implement
106 devicestate natively
108 int ast_parse_device_state(const char *device)
110 struct ast_channel *chan;
111 char match[AST_CHANNEL_NAME];
112 int res;
114 ast_copy_string(match, device, sizeof(match)-1);
115 strcat(match, "-");
116 chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
118 if (!chan)
119 return AST_DEVICE_UNKNOWN;
121 if (chan->_state == AST_STATE_RINGING)
122 res = AST_DEVICE_RINGING;
123 else
124 res = AST_DEVICE_INUSE;
126 ast_channel_unlock(chan);
128 return res;
131 /*! \brief Check device state through channel specific function or generic function */
132 int ast_device_state(const char *device)
134 char *buf;
135 char *number;
136 const struct ast_channel_tech *chan_tech;
137 int res = 0;
138 /*! \brief Channel driver that provides device state */
139 char *tech;
140 /*! \brief Another provider of device state */
141 char *provider = NULL;
143 buf = ast_strdupa(device);
144 tech = strsep(&buf, "/");
145 number = buf;
146 if (!number) {
147 provider = strsep(&tech, ":");
148 if (!provider)
149 return AST_DEVICE_INVALID;
150 /* We have a provider */
151 number = tech;
152 tech = NULL;
155 if (provider) {
156 if(option_debug > 2)
157 ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
158 return getproviderstate(provider, number);
160 if (option_debug > 3)
161 ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
163 chan_tech = ast_get_channel_tech(tech);
164 if (!chan_tech)
165 return AST_DEVICE_INVALID;
167 if (!chan_tech->devicestate) /* Does the channel driver support device state notification? */
168 return ast_parse_device_state(device); /* No, try the generic function */
169 else {
170 res = chan_tech->devicestate(number); /* Ask the channel driver for device state */
171 if (res == AST_DEVICE_UNKNOWN) {
172 res = ast_parse_device_state(device);
173 /* at this point we know the device exists, but the channel driver
174 could not give us a state; if there is no channel state available,
175 it must be 'not in use'
177 if (res == AST_DEVICE_UNKNOWN)
178 res = AST_DEVICE_NOT_INUSE;
179 return res;
180 } else
181 return res;
185 /*! \brief Add device state provider */
186 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
188 struct devstate_prov *devprov;
190 if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
191 return -1;
193 devprov->callback = callback;
194 ast_copy_string(devprov->label, label, sizeof(devprov->label));
196 AST_LIST_LOCK(&devstate_provs);
197 AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
198 AST_LIST_UNLOCK(&devstate_provs);
200 return 0;
203 /*! \brief Remove device state provider */
204 void ast_devstate_prov_del(const char *label)
206 struct devstate_prov *devcb;
208 AST_LIST_LOCK(&devstate_provs);
209 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
210 if (!strcasecmp(devcb->label, label)) {
211 AST_LIST_REMOVE_CURRENT(&devstate_provs, list);
212 free(devcb);
213 break;
216 AST_LIST_TRAVERSE_SAFE_END;
217 AST_LIST_UNLOCK(&devstate_provs);
220 /*! \brief Get provider device state */
221 static int getproviderstate(const char *provider, const char *address)
223 struct devstate_prov *devprov;
224 int res = AST_DEVICE_INVALID;
227 AST_LIST_LOCK(&devstate_provs);
228 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) {
229 if(option_debug > 4)
230 ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
232 if (!strcasecmp(devprov->label, provider)) {
233 res = devprov->callback(address);
234 break;
237 AST_LIST_TRAVERSE_SAFE_END;
238 AST_LIST_UNLOCK(&devstate_provs);
239 return res;
242 /*! \brief Add device state watcher */
243 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
245 struct devstate_cb *devcb;
247 if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
248 return -1;
250 devcb->data = data;
251 devcb->callback = callback;
253 AST_LIST_LOCK(&devstate_cbs);
254 AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
255 AST_LIST_UNLOCK(&devstate_cbs);
257 return 0;
260 /*! \brief Remove device state watcher */
261 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
263 struct devstate_cb *devcb;
265 AST_LIST_LOCK(&devstate_cbs);
266 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
267 if ((devcb->callback == callback) && (devcb->data == data)) {
268 AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
269 free(devcb);
270 break;
273 AST_LIST_TRAVERSE_SAFE_END;
274 AST_LIST_UNLOCK(&devstate_cbs);
277 /*! \brief Notify callback watchers of change, and notify PBX core for hint updates
278 Normally executed within a separate thread
280 static void do_state_change(const char *device)
282 int state;
283 struct devstate_cb *devcb;
285 state = ast_device_state(device);
286 if (option_debug > 2)
287 ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
289 AST_LIST_LOCK(&devstate_cbs);
290 AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
291 devcb->callback(device, state, devcb->data);
292 AST_LIST_UNLOCK(&devstate_cbs);
294 ast_hint_state_changed(device);
297 int ast_device_state_changed_literal(const char *device)
299 struct state_change *change;
301 if (option_debug > 2)
302 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", device);
304 if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
305 /* we could not allocate a change struct, or */
306 /* there is no background thread, so process the change now */
307 do_state_change(device);
308 } else {
309 /* queue the change */
310 strcpy(change->device, device);
311 AST_LIST_LOCK(&state_changes);
312 AST_LIST_INSERT_TAIL(&state_changes, change, list);
313 if (AST_LIST_FIRST(&state_changes) == change)
314 /* the list was empty, signal the thread */
315 ast_cond_signal(&change_pending);
316 AST_LIST_UNLOCK(&state_changes);
319 return 1;
322 /*! \brief Accept change notification, add it to change queue */
323 int ast_device_state_changed(const char *fmt, ...)
325 char buf[AST_MAX_EXTENSION];
326 va_list ap;
328 va_start(ap, fmt);
329 vsnprintf(buf, sizeof(buf), fmt, ap);
330 va_end(ap);
331 return ast_device_state_changed_literal(buf);
334 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
335 static void *do_devstate_changes(void *data)
337 struct state_change *cur;
339 AST_LIST_LOCK(&state_changes);
340 for(;;) {
341 /* the list lock will _always_ be held at this point in the loop */
342 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
343 if (cur) {
344 /* we got an entry, so unlock the list while we process it */
345 AST_LIST_UNLOCK(&state_changes);
346 do_state_change(cur->device);
347 free(cur);
348 AST_LIST_LOCK(&state_changes);
349 } else {
350 /* there was no entry, so atomically unlock the list and wait for
351 the condition to be signalled (returns with the lock held) */
352 ast_cond_wait(&change_pending, &state_changes.lock);
356 return NULL;
359 /*! \brief Initialize the device state engine in separate thread */
360 int ast_device_state_engine_init(void)
362 ast_cond_init(&change_pending, NULL);
363 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
364 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
365 return -1;
368 return 0;