Asterisk - The Open Source Telephony Project  GIT-master-44aef04
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 
46 /*** DOCUMENTATION
47  <function name="LOCK" language="en_US">
48  <synopsis>
49  Attempt to obtain a named mutex.
50  </synopsis>
51  <syntax>
52  <parameter name="lockname" required="true" />
53  </syntax>
54  <description>
55  <para>Attempts to grab a named lock exclusively, and prevents other channels from
56  obtaining the same lock. LOCK will wait for the lock to become available.
57  Returns <literal>1</literal> if the lock was obtained or <literal>0</literal> on error.</para>
58  <note><para>To avoid the possibility of a deadlock, LOCK will only attempt to
59  obtain the lock for 3 seconds if the channel already has another lock.</para></note>
60  <note>
61  <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
62  is set to <literal>no</literal>, this function can only be executed from the
63  dialplan, and not directly from external protocols.</para>
64  </note>
65  </description>
66  </function>
67  <function name="TRYLOCK" language="en_US">
68  <synopsis>
69  Attempt to obtain a named mutex.
70  </synopsis>
71  <syntax>
72  <parameter name="lockname" required="true" />
73  </syntax>
74  <description>
75  <para>Attempts to grab a named lock exclusively, and prevents other channels
76  from obtaining the same lock. Returns <literal>1</literal> if the lock was
77  available or <literal>0</literal> otherwise.</para>
78  <note>
79  <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
80  is set to <literal>no</literal>, this function can only be executed from the
81  dialplan, and not directly from external protocols.</para>
82  </note>
83  </description>
84  </function>
85  <function name="UNLOCK" language="en_US">
86  <synopsis>
87  Unlocks a named mutex.
88  </synopsis>
89  <syntax>
90  <parameter name="lockname" required="true" />
91  </syntax>
92  <description>
93  <para>Unlocks a previously locked mutex. Returns <literal>1</literal> if the channel
94  had a lock or <literal>0</literal> otherwise.</para>
95  <note><para>It is generally unnecessary to unlock in a hangup routine, as any locks
96  held are automatically freed when the channel is destroyed.</para></note>
97  <note>
98  <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
99  is set to <literal>no</literal>, this function can only be executed from the
100  dialplan, and not directly from external protocols.</para>
101  </note>
102  </description>
103  </function>
104  ***/
105 
106 
107 
109 
110 static void lock_free(void *data);
111 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
112 static int unloading = 0;
113 
114 static const struct ast_datastore_info lock_info = {
115  .type = "MUTEX",
116  .destroy = lock_free,
117  .chan_fixup = lock_fixup,
118 };
119 
120 struct lock_frame {
124  /*! count is needed so if a recursive mutex exits early, we know how many times to unlock it. */
125  unsigned int count;
126  /*! Count of waiting of requesters for the named lock */
127  unsigned int requesters;
128  /*! who owns us */
130  /*! name of the lock */
131  char name[0];
132 };
133 
136  /*! Need to save channel pointer here, because during destruction, we won't have it. */
139 };
140 
141 static void lock_free(void *data)
142 {
143  AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
144  struct channel_lock_frame *clframe;
145  AST_LIST_LOCK(oldlist);
146  while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
147  /* Only unlock if we own the lock */
148  if (clframe->channel == clframe->lock_frame->owner) {
149  ast_mutex_lock(&clframe->lock_frame->mutex);
150  clframe->lock_frame->count = 0;
151  clframe->lock_frame->owner = NULL;
152  ast_cond_signal(&clframe->lock_frame->cond);
153  ast_mutex_unlock(&clframe->lock_frame->mutex);
154  }
155  ast_free(clframe);
156  }
157  AST_LIST_UNLOCK(oldlist);
158  AST_LIST_HEAD_DESTROY(oldlist);
159  ast_free(oldlist);
160 }
161 
162 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
163 {
164  struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
166  struct channel_lock_frame *clframe = NULL;
167 
168  if (!lock_store) {
169  return;
170  }
171  list = lock_store->data;
172 
174  AST_LIST_TRAVERSE(list, clframe, list) {
175  if (clframe->lock_frame->owner == oldchan) {
176  clframe->lock_frame->owner = newchan;
177  }
178  clframe->channel = newchan;
179  }
181 }
182 
183 static int get_lock(struct ast_channel *chan, char *lockname, int trylock)
184 {
185  struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
186  struct lock_frame *current;
187  struct channel_lock_frame *clframe = NULL;
189  int res = 0;
190  struct timespec timeout = { 0, };
191  struct timeval now;
192 
193  if (!lock_store) {
194  ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", ast_channel_name(chan));
195  lock_store = ast_datastore_alloc(&lock_info, NULL);
196  if (!lock_store) {
197  ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
198  return -1;
199  }
200 
201  list = ast_calloc(1, sizeof(*list));
202  if (!list) {
204  "Unable to allocate datastore list head. %sLOCK will fail.\n",
205  trylock ? "TRY" : "");
206  ast_datastore_free(lock_store);
207  return -1;
208  }
209 
210  lock_store->data = list;
211  AST_LIST_HEAD_INIT(list);
212  ast_channel_datastore_add(chan, lock_store);
213  } else
214  list = lock_store->data;
215 
216  /* Lock already exists? */
218  AST_LIST_TRAVERSE(&locklist, current, entries) {
219  if (strcmp(current->name, lockname) == 0) {
220  break;
221  }
222  }
223 
224  if (!current) {
225  if (unloading) {
226  /* Don't bother */
228  return -1;
229  }
230 
231  /* Create new lock entry */
232  current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
233  if (!current) {
235  return -1;
236  }
237 
238  strcpy(current->name, lockname); /* SAFE */
239  if ((res = ast_mutex_init(&current->mutex))) {
240  ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
241  ast_free(current);
243  return -1;
244  }
245  if ((res = ast_cond_init(&current->cond, NULL))) {
246  ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
247  ast_mutex_destroy(&current->mutex);
248  ast_free(current);
250  return -1;
251  }
252  current->requesters = 0;
254  }
255  /* Add to requester list */
256  ast_mutex_lock(&current->mutex);
257  current->requesters++;
258  ast_mutex_unlock(&current->mutex);
260 
261  /* Found lock or created one - now find or create the corresponding link in the channel */
262  AST_LIST_LOCK(list);
263  AST_LIST_TRAVERSE(list, clframe, list) {
264  if (clframe->lock_frame == current) {
265  break;
266  }
267  }
268 
269  if (!clframe) {
270  if (unloading) {
271  /* Don't bother */
272  AST_LIST_UNLOCK(list);
273  return -1;
274  }
275 
276  if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
278  "Unable to allocate channel lock frame. %sLOCK will fail.\n",
279  trylock ? "TRY" : "");
280  AST_LIST_UNLOCK(list);
281  return -1;
282  }
283 
284  clframe->lock_frame = current;
285  clframe->channel = chan;
286  AST_LIST_INSERT_TAIL(list, clframe, list);
287  }
288  AST_LIST_UNLOCK(list);
289 
290  /* If we already own the lock, then we're being called recursively.
291  * Keep track of how many times that is, because we need to unlock
292  * the same amount, before we'll release this one.
293  */
294  if (current->owner == chan) {
295  /* We're not a requester, we already have it */
296  ast_mutex_lock(&current->mutex);
297  current->requesters--;
298  ast_mutex_unlock(&current->mutex);
299  current->count++;
300  return 0;
301  }
302 
303  /* Wait up to three seconds from now for LOCK. */
304  now = ast_tvnow();
305  timeout.tv_sec = now.tv_sec + 3;
306  timeout.tv_nsec = now.tv_usec * 1000;
307 
308  ast_mutex_lock(&current->mutex);
309 
310  res = 0;
311  while (!trylock && !res && current->owner) {
312  res = ast_cond_timedwait(&current->cond, &current->mutex, &timeout);
313  }
314  if (current->owner) {
315  /* timeout;
316  * trylock; or
317  * cond_timedwait failed.
318  *
319  * either way, we fail to obtain the lock.
320  */
321  res = -1;
322  } else {
323  current->owner = chan;
324  current->count++;
325  res = 0;
326  }
327  /* Remove from requester list */
328  current->requesters--;
329  if (res && unloading)
330  ast_cond_signal(&current->cond);
331  ast_mutex_unlock(&current->mutex);
332 
333  return res;
334 }
335 
336 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
337 {
338  struct ast_datastore *lock_store;
339  struct channel_lock_frame *clframe;
341 
342  if (!chan) {
343  return -1;
344  }
345 
346  lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
347  if (!lock_store) {
348  ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
349  ast_copy_string(buf, "0", len);
350  return 0;
351  }
352 
353  if (!(list = lock_store->data)) {
354  ast_debug(1, "This should NEVER happen\n");
355  ast_copy_string(buf, "0", len);
356  return 0;
357  }
358 
359  /* Find item in the channel list */
361  AST_LIST_TRAVERSE(list, clframe, list) {
362  if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
363  break;
364  }
365  }
366  /* We never destroy anything until channel destruction, which will never
367  * happen while this routine is executing, so we don't need to hold the
368  * lock beyond this point. */
370 
371  if (!clframe) {
372  /* We didn't have this lock in the first place */
373  ast_copy_string(buf, "0", len);
374  return 0;
375  }
376 
377  if (--clframe->lock_frame->count == 0) {
378  ast_mutex_lock(&clframe->lock_frame->mutex);
379  clframe->lock_frame->owner = NULL;
380  ast_cond_signal(&clframe->lock_frame->cond);
381  ast_mutex_unlock(&clframe->lock_frame->mutex);
382  }
383 
384  ast_copy_string(buf, "1", len);
385  return 0;
386 }
387 
388 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
389 {
390  if (!chan) {
391  return -1;
392  }
393  ast_autoservice_start(chan);
394  ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
395  ast_autoservice_stop(chan);
396 
397  return 0;
398 }
399 
400 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
401 {
402  if (!chan) {
403  return -1;
404  }
405  ast_autoservice_start(chan);
406  ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
407  ast_autoservice_stop(chan);
408 
409  return 0;
410 }
411 
413  .name = "LOCK",
414  .read = lock_read,
415  .read_max = 2,
416 };
417 
419  .name = "TRYLOCK",
420  .read = trylock_read,
421  .read_max = 2,
422 };
423 
425  .name = "UNLOCK",
426  .read = unlock_read,
427  .read_max = 2,
428 };
429 
430 static int unload_module(void)
431 {
432  struct lock_frame *current;
433 
434  /* Module flag */
435  unloading = 1;
436 
437  /* Make it impossible for new requesters to be added
438  * NOTE: channels could already be in get_lock() */
439  ast_custom_function_unregister(&lock_function);
440  ast_custom_function_unregister(&trylock_function);
441 
443  AST_LIST_TRAVERSE(&locklist, current, entries) {
444  ast_mutex_lock(&current->mutex);
445  while (current->owner || current->requesters) {
446  /* either the mutex is locked, or other parties are currently in get_lock,
447  * we need to wait for all of those to clear first */
448  ast_cond_wait(&current->cond, &current->mutex);
449  }
450  ast_mutex_unlock(&current->mutex);
451  /* At this point we know:
452  * 1. the lock has been released,
453  * 2. there are no requesters (nor should any be able to sneak in).
454  */
455  ast_mutex_destroy(&current->mutex);
456  ast_cond_destroy(&current->cond);
457  ast_free(current);
458  }
461 
462  /* At this point we can safely stop access to UNLOCK */
463  ast_custom_function_unregister(&unlock_function);
464 
465  return 0;
466 }
467 
468 static int load_module(void)
469 {
470  int res = ast_custom_function_register_escalating(&lock_function, AST_CFE_READ);
471  res |= ast_custom_function_register_escalating(&trylock_function, AST_CFE_READ);
472  res |= ast_custom_function_register_escalating(&unlock_function, AST_CFE_READ);
473 
474  return res;
475 }
476 
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
Main Channel structure associated with a channel.
#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:468
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:112
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:138
#define LOG_WARNING
Definition: logger.h:274
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static struct ast_custom_function trylock_function
Definition: func_lock.c:418
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:127
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
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:2390
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
#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.
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
static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_lock.c:400
#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:444
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
struct lock_frame::@205 entries
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:114
#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:122
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:183
#define ast_cond_destroy(cond)
Definition: lock.h:200
static void lock_free(void *data)
Definition: func_lock.c:141
ast_cond_t cond
Definition: func_lock.c:123
#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
#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:162
char name[0]
Definition: func_lock.c:131
static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_lock.c:336
struct channel_lock_frame::@206 list
static struct ast_custom_function unlock_function
Definition: func_lock.c:424
void * data
Definition: datastore.h:70
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:129
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_channel * channel
Definition: func_lock.c:137
static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_lock.c:388
#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:2376
static int unload_module(void)
Definition: func_lock.c:430
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:204
static struct ast_custom_function lock_function
Definition: func_lock.c:412
unsigned int count
Definition: func_lock.c:125
Structure for mutex and tracking information.
Definition: lock.h:135
#define ast_mutex_unlock(a)
Definition: lock.h:188