remove the dlfcn compatibility stuff, because no platforms that Asterisk currently...
[asterisk-bristuff.git] / main / devicestate.c
blobcc647a82392f9f4a74aa3ba6dd41867a81143364
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 static int __ast_device_state_changed_literal(char *buf, int norecurse)
299 char *device;
300 struct state_change *change;
301 char *tmp = NULL;
303 if (option_debug > 2)
304 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
306 device = buf;
308 if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
309 /* we could not allocate a change struct, or */
310 /* there is no background thread, so process the change now */
311 do_state_change(device);
312 } else {
313 /* queue the change */
314 strcpy(change->device, device);
315 AST_LIST_LOCK(&state_changes);
316 AST_LIST_INSERT_TAIL(&state_changes, change, list);
317 if (AST_LIST_FIRST(&state_changes) == change)
318 /* the list was empty, signal the thread */
319 ast_cond_signal(&change_pending);
320 AST_LIST_UNLOCK(&state_changes);
323 /* The problem with this API is that a device may be called with the unique
324 * identifier appended or not, but it's separated from the channel name
325 * with a '-', which is also a legitimate character in a channel name. So,
326 * we have to force both names to get their names checked for state changes
327 * to ensure that the right one gets notified. Not a huge performance hit,
328 * but it might could be fixed by an enterprising programmer in trunk.
330 if (!norecurse && (tmp = strrchr(device, '-'))) {
331 *tmp = '\0';
332 __ast_device_state_changed_literal(device, 1);
335 return 1;
338 int ast_device_state_changed_literal(const char *dev)
340 char *buf;
341 buf = ast_strdupa(dev);
342 return __ast_device_state_changed_literal(buf, 0);
345 /*! \brief Accept change notification, add it to change queue */
346 int ast_device_state_changed(const char *fmt, ...)
348 char buf[AST_MAX_EXTENSION];
349 va_list ap;
351 va_start(ap, fmt);
352 vsnprintf(buf, sizeof(buf), fmt, ap);
353 va_end(ap);
354 return __ast_device_state_changed_literal(buf, 0);
357 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
358 static void *do_devstate_changes(void *data)
360 struct state_change *cur;
362 AST_LIST_LOCK(&state_changes);
363 for(;;) {
364 /* the list lock will _always_ be held at this point in the loop */
365 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
366 if (cur) {
367 /* we got an entry, so unlock the list while we process it */
368 AST_LIST_UNLOCK(&state_changes);
369 do_state_change(cur->device);
370 free(cur);
371 AST_LIST_LOCK(&state_changes);
372 } else {
373 /* there was no entry, so atomically unlock the list and wait for
374 the condition to be signalled (returns with the lock held) */
375 ast_cond_wait(&change_pending, &state_changes.lock);
379 return NULL;
382 /*! \brief Initialize the device state engine in separate thread */
383 int ast_device_state_engine_init(void)
385 ast_cond_init(&change_pending, NULL);
386 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
387 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
388 return -1;
391 return 0;