00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "asterisk.h"
00034
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370183 $")
00036
00037 #include <signal.h>
00038
00039 #include "asterisk/lock.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/linkedlists.h"
00045 #include "asterisk/astobj2.h"
00046 #include "asterisk/utils.h"
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095 static AST_LIST_HEAD_STATIC(locklist, lock_frame);
00096
00097 static void lock_free(void *data);
00098 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
00099 static int unloading = 0;
00100 static pthread_t broker_tid = AST_PTHREADT_NULL;
00101
00102 static const struct ast_datastore_info lock_info = {
00103 .type = "MUTEX",
00104 .destroy = lock_free,
00105 .chan_fixup = lock_fixup,
00106 };
00107
00108 struct lock_frame {
00109 AST_LIST_ENTRY(lock_frame) entries;
00110 ast_mutex_t mutex;
00111 ast_cond_t cond;
00112
00113 unsigned int count;
00114
00115 struct ao2_container *requesters;
00116
00117 struct ast_channel *owner;
00118
00119 char name[0];
00120 };
00121
00122 struct channel_lock_frame {
00123 AST_LIST_ENTRY(channel_lock_frame) list;
00124
00125 struct ast_channel *channel;
00126 struct lock_frame *lock_frame;
00127 };
00128
00129 static void lock_free(void *data)
00130 {
00131 AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
00132 struct channel_lock_frame *clframe;
00133 AST_LIST_LOCK(oldlist);
00134 while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00135
00136 if (clframe->channel == clframe->lock_frame->owner) {
00137 clframe->lock_frame->count = 0;
00138 clframe->lock_frame->owner = NULL;
00139 }
00140 ast_free(clframe);
00141 }
00142 AST_LIST_UNLOCK(oldlist);
00143 AST_LIST_HEAD_DESTROY(oldlist);
00144 ast_free(oldlist);
00145 }
00146
00147 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
00148 {
00149 struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
00150 AST_LIST_HEAD(, channel_lock_frame) *list;
00151 struct channel_lock_frame *clframe = NULL;
00152
00153 if (!lock_store) {
00154 return;
00155 }
00156 list = lock_store->data;
00157
00158 AST_LIST_LOCK(list);
00159 AST_LIST_TRAVERSE(list, clframe, list) {
00160 if (clframe->lock_frame->owner == oldchan) {
00161 clframe->lock_frame->owner = newchan;
00162 }
00163
00164 clframe->channel = newchan;
00165 }
00166 AST_LIST_UNLOCK(list);
00167 }
00168
00169 static void *lock_broker(void *unused)
00170 {
00171 struct lock_frame *frame;
00172 struct timespec forever = { 1000000, 0 };
00173 for (;;) {
00174 int found_requester = 0;
00175
00176
00177 pthread_testcancel();
00178 AST_LIST_LOCK(&locklist);
00179
00180 AST_LIST_TRAVERSE(&locklist, frame, entries) {
00181 if (ao2_container_count(frame->requesters)) {
00182 found_requester++;
00183 ast_mutex_lock(&frame->mutex);
00184 if (!frame->owner) {
00185 ast_cond_signal(&frame->cond);
00186 }
00187 ast_mutex_unlock(&frame->mutex);
00188 }
00189 }
00190
00191 AST_LIST_UNLOCK(&locklist);
00192 pthread_testcancel();
00193
00194
00195 if (!found_requester) {
00196 nanosleep(&forever, NULL);
00197 } else {
00198 sched_yield();
00199 }
00200 }
00201
00202 return NULL;
00203 }
00204
00205 static int ast_channel_hash_cb(const void *obj, const int flags)
00206 {
00207 const struct ast_channel *chan = obj;
00208 return ast_str_case_hash(chan->name);
00209 }
00210
00211 static int ast_channel_cmp_cb(void *obj, void *arg, int flags)
00212 {
00213 struct ast_channel *chan = obj, *cmp_args = arg;
00214 return strcasecmp(chan->name, cmp_args->name) ? 0 : CMP_MATCH;
00215 }
00216
00217 static int get_lock(struct ast_channel *chan, char *lockname, int trylock)
00218 {
00219 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00220 struct lock_frame *current;
00221 struct channel_lock_frame *clframe = NULL;
00222 AST_LIST_HEAD(, channel_lock_frame) *list;
00223 int res = 0;
00224 struct timespec timeout = { 0, };
00225 struct timeval now;
00226
00227 if (!lock_store) {
00228 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
00229 lock_store = ast_datastore_alloc(&lock_info, NULL);
00230 if (!lock_store) {
00231 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
00232 return -1;
00233 }
00234
00235 list = ast_calloc(1, sizeof(*list));
00236 if (!list) {
00237 ast_log(LOG_ERROR,
00238 "Unable to allocate datastore list head. %sLOCK will fail.\n",
00239 trylock ? "TRY" : "");
00240 ast_datastore_free(lock_store);
00241 return -1;
00242 }
00243
00244 lock_store->data = list;
00245 AST_LIST_HEAD_INIT(list);
00246 ast_channel_datastore_add(chan, lock_store);
00247 } else
00248 list = lock_store->data;
00249
00250
00251 AST_LIST_LOCK(&locklist);
00252 AST_LIST_TRAVERSE(&locklist, current, entries) {
00253 if (strcmp(current->name, lockname) == 0) {
00254 break;
00255 }
00256 }
00257
00258 if (!current) {
00259 if (unloading) {
00260
00261 AST_LIST_UNLOCK(&locklist);
00262 return -1;
00263 }
00264
00265
00266 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
00267 if (!current) {
00268 AST_LIST_UNLOCK(&locklist);
00269 return -1;
00270 }
00271
00272 strcpy(current->name, lockname);
00273 if ((res = ast_mutex_init(¤t->mutex))) {
00274 ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
00275 ast_free(current);
00276 AST_LIST_UNLOCK(&locklist);
00277 return -1;
00278 }
00279 if ((res = ast_cond_init(¤t->cond, NULL))) {
00280 ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
00281 ast_mutex_destroy(¤t->mutex);
00282 ast_free(current);
00283 AST_LIST_UNLOCK(&locklist);
00284 return -1;
00285 }
00286 if (!(current->requesters = ao2_container_alloc(1, ast_channel_hash_cb, ast_channel_cmp_cb))) {
00287 ast_mutex_destroy(¤t->mutex);
00288 ast_cond_destroy(¤t->cond);
00289 ast_free(current);
00290 AST_LIST_UNLOCK(&locklist);
00291 return -1;
00292 }
00293 AST_LIST_INSERT_TAIL(&locklist, current, entries);
00294 }
00295 AST_LIST_UNLOCK(&locklist);
00296
00297
00298 AST_LIST_LOCK(list);
00299 AST_LIST_TRAVERSE(list, clframe, list) {
00300 if (clframe->lock_frame == current) {
00301 break;
00302 }
00303 }
00304
00305 if (!clframe) {
00306 if (unloading) {
00307
00308 AST_LIST_UNLOCK(list);
00309 return -1;
00310 }
00311
00312 if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
00313 ast_log(LOG_ERROR,
00314 "Unable to allocate channel lock frame. %sLOCK will fail.\n",
00315 trylock ? "TRY" : "");
00316 AST_LIST_UNLOCK(list);
00317 return -1;
00318 }
00319
00320 clframe->lock_frame = current;
00321 clframe->channel = chan;
00322 AST_LIST_INSERT_TAIL(list, clframe, list);
00323 }
00324 AST_LIST_UNLOCK(list);
00325
00326
00327
00328
00329
00330 if (current->owner == chan) {
00331 current->count++;
00332 return 0;
00333 }
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346 AST_LIST_LOCK(&locklist);
00347 ast_mutex_lock(¤t->mutex);
00348
00349 ao2_link(current->requesters, chan);
00350 pthread_kill(broker_tid, SIGURG);
00351 AST_LIST_UNLOCK(&locklist);
00352
00353
00354 now = ast_tvnow();
00355 timeout.tv_sec = now.tv_sec + 3;
00356 timeout.tv_nsec = now.tv_usec * 1000;
00357
00358 if (!current->owner
00359 || (!trylock
00360 && !(res = ast_cond_timedwait(¤t->cond, ¤t->mutex, &timeout)))) {
00361 res = 0;
00362 current->owner = chan;
00363 current->count++;
00364 } else {
00365 res = -1;
00366 }
00367
00368 ao2_unlink(current->requesters, chan);
00369 ast_mutex_unlock(¤t->mutex);
00370
00371 return res;
00372 }
00373
00374 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00375 {
00376 struct ast_datastore *lock_store;
00377 struct channel_lock_frame *clframe;
00378 AST_LIST_HEAD(, channel_lock_frame) *list;
00379
00380 if (!chan) {
00381 return -1;
00382 }
00383
00384 lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00385 if (!lock_store) {
00386 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
00387 ast_copy_string(buf, "0", len);
00388 return 0;
00389 }
00390
00391 if (!(list = lock_store->data)) {
00392 ast_debug(1, "This should NEVER happen\n");
00393 ast_copy_string(buf, "0", len);
00394 return 0;
00395 }
00396
00397
00398 AST_LIST_LOCK(list);
00399 AST_LIST_TRAVERSE(list, clframe, list) {
00400 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00401 break;
00402 }
00403 }
00404
00405
00406
00407 AST_LIST_UNLOCK(list);
00408
00409 if (!clframe) {
00410
00411 ast_copy_string(buf, "0", len);
00412 return 0;
00413 }
00414
00415 if (--clframe->lock_frame->count == 0) {
00416 clframe->lock_frame->owner = NULL;
00417 }
00418
00419 ast_copy_string(buf, "1", len);
00420 return 0;
00421 }
00422
00423 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00424 {
00425 if (!chan) {
00426 return -1;
00427 }
00428 ast_autoservice_start(chan);
00429 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00430 ast_autoservice_stop(chan);
00431
00432 return 0;
00433 }
00434
00435 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00436 {
00437 if (!chan) {
00438 return -1;
00439 }
00440 ast_autoservice_start(chan);
00441 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00442 ast_autoservice_stop(chan);
00443
00444 return 0;
00445 }
00446
00447 static struct ast_custom_function lock_function = {
00448 .name = "LOCK",
00449 .read = lock_read,
00450 .read_max = 2,
00451 };
00452
00453 static struct ast_custom_function trylock_function = {
00454 .name = "TRYLOCK",
00455 .read = trylock_read,
00456 .read_max = 2,
00457 };
00458
00459 static struct ast_custom_function unlock_function = {
00460 .name = "UNLOCK",
00461 .read = unlock_read,
00462 .read_max = 2,
00463 };
00464
00465 static int unload_module(void)
00466 {
00467 struct lock_frame *current;
00468
00469
00470 unloading = 1;
00471
00472 AST_LIST_LOCK(&locklist);
00473 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00474
00475 if (current->owner || ao2_container_count(current->requesters)) {
00476
00477 AST_LIST_INSERT_HEAD(&locklist, current, entries);
00478 AST_LIST_UNLOCK(&locklist);
00479 unloading = 0;
00480 return -1;
00481 }
00482 ast_mutex_destroy(¤t->mutex);
00483 ao2_ref(current->requesters, -1);
00484 ast_free(current);
00485 }
00486
00487
00488 ast_custom_function_unregister(&lock_function);
00489 ast_custom_function_unregister(&trylock_function);
00490 ast_custom_function_unregister(&unlock_function);
00491
00492 if (broker_tid != AST_PTHREADT_NULL) {
00493 pthread_cancel(broker_tid);
00494 pthread_kill(broker_tid, SIGURG);
00495 pthread_join(broker_tid, NULL);
00496 }
00497
00498 AST_LIST_UNLOCK(&locklist);
00499
00500 return 0;
00501 }
00502
00503 static int load_module(void)
00504 {
00505 int res = ast_custom_function_register(&lock_function);
00506 res |= ast_custom_function_register(&trylock_function);
00507 res |= ast_custom_function_register(&unlock_function);
00508
00509 if (ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL)) {
00510 ast_log(LOG_ERROR, "Failed to start lock broker thread. Unloading func_lock module.\n");
00511 broker_tid = AST_PTHREADT_NULL;
00512 unload_module();
00513 return AST_MODULE_LOAD_DECLINE;
00514 }
00515
00516 return res;
00517 }
00518
00519 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");