Asterisk - The Open Source Telephony Project  21.4.1
func_lock.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007, Tilghman Lesher
5  *
6  * Tilghman Lesher <func_lock_2007@the-tilghman.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 Dialplan mutexes
22  *
23  * \author Tilghman Lesher <func_lock_2007@the-tilghman.com>
24  *
25  * \ingroup functions
26  *
27  */
28 
29 /*** MODULEINFO
30  <support_level>core</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 #include <signal.h>
36 
37 #include "asterisk/lock.h"
38 #include "asterisk/file.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/linkedlists.h"
43 #include "asterisk/astobj2.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/cli.h"
46 
47 /*** DOCUMENTATION
48  <function name="LOCK" language="en_US">
49  <synopsis>
50  Attempt to obtain a named mutex.
51  </synopsis>
52  <syntax>
53  <parameter name="lockname" required="true" />
54  </syntax>
55  <description>
56  <para>Attempts to grab a named lock exclusively, and prevents other channels from
57  obtaining the same lock. LOCK will wait for the lock to become available.
58  Returns <literal>1</literal> if the lock was obtained or <literal>0</literal> on error.</para>
59  <note><para>To avoid the possibility of a deadlock, LOCK will only attempt to
60  obtain the lock for 3 seconds if the channel already has another lock.</para></note>
61  <note>
62  <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
63  is set to <literal>no</literal>, this function can only be executed from the
64  dialplan, and not directly from external protocols.</para>
65  </note>
66  </description>
67  <see-also>
68  <ref type="function">TRYLOCK</ref>
69  <ref type="function">UNLOCK</ref>
70  </see-also>
71  </function>
72  <function name="TRYLOCK" language="en_US">
73  <synopsis>
74  Attempt to obtain a named mutex.
75  </synopsis>
76  <syntax>
77  <parameter name="lockname" required="true" />
78  </syntax>
79  <description>
80  <para>Attempts to grab a named lock exclusively, and prevents other channels
81  from obtaining the same lock. Returns <literal>1</literal> if the lock was
82  available or <literal>0</literal> otherwise.</para>
83  <note>
84  <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
85  is set to <literal>no</literal>, this function can only be executed from the
86  dialplan, and not directly from external protocols.</para>
87  </note>
88  </description>
89  <see-also>
90  <ref type="function">LOCK</ref>
91  <ref type="function">UNLOCK</ref>
92  </see-also>
93  </function>
94  <function name="UNLOCK" language="en_US">
95  <synopsis>
96  Unlocks a named mutex.
97  </synopsis>
98  <syntax>
99  <parameter name="lockname" required="true" />
100  </syntax>
101  <description>
102  <para>Unlocks a previously locked mutex. Returns <literal>1</literal> if the channel
103  had a lock or <literal>0</literal> otherwise.</para>
104  <note><para>It is generally unnecessary to unlock in a hangup routine, as any locks
105  held are automatically freed when the channel is destroyed.</para></note>
106  <note>
107  <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
108  is set to <literal>no</literal>, this function can only be executed from the
109  dialplan, and not directly from external protocols.</para>
110  </note>
111  </description>
112  <see-also>
113  <ref type="function">LOCK</ref>
114  <ref type="function">TRYLOCK</ref>
115  </see-also>
116  </function>
117  ***/
118 
119 
120 
122 
123 static void lock_free(void *data);
124 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
125 static int unloading = 0;
126 
127 static const struct ast_datastore_info lock_info = {
128  .type = "MUTEX",
129  .destroy = lock_free,
130  .chan_fixup = lock_fixup,
131 };
132 
133 struct lock_frame {
134  AST_LIST_ENTRY(lock_frame) entries;
135  ast_mutex_t mutex;
136  ast_cond_t cond;
137  /*! count is needed so if a recursive mutex exits early, we know how many times to unlock it. */
138  unsigned int count;
139  /*! Count of waiting of requesters for the named lock */
140  unsigned int requesters;
141  /*! who owns us */
143  /*! name of the lock */
144  char name[0];
145 };
146 
149  /*! Need to save channel pointer here, because during destruction, we won't have it. */
151  struct lock_frame *lock_frame;
152 };
153 
154 static void lock_free(void *data)
155 {
156  AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
157  struct channel_lock_frame *clframe;
158  AST_LIST_LOCK(oldlist);
159  while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
160  /* Only unlock if we own the lock */
161  if (clframe->channel == clframe->lock_frame->owner) {
162  ast_mutex_lock(&clframe->lock_frame->mutex);
163  clframe->lock_frame->count = 0;
164  clframe->lock_frame->owner = NULL;
165  ast_cond_signal(&clframe->lock_frame->cond);
166  ast_mutex_unlock(&clframe->lock_frame->mutex);
167  }
168  ast_free(clframe);
169  }
170  AST_LIST_UNLOCK(oldlist);
171  AST_LIST_HEAD_DESTROY(oldlist);
172  ast_free(oldlist);
173 
175 }
176 
177 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
178 {
179  struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
181  struct channel_lock_frame *clframe = NULL;
182 
183  if (!lock_store) {
184  return;
185  }
186  list = lock_store->data;
187 
188  AST_LIST_LOCK(list);
189  AST_LIST_TRAVERSE(list, clframe, list) {
190  if (clframe->lock_frame->owner == oldchan) {
191  clframe->lock_frame->owner = newchan;
192  }
193  clframe->channel = newchan;
194  }
195  AST_LIST_UNLOCK(list);
196 }
197 
198 static int get_lock(struct ast_channel *chan, char *lockname, int trylock)
199 {
200  struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
201  struct lock_frame *current;
202  struct channel_lock_frame *clframe = NULL;
204  int res = 0;
205  struct timespec timeout = { 0, };
206  struct timeval now;
207 
208  if (!lock_store) {
209  if (unloading) {
210  ast_log(LOG_ERROR, "%sLOCK has no datastore and func_lock is unloading, failing.\n",
211  trylock ? "TRY" : "");
212  return -1;
213  }
214 
215  lock_store = ast_datastore_alloc(&lock_info, NULL);
216  if (!lock_store) {
217  ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
218  return -1;
219  }
220 
221  list = ast_calloc(1, sizeof(*list));
222  if (!list) {
223  ast_log(LOG_ERROR,
224  "Unable to allocate datastore list head. %sLOCK will fail.\n",
225  trylock ? "TRY" : "");
226  ast_datastore_free(lock_store);
227  return -1;
228  }
229 
230  lock_store->data = list;
231  AST_LIST_HEAD_INIT(list);
232  ast_channel_datastore_add(chan, lock_store);
233 
234  /* We cannot unload until this channel has released the lock_store */
236  } else
237  list = lock_store->data;
238 
239  /* Lock already exists? */
241  AST_LIST_TRAVERSE(&locklist, current, entries) {
242  if (strcmp(current->name, lockname) == 0) {
243  break;
244  }
245  }
246 
247  if (!current) {
248  if (unloading) {
249  ast_log(LOG_ERROR,
250  "Lock doesn't exist whilst unloading. %sLOCK will fail.\n",
251  trylock ? "TRY" : "");
252  /* Don't bother */
254  return -1;
255  }
256 
257  /* Create new lock entry */
258  current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
259  if (!current) {
261  return -1;
262  }
263 
264  strcpy(current->name, lockname); /* SAFE */
265  if ((res = ast_mutex_init(&current->mutex))) {
266  ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
267  ast_free(current);
269  return -1;
270  }
271  if ((res = ast_cond_init(&current->cond, NULL))) {
272  ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
273  ast_mutex_destroy(&current->mutex);
274  ast_free(current);
276  return -1;
277  }
278  AST_LIST_INSERT_TAIL(&locklist, current, entries);
279  }
280  /* Add to requester list */
281  ast_mutex_lock(&current->mutex);
282  current->requesters++;
283  ast_mutex_unlock(&current->mutex);
285 
286  /* Found lock or created one - now find or create the corresponding link in the channel */
287  AST_LIST_LOCK(list);
288  AST_LIST_TRAVERSE(list, clframe, list) {
289  if (clframe->lock_frame == current) {
290  break;
291  }
292  }
293 
294  if (!clframe) {
295  if (unloading) {
296  ast_log(LOG_ERROR,
297  "Busy unloading. %sLOCK will fail.\n",
298  trylock ? "TRY" : "");
299  /* Don't bother */
300  ast_mutex_lock(&current->mutex);
301  current->requesters--;
302  ast_mutex_unlock(&current->mutex);
303  AST_LIST_UNLOCK(list);
304  return -1;
305  }
306 
307  if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
308  ast_log(LOG_ERROR,
309  "Unable to allocate channel lock frame. %sLOCK will fail.\n",
310  trylock ? "TRY" : "");
311  ast_mutex_lock(&current->mutex);
312  current->requesters--;
313  ast_mutex_unlock(&current->mutex);
314  AST_LIST_UNLOCK(list);
315  return -1;
316  }
317 
318  clframe->lock_frame = current;
319  clframe->channel = chan;
320  AST_LIST_INSERT_TAIL(list, clframe, list);
321  }
322  AST_LIST_UNLOCK(list);
323 
324  /* If we already own the lock, then we're being called recursively.
325  * Keep track of how many times that is, because we need to unlock
326  * the same amount, before we'll release this one.
327  */
328  if (current->owner == chan) {
329  /* We're not a requester, we already have it */
330  ast_mutex_lock(&current->mutex);
331  current->requesters--;
332  ast_mutex_unlock(&current->mutex);
333  current->count++;
334  return 0;
335  }
336 
337  /* Wait up to three seconds from now for LOCK. */
338  now = ast_tvnow();
339  timeout.tv_sec = now.tv_sec + 3;
340  timeout.tv_nsec = now.tv_usec * 1000;
341 
342  ast_mutex_lock(&current->mutex);
343 
344  res = 0;
345  while (!trylock && !res && current->owner) {
346  res = ast_cond_timedwait(&current->cond, &current->mutex, &timeout);
347  }
348  if (current->owner) {
349  /* timeout;
350  * trylock; or
351  * cond_timedwait failed.
352  *
353  * either way, we fail to obtain the lock.
354  */
355  res = -1;
356  } else {
357  current->owner = chan;
358  current->count++;
359  res = 0;
360  }
361  /* Remove from requester list */
362  current->requesters--;
363  if (res && unloading)
364  ast_cond_signal(&current->cond);
365  ast_mutex_unlock(&current->mutex);
366 
367  return res;
368 }
369 
370 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
371 {
372  struct ast_datastore *lock_store;
373  struct channel_lock_frame *clframe;
375 
376  if (!chan) {
377  return -1;
378  }
379 
380  lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
381  if (!lock_store) {
382  ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
383  ast_copy_string(buf, "0", len);
384  return 0;
385  }
386 
387  if (!(list = lock_store->data)) {
388  ast_debug(1, "This should NEVER happen\n");
389  ast_copy_string(buf, "0", len);
390  return 0;
391  }
392 
393  /* Find item in the channel list */
394  AST_LIST_LOCK(list);
395  AST_LIST_TRAVERSE(list, clframe, list) {
396  if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
397  break;
398  }
399  }
400  /* We never destroy anything until channel destruction, which will never
401  * happen while this routine is executing, so we don't need to hold the
402  * lock beyond this point. */
403  AST_LIST_UNLOCK(list);
404 
405  if (!clframe) {
406  /* We didn't have this lock in the first place */
407  ast_copy_string(buf, "0", len);
408  return 0;
409  }
410 
411  if (--clframe->lock_frame->count == 0) {
412  ast_mutex_lock(&clframe->lock_frame->mutex);
413  clframe->lock_frame->owner = NULL;
414  ast_cond_signal(&clframe->lock_frame->cond);
415  ast_mutex_unlock(&clframe->lock_frame->mutex);
416  }
417 
418  ast_copy_string(buf, "1", len);
419  return 0;
420 }
421 
422 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
423 {
424  if (!chan) {
425  return -1;
426  }
427  ast_autoservice_start(chan);
428  ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
429  ast_autoservice_stop(chan);
430 
431  return 0;
432 }
433 
434 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
435 {
436  if (!chan) {
437  return -1;
438  }
439  ast_autoservice_start(chan);
440  ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
441  ast_autoservice_stop(chan);
442 
443  return 0;
444 }
445 
446 static char *handle_cli_locks_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
447 {
448  int c = 0;
449  struct lock_frame* current;
450  switch (cmd) {
451  case CLI_INIT:
452  e->command = "dialplan locks show";
453  e->usage =
454  "Usage: dialplan locks show\n"
455  " List all locks known to func_lock, along with their current status.\n";
456  return NULL;
457  case CLI_GENERATE:
458  return NULL;
459  }
460 
461  ast_cli(a->fd, "func_lock locks:\n");
462  ast_cli(a->fd, "%-40s Requesters Owner\n", "Name");
464  AST_LIST_TRAVERSE(&locklist, current, entries) {
465  ast_mutex_lock(&current->mutex);
466  ast_cli(a->fd, "%-40s %-10d %s\n", current->name, current->requesters,
467  current->owner ? ast_channel_name(current->owner) : "(unlocked)");
468  ast_mutex_unlock(&current->mutex);
469  c++;
470  }
472  ast_cli(a->fd, "%d total locks listed.\n", c);
473 
474  return 0;
475 }
476 
477 static struct ast_custom_function lock_function = {
478  .name = "LOCK",
479  .read = lock_read,
480  .read_max = 2,
481 };
482 
483 static struct ast_custom_function trylock_function = {
484  .name = "TRYLOCK",
485  .read = trylock_read,
486  .read_max = 2,
487 };
488 
489 static struct ast_custom_function unlock_function = {
490  .name = "UNLOCK",
491  .read = unlock_read,
492  .read_max = 2,
493 };
494 
495 static struct ast_cli_entry cli_locks_show = AST_CLI_DEFINE(handle_cli_locks_show, "List func_lock locks.");
496 
497 static int unload_module(void)
498 {
499  struct lock_frame *current;
500 
501  /* Module flag */
502  unloading = 1;
503 
504  /* Make it impossible for new requesters to be added
505  * NOTE: channels could already be in get_lock() */
506  ast_custom_function_unregister(&lock_function);
507  ast_custom_function_unregister(&trylock_function);
508 
509  ast_cli_unregister(&cli_locks_show);
510 
512  while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
513  int warned = 0;
514  ast_mutex_lock(&current->mutex);
515  while (current->owner || current->requesters) {
516  if (!warned) {
517  ast_log(LOG_WARNING, "Waiting for %d requesters for %s lock %s.\n",
518  current->requesters, current->owner ? "locked" : "unlocked",
519  current->name);
520  warned = 1;
521  }
522  /* either the mutex is locked, or other parties are currently in get_lock,
523  * we need to wait for all of those to clear first */
524  ast_cond_wait(&current->cond, &current->mutex);
525  }
526  ast_mutex_unlock(&current->mutex);
527  /* At this point we know:
528  * 1. the lock has been released,
529  * 2. there are no requesters (nor should any be able to sneak in).
530  */
531  ast_mutex_destroy(&current->mutex);
532  ast_cond_destroy(&current->cond);
533  ast_free(current);
534  }
537 
538  /* At this point we can safely stop access to UNLOCK */
539  ast_custom_function_unregister(&unlock_function);
540 
541  return 0;
542 }
543 
544 static int load_module(void)
545 {
546  int res = ast_custom_function_register_escalating(&lock_function, AST_CFE_READ);
547  res |= ast_custom_function_register_escalating(&trylock_function, AST_CFE_READ);
548  res |= ast_custom_function_register_escalating(&unlock_function, AST_CFE_READ);
549  res |= ast_cli_register(&cli_locks_show);
550 
551  return res;
552 }
553 
554 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
Main Channel structure associated with a channel.
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:173
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
int ast_cli_unregister(struct ast_cli_entry *e)
Unregisters a command or an array of commands.
Definition: main/cli.c:2432
descriptor for a cli entry.
Definition: cli.h:171
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
Structure for a data store type.
Definition: datastore.h:31
unsigned int requesters
Definition: func_lock.c:140
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Structure for a data store object.
Definition: datastore.h:64
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
Utility functions.
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:653
#define ast_cli_register(e)
Registers a command or an array of commands.
Definition: cli.h:256
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition: pbx.h:1567
struct ast_module * self
Definition: module.h:356
General Asterisk PBX channel definitions.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
A set of macros to manage forward-linked lists.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:291
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:457
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:626
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
char name[0]
Definition: func_lock.c:144
const char * usage
Definition: cli.h:177
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:483
void * data
Definition: datastore.h:66
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
struct ast_channel * owner
Definition: func_lock.c:142
struct ast_channel * channel
Definition: func_lock.c:150
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
unsigned int count
Definition: func_lock.c:138
Structure for mutex and tracking information.
Definition: lock.h:135