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