METERMAIDS:
[asterisk/asterisk.git] / devicestate.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Device state management
22  *
23  * \author Mark Spencer <markster@digium.com> 
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdio.h>
35
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"
45
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 };
56
57 /*! \brief  A device state provider (not a channel) */
58 struct devstate_prov {
59         char label[40];
60         ast_devstate_prov_cb_type callback;
61         AST_LIST_ENTRY(devstate_prov) list;
62 };
63
64 /*! \brief A list of providers */
65 static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov);
66
67 /*! \brief  A device state watcher (callback) */
68 struct devstate_cb {
69         void *data;
70         ast_devstate_cb_type callback;
71         AST_LIST_ENTRY(devstate_cb) list;
72 };
73
74 /*! \brief A device state watcher list */
75 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
76
77 struct state_change {
78         AST_LIST_ENTRY(state_change) list;
79         char device[1];
80 };
81
82 /*! \brief The state change queue. State changes are queued
83         for processing by a separate thread */
84 static AST_LIST_HEAD_STATIC(state_changes, state_change);
85
86 /*! \brief The device state change notification thread */
87 static pthread_t change_thread = AST_PTHREADT_NULL;
88
89 /*! \brief Flag for the queue */
90 static ast_cond_t change_pending;
91
92 /* Forward declarations */
93 static int getproviderstate(const char *provider, const char *address);
94
95 /*! \brief Find devicestate as text message for output */
96 const char *devstate2str(int devstate) 
97 {
98         return devstatestring[devstate];
99 }
100
101 /*! \brief Find out if device is active in a call or not 
102         \note find channels with the device's name in it
103         This function is only used for channels that does not implement 
104         devicestate natively
105 */
106 int ast_parse_device_state(const char *device)
107 {
108         struct ast_channel *chan;
109         char match[AST_CHANNEL_NAME];
110         int res;
111
112         ast_copy_string(match, device, sizeof(match)-1);
113         strcat(match, "-");
114         chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
115
116         if (!chan)
117                 return AST_DEVICE_UNKNOWN;
118
119         if (chan->_state == AST_STATE_RINGING)
120                 res = AST_DEVICE_RINGING;
121         else
122                 res = AST_DEVICE_INUSE;
123         
124         ast_channel_unlock(chan);
125
126         return res;
127 }
128
129 /*! \brief Check device state through channel specific function or generic function */
130 int ast_device_state(const char *device)
131 {
132         char *buf;
133         char *number;
134         const struct ast_channel_tech *chan_tech;
135         int res = 0;
136         /*! \brief Channel driver that provides device state */
137         char *tech;
138         /*! \brief Another provider of device state */
139         char *provider = NULL;
140         
141         buf = ast_strdupa(device);
142         tech = strsep(&buf, "/");
143         number = buf;
144         if (!number) {
145                 provider = strsep(&tech, ":");
146                 if (!provider)
147                         return AST_DEVICE_INVALID;
148                 /* We have a provider */
149                 number = tech;
150                 tech = NULL;
151         }
152
153         if (provider)  {
154                 if(option_debug > 2)
155                         ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
156                 return getproviderstate(provider, number);
157         }
158         if (option_debug > 3)
159                 ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
160
161         chan_tech = ast_get_channel_tech(tech);
162         if (!chan_tech)
163                 return AST_DEVICE_INVALID;
164
165         if (!chan_tech->devicestate)    /* Does the channel driver support device state notification? */
166                 return ast_parse_device_state(device);  /* No, try the generic function */
167         else {
168                 res = chan_tech->devicestate(number);   /* Ask the channel driver for device state */
169                 if (res == AST_DEVICE_UNKNOWN) {
170                         res = ast_parse_device_state(device);
171                         /* at this point we know the device exists, but the channel driver
172                            could not give us a state; if there is no channel state available,
173                            it must be 'not in use'
174                         */
175                         if (res == AST_DEVICE_UNKNOWN)
176                                 res = AST_DEVICE_NOT_INUSE;
177                         return res;
178                 } else
179                         return res;
180         }
181 }
182
183 /*! \brief Add device state provider */
184 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
185 {
186         struct devstate_prov *devprov;
187
188         if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
189                 return -1;
190
191         devprov->callback = callback;
192         ast_copy_string(devprov->label, label, sizeof(devprov->label));
193
194         AST_LIST_LOCK(&devstate_provs);
195         AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
196         AST_LIST_UNLOCK(&devstate_provs);
197
198         return 0;
199 }
200
201 /*! \brief Remove device state provider */
202 void ast_devstate_prov_del(const char *label)
203 {
204         struct devstate_prov *devcb;
205
206         AST_LIST_LOCK(&devstate_provs);
207         AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
208                 if (!strcasecmp(devcb->label, label)) {
209                         AST_LIST_REMOVE_CURRENT(&devstate_provs, list);
210                         free(devcb);
211                         break;
212                 }
213         }
214         AST_LIST_TRAVERSE_SAFE_END;
215         AST_LIST_UNLOCK(&devstate_provs);
216 }
217
218 /*! \brief Get provider device state */
219 static int getproviderstate(const char *provider, const char *address)
220 {
221         struct devstate_prov *devprov;
222         int res = AST_DEVICE_INVALID;
223
224
225         AST_LIST_LOCK(&devstate_provs);
226         AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) {
227                 if(option_debug > 4)
228                         ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
229
230                 if (!strcasecmp(devprov->label, provider)) {
231                         res = devprov->callback(address);
232                         break;
233                 }
234         }
235         AST_LIST_TRAVERSE_SAFE_END;
236         AST_LIST_UNLOCK(&devstate_provs);
237         return res;
238 }
239
240 /*! \brief Add device state watcher */
241 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
242 {
243         struct devstate_cb *devcb;
244
245         if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
246                 return -1;
247
248         devcb->data = data;
249         devcb->callback = callback;
250
251         AST_LIST_LOCK(&devstate_cbs);
252         AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
253         AST_LIST_UNLOCK(&devstate_cbs);
254
255         return 0;
256 }
257
258 /*! \brief Remove device state watcher */
259 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
260 {
261         struct devstate_cb *devcb;
262
263         AST_LIST_LOCK(&devstate_cbs);
264         AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
265                 if ((devcb->callback == callback) && (devcb->data == data)) {
266                         AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
267                         free(devcb);
268                         break;
269                 }
270         }
271         AST_LIST_TRAVERSE_SAFE_END;
272         AST_LIST_UNLOCK(&devstate_cbs);
273 }
274
275 /*! \brief Notify callback watchers of change, and notify PBX core for hint updates
276         Normally executed within a separate thread
277 */
278 static void do_state_change(const char *device)
279 {
280         int state;
281         struct devstate_cb *devcb;
282
283         state = ast_device_state(device);
284         if (option_debug > 2)
285                 ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
286
287         AST_LIST_LOCK(&devstate_cbs);
288         AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
289                 devcb->callback(device, state, devcb->data);
290         AST_LIST_UNLOCK(&devstate_cbs);
291
292         ast_hint_state_changed(device);
293 }
294
295 static int __ast_device_state_changed_literal(char *buf)
296 {
297         char *device, *tmp;
298         struct state_change *change;
299
300         if (option_debug > 2)
301                 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
302
303         device = buf;
304         if ((tmp = strrchr(device, '-')))
305                 *tmp = '\0';
306
307         
308
309         if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
310                 /* we could not allocate a change struct, or */
311                 /* there is no background thread, so process the change now */
312                 do_state_change(device);
313         } else {
314                 /* queue the change */
315                 strcpy(change->device, device);
316                 AST_LIST_LOCK(&state_changes);
317                 AST_LIST_INSERT_TAIL(&state_changes, change, list);
318                 if (AST_LIST_FIRST(&state_changes) == change)
319                         /* the list was empty, signal the thread */
320                         ast_cond_signal(&change_pending);
321                 AST_LIST_UNLOCK(&state_changes);
322         }
323
324         return 1;
325 }
326
327 int ast_device_state_changed_literal(const char *dev)
328 {
329         char *buf;
330         buf = ast_strdupa(dev);
331         return __ast_device_state_changed_literal(buf);
332 }
333
334 /*! \brief Accept change notification, add it to change queue */
335 int ast_device_state_changed(const char *fmt, ...) 
336 {
337         char buf[AST_MAX_EXTENSION];
338         va_list ap;
339
340         va_start(ap, fmt);
341         vsnprintf(buf, sizeof(buf), fmt, ap);
342         va_end(ap);
343         return __ast_device_state_changed_literal(buf);
344 }
345
346 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
347 static void *do_devstate_changes(void *data)
348 {
349         struct state_change *cur;
350
351         AST_LIST_LOCK(&state_changes);
352         for(;;) {
353                 /* the list lock will _always_ be held at this point in the loop */
354                 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
355                 if (cur) {
356                         /* we got an entry, so unlock the list while we process it */
357                         AST_LIST_UNLOCK(&state_changes);
358                         do_state_change(cur->device);
359                         free(cur);
360                         AST_LIST_LOCK(&state_changes);
361                 } else {
362                         /* there was no entry, so atomically unlock the list and wait for
363                            the condition to be signalled (returns with the lock held) */
364                         ast_cond_wait(&change_pending, &state_changes.lock);
365                 }
366         }
367
368         return NULL;
369 }
370
371 /*! \brief Initialize the device state engine in separate thread */
372 int ast_device_state_engine_init(void)
373 {
374         ast_cond_init(&change_pending, NULL);
375         if (ast_pthread_create(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
376                 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
377                 return -1;
378         }
379
380         return 0;
381 }