Add support to see if a peer has someone on hold, and fix a small bug with getting...
[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         /* 7 AST_DEVICE_RINGINUSE */    "Ring+Inuse",   /*!< Ring and in use */
56         /* 8 AST_DEVICE_ONHOLD */       "On Hold"       /*!< On Hold */
57 };
58
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;
64 };
65
66 /*! \brief A list of providers */
67 static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov);
68
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;
74 };
75
76 /*! \brief A device state watcher list */
77 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
78
79 struct state_change {
80         AST_LIST_ENTRY(state_change) list;
81         char device[1];
82 };
83
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);
87
88 /*! \brief The device state change notification thread */
89 static pthread_t change_thread = AST_PTHREADT_NULL;
90
91 /*! \brief Flag for the queue */
92 static ast_cond_t change_pending;
93
94 /* Forward declarations */
95 static int getproviderstate(const char *provider, const char *address);
96
97 /*! \brief Find devicestate as text message for output */
98 const char *devstate2str(int devstate) 
99 {
100         return devstatestring[devstate];
101 }
102
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
107 */
108 int ast_parse_device_state(const char *device)
109 {
110         struct ast_channel *chan;
111         char match[AST_CHANNEL_NAME];
112         int res;
113
114         ast_copy_string(match, device, sizeof(match)-1);
115         strcat(match, "-");
116         chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
117
118         if (!chan)
119                 return AST_DEVICE_UNKNOWN;
120
121         if (chan->_state == AST_STATE_RINGING)
122                 res = AST_DEVICE_RINGING;
123         else
124                 res = AST_DEVICE_INUSE;
125         
126         ast_channel_unlock(chan);
127
128         return res;
129 }
130
131 /*! \brief Check device state through channel specific function or generic function */
132 int ast_device_state(const char *device)
133 {
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;
142         
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;
153         }
154
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);
159         }
160         if (option_debug > 3)
161                 ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
162
163         chan_tech = ast_get_channel_tech(tech);
164         if (!chan_tech)
165                 return AST_DEVICE_INVALID;
166
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'
176                         */
177                         if (res == AST_DEVICE_UNKNOWN)
178                                 res = AST_DEVICE_NOT_INUSE;
179                         return res;
180                 } else
181                         return res;
182         }
183 }
184
185 /*! \brief Add device state provider */
186 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
187 {
188         struct devstate_prov *devprov;
189
190         if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
191                 return -1;
192
193         devprov->callback = callback;
194         ast_copy_string(devprov->label, label, sizeof(devprov->label));
195
196         AST_LIST_LOCK(&devstate_provs);
197         AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
198         AST_LIST_UNLOCK(&devstate_provs);
199
200         return 0;
201 }
202
203 /*! \brief Remove device state provider */
204 void ast_devstate_prov_del(const char *label)
205 {
206         struct devstate_prov *devcb;
207
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;
214                 }
215         }
216         AST_LIST_TRAVERSE_SAFE_END;
217         AST_LIST_UNLOCK(&devstate_provs);
218 }
219
220 /*! \brief Get provider device state */
221 static int getproviderstate(const char *provider, const char *address)
222 {
223         struct devstate_prov *devprov;
224         int res = AST_DEVICE_INVALID;
225
226
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);
231
232                 if (!strcasecmp(devprov->label, provider)) {
233                         res = devprov->callback(address);
234                         break;
235                 }
236         }
237         AST_LIST_TRAVERSE_SAFE_END;
238         AST_LIST_UNLOCK(&devstate_provs);
239         return res;
240 }
241
242 /*! \brief Add device state watcher */
243 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
244 {
245         struct devstate_cb *devcb;
246
247         if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
248                 return -1;
249
250         devcb->data = data;
251         devcb->callback = callback;
252
253         AST_LIST_LOCK(&devstate_cbs);
254         AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
255         AST_LIST_UNLOCK(&devstate_cbs);
256
257         return 0;
258 }
259
260 /*! \brief Remove device state watcher */
261 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
262 {
263         struct devstate_cb *devcb;
264
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;
271                 }
272         }
273         AST_LIST_TRAVERSE_SAFE_END;
274         AST_LIST_UNLOCK(&devstate_cbs);
275 }
276
277 /*! \brief Notify callback watchers of change, and notify PBX core for hint updates
278         Normally executed within a separate thread
279 */
280 static void do_state_change(const char *device)
281 {
282         int state;
283         struct devstate_cb *devcb;
284
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));
288
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);
293
294         ast_hint_state_changed(device);
295 }
296
297 static int __ast_device_state_changed_literal(char *buf)
298 {
299         char *device, *tmp;
300         struct state_change *change;
301
302         if (option_debug > 2)
303                 ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf);
304
305         device = buf;
306         if ((tmp = strrchr(device, '-')))
307                 *tmp = '\0';
308
309         
310
311         if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
312                 /* we could not allocate a change struct, or */
313                 /* there is no background thread, so process the change now */
314                 do_state_change(device);
315         } else {
316                 /* queue the change */
317                 strcpy(change->device, device);
318                 AST_LIST_LOCK(&state_changes);
319                 AST_LIST_INSERT_TAIL(&state_changes, change, list);
320                 if (AST_LIST_FIRST(&state_changes) == change)
321                         /* the list was empty, signal the thread */
322                         ast_cond_signal(&change_pending);
323                 AST_LIST_UNLOCK(&state_changes);
324         }
325
326         return 1;
327 }
328
329 int ast_device_state_changed_literal(const char *dev)
330 {
331         char *buf;
332         buf = ast_strdupa(dev);
333         return __ast_device_state_changed_literal(buf);
334 }
335
336 /*! \brief Accept change notification, add it to change queue */
337 int ast_device_state_changed(const char *fmt, ...) 
338 {
339         char buf[AST_MAX_EXTENSION];
340         va_list ap;
341
342         va_start(ap, fmt);
343         vsnprintf(buf, sizeof(buf), fmt, ap);
344         va_end(ap);
345         return __ast_device_state_changed_literal(buf);
346 }
347
348 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
349 static void *do_devstate_changes(void *data)
350 {
351         struct state_change *cur;
352
353         AST_LIST_LOCK(&state_changes);
354         for(;;) {
355                 /* the list lock will _always_ be held at this point in the loop */
356                 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
357                 if (cur) {
358                         /* we got an entry, so unlock the list while we process it */
359                         AST_LIST_UNLOCK(&state_changes);
360                         do_state_change(cur->device);
361                         free(cur);
362                         AST_LIST_LOCK(&state_changes);
363                 } else {
364                         /* there was no entry, so atomically unlock the list and wait for
365                            the condition to be signalled (returns with the lock held) */
366                         ast_cond_wait(&change_pending, &state_changes.lock);
367                 }
368         }
369
370         return NULL;
371 }
372
373 /*! \brief Initialize the device state engine in separate thread */
374 int ast_device_state_engine_init(void)
375 {
376         ast_cond_init(&change_pending, NULL);
377         if (ast_pthread_create(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
378                 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
379                 return -1;
380         }
381
382         return 0;
383 }