ensure that dependencies are rebuilt after 'make update' so that builds don't break...
[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 <sys/types.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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/options.h"
44
45 static const char *devstatestring[] = {
46         /* 0 AST_DEVICE_UNKNOWN */      "Unknown",      /* Valid, but unknown state */
47         /* 1 AST_DEVICE_NOT_INUSE */    "Not in use",   /* Not used */
48         /* 2 AST_DEVICE IN USE */       "In use",       /* In use */
49         /* 3 AST_DEVICE_BUSY */         "Busy",         /* Busy */
50         /* 4 AST_DEVICE_INVALID */      "Invalid",      /* Invalid - not known to Asterisk */
51         /* 5 AST_DEVICE_UNAVAILABLE */  "Unavailable",  /* Unavailable (not registred) */
52         /* 6 AST_DEVICE_RINGING */      "Ringing"       /* Ring, ring, ring */
53 };
54
55 /* ast_devstate_cb: A device state watcher (callback) */
56 struct devstate_cb {
57         void *data;
58         ast_devstate_cb_type callback;
59         AST_LIST_ENTRY(devstate_cb) list;
60 };
61
62 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
63
64 struct state_change {
65         AST_LIST_ENTRY(state_change) list;
66         char device[1];
67 };
68
69 static AST_LIST_HEAD_STATIC(state_changes, state_change);
70
71 static pthread_t change_thread = AST_PTHREADT_NULL;
72 static ast_cond_t change_pending;
73
74 /*! \brief Find devicestate as text message for output */
75 const char *devstate2str(int devstate) 
76 {
77         return devstatestring[devstate];
78 }
79
80 /*! \brief Find out if device is active in a call or not */
81 int ast_parse_device_state(const char *device)
82 {
83         struct ast_channel *chan;
84         char match[AST_CHANNEL_NAME];
85         int res;
86
87         ast_copy_string(match, device, sizeof(match)-1);
88         strcat(match, "-");
89         chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
90
91         if (!chan)
92                 return AST_DEVICE_UNKNOWN;
93
94         if (chan->_state == AST_STATE_RINGING)
95                 res = AST_DEVICE_RINGING;
96         else
97                 res = AST_DEVICE_INUSE;
98         
99         ast_mutex_unlock(&chan->lock);
100
101         return res;
102 }
103
104 /*! \brief Check device state through channel specific function or generic function */
105 int ast_device_state(const char *device)
106 {
107         char *buf;
108         char *tech;
109         char *number;
110         const struct ast_channel_tech *chan_tech;
111         int res = 0;
112         
113         buf = ast_strdupa(device);
114         tech = strsep(&buf, "/");
115         number = buf;
116         if (!number)
117                 return AST_DEVICE_INVALID;
118                 
119         chan_tech = ast_get_channel_tech(tech);
120         if (!chan_tech)
121                 return AST_DEVICE_INVALID;
122
123         if (!chan_tech->devicestate)    /* Does the channel driver support device state notification? */
124                 return ast_parse_device_state(device);  /* No, try the generic function */
125         else {
126                 res = chan_tech->devicestate(number);   /* Ask the channel driver for device state */
127                 if (res == AST_DEVICE_UNKNOWN) {
128                         res = ast_parse_device_state(device);
129                         /* at this point we know the device exists, but the channel driver
130                            could not give us a state; if there is no channel state available,
131                            it must be 'not in use'
132                         */
133                         if (res == AST_DEVICE_UNKNOWN)
134                                 res = AST_DEVICE_NOT_INUSE;
135                         return res;
136                 } else
137                         return res;
138         }
139 }
140
141 /*! \brief Add device state watcher */
142 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
143 {
144         struct devstate_cb *devcb;
145
146         if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
147                 return -1;
148
149         devcb->data = data;
150         devcb->callback = callback;
151
152         AST_LIST_LOCK(&devstate_cbs);
153         AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
154         AST_LIST_UNLOCK(&devstate_cbs);
155
156         return 0;
157 }
158
159 /*! \brief Remove device state watcher */
160 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
161 {
162         struct devstate_cb *devcb;
163
164         AST_LIST_LOCK(&devstate_cbs);
165         AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
166                 if ((devcb->callback == callback) && (devcb->data == data)) {
167                         AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
168                         free(devcb);
169                         break;
170                 }
171         }
172         AST_LIST_TRAVERSE_SAFE_END;
173         AST_LIST_UNLOCK(&devstate_cbs);
174 }
175
176 /*! \brief Notify callback watchers of change, and notify PBX core for hint updates */
177 static void do_state_change(const char *device)
178 {
179         int state;
180         struct devstate_cb *devcb;
181
182         state = ast_device_state(device);
183         if (option_debug > 2)
184                 ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
185
186         AST_LIST_LOCK(&devstate_cbs);
187         AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
188                 devcb->callback(device, state, devcb->data);
189         AST_LIST_UNLOCK(&devstate_cbs);
190
191         ast_hint_state_changed(device);
192 }
193
194 static int __ast_device_state_changed_literal(char *buf)
195 {
196         char *device, *tmp;
197         struct state_change *change;
198
199         device = buf;
200         if ((tmp = strrchr(device, '-')))
201                 *tmp = '\0';
202
203         if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
204                 /* we could not allocate a change struct, or */
205                 /* there is no background thread, so process the change now */
206                 do_state_change(device);
207         } else {
208                 /* queue the change */
209                 strcpy(change->device, device);
210                 AST_LIST_LOCK(&state_changes);
211                 AST_LIST_INSERT_TAIL(&state_changes, change, list);
212                 if (AST_LIST_FIRST(&state_changes) == change)
213                         /* the list was empty, signal the thread */
214                         ast_cond_signal(&change_pending);
215                 AST_LIST_UNLOCK(&state_changes);
216         }
217
218         return 1;
219 }
220
221 int ast_device_state_changed_literal(const char *dev)
222 {
223         char *buf;
224         buf = ast_strdupa(dev);
225         return __ast_device_state_changed_literal(buf);
226 }
227
228 /*! \brief Accept change notification, add it to change queue */
229 int ast_device_state_changed(const char *fmt, ...) 
230 {
231         char buf[AST_MAX_EXTENSION];
232         va_list ap;
233
234         va_start(ap, fmt);
235         vsnprintf(buf, sizeof(buf), fmt, ap);
236         va_end(ap);
237         return __ast_device_state_changed_literal(buf);
238 }
239
240 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
241 static void *do_devstate_changes(void *data)
242 {
243         struct state_change *cur;
244
245         AST_LIST_LOCK(&state_changes);
246         for(;;) {
247                 /* the list lock will _always_ be held at this point in the loop */
248                 cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
249                 if (cur) {
250                         /* we got an entry, so unlock the list while we process it */
251                         AST_LIST_UNLOCK(&state_changes);
252                         do_state_change(cur->device);
253                         free(cur);
254                         AST_LIST_LOCK(&state_changes);
255                 } else {
256                         /* there was no entry, so atomically unlock the list and wait for
257                            the condition to be signalled (returns with the lock held) */
258                         ast_cond_wait(&change_pending, &state_changes.lock);
259                 }
260         }
261
262         return NULL;
263 }
264
265 /*! \brief Initialize the device state engine in separate thread */
266 int ast_device_state_engine_init(void)
267 {
268         ast_cond_init(&change_pending, NULL);
269         if (ast_pthread_create(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
270                 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
271                 return -1;
272         }
273
274         return 0;
275 }