Asterisk - The Open Source Telephony Project GIT-master-7e7a603
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"
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
123static void lock_free(void *data);
124static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
125static int unloading = 0;
126
127static const struct ast_datastore_info lock_info = {
128 .type = "MUTEX",
129 .destroy = lock_free,
130 .chan_fixup = lock_fixup,
131};
132
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. */
152};
153
154static 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);
167 }
168 ast_free(clframe);
169 }
170 AST_LIST_UNLOCK(oldlist);
171 AST_LIST_HEAD_DESTROY(oldlist);
172 ast_free(oldlist);
173
175}
176
177static 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
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 }
196}
197
198static 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) {
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? */
242 if (strcmp(current->name, lockname) == 0) {
243 break;
244 }
245 }
246
247 if (!current) {
248 if (unloading) {
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));
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);
276 return -1;
277 }
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) {
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)))) {
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
370static 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 */
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. */
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);
416 }
417
418 ast_copy_string(buf, "1", len);
419 return 0;
420}
421
422static 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 }
428 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
430
431 return 0;
432}
433
434static 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 }
440 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
442
443 return 0;
444}
445
446static 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");
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
478 .name = "LOCK",
479 .read = lock_read,
480 .read_max = 2,
481};
482
484 .name = "TRYLOCK",
485 .read = trylock_read,
486 .read_max = 2,
487};
488
490 .name = "UNLOCK",
491 .read = unlock_read,
492 .read_max = 2,
493};
494
495static struct ast_cli_entry cli_locks_show = AST_CLI_DEFINE(handle_cli_locks_show, "List func_lock locks.");
496
497static 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() */
508
510
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);
534 }
537
538 /* At this point we can safely stop access to UNLOCK */
540
541 return 0;
542}
543
544static int load_module(void)
545{
550
551 return res;
552}
553
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
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
Standard Command Line Interface.
int ast_cli_unregister(struct ast_cli_entry *e)
Unregisters a command or an array of commands.
Definition: main/cli.c:2427
#define ast_cli_register(e)
Registers a command or an array of commands.
Definition: cli.h:256
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
static struct ast_custom_function trylock_function
Definition: func_lock.c:483
static struct ast_custom_function lock_function
Definition: func_lock.c:477
static struct ast_cli_entry cli_locks_show
Definition: func_lock.c:495
static const struct ast_datastore_info lock_info
Definition: func_lock.c:127
static void lock_free(void *data)
Definition: func_lock.c:154
static struct ast_custom_function unlock_function
Definition: func_lock.c:489
static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_lock.c:370
static int unloading
Definition: func_lock.c:125
static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_lock.c:434
static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_lock.c:422
static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
Definition: func_lock.c:177
static int get_lock(struct ast_channel *chan, char *lockname, int trylock)
Definition: func_lock.c:198
static int load_module(void)
Definition: func_lock.c:544
static char * handle_cli_locks_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: func_lock.c:446
static int unload_module(void)
Definition: func_lock.c:497
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
A set of macros to manage forward-linked lists.
#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_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:653
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#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
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:173
Asterisk locking-related definitions:
#define ast_cond_destroy(cond)
Definition: lock.h:202
#define ast_cond_wait(cond, mutex)
Definition: lock.h:205
#define ast_cond_init(cond, attr)
Definition: lock.h:201
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:206
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
pthread_cond_t ast_cond_t
Definition: lock.h:178
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
#define ast_cond_signal(cond)
Definition: lock.h:203
size_t current
Definition: main/cli.c:113
Asterisk module definitions.
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Core PBX routines and definitions.
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition: pbx.h:1567
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
@ AST_CFE_READ
Definition: pbx.h:1550
#define NULL
Definition: resample.c:96
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
struct ast_module * self
Definition: module.h:342
Structure for mutex and tracking information.
Definition: lock.h:135
struct channel_lock_frame::@174 list
struct ast_channel * channel
Definition: func_lock.c:150
struct lock_frame * lock_frame
Definition: func_lock.c:151
unsigned int requesters
Definition: func_lock.c:140
unsigned int count
Definition: func_lock.c:138
ast_cond_t cond
Definition: func_lock.c:136
ast_mutex_t mutex
Definition: func_lock.c:135
struct lock_frame::@173 entries
struct ast_channel * owner
Definition: func_lock.c:142
char name[0]
Definition: func_lock.c:144
static struct test_val a
static struct test_val c
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Utility functions.