devicestate.c 7.4 KB

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