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: 328209 $")
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 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 try)
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 three_seconds = { .tv_sec = 3 };
00225
00226 if (!lock_store) {
00227 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
00228 lock_store = ast_datastore_alloc(&lock_info, NULL);
00229 if (!lock_store) {
00230 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
00231 return -1;
00232 }
00233
00234 list = ast_calloc(1, sizeof(*list));
00235 if (!list) {
00236 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %sLOCK will fail.\n", try ? "TRY" : "");
00237 ast_datastore_free(lock_store);
00238 return -1;
00239 }
00240
00241 lock_store->data = list;
00242 AST_LIST_HEAD_INIT(list);
00243 ast_channel_datastore_add(chan, lock_store);
00244 } else
00245 list = lock_store->data;
00246
00247
00248 AST_LIST_LOCK(&locklist);
00249 AST_LIST_TRAVERSE(&locklist, current, entries) {
00250 if (strcmp(current->name, lockname) == 0) {
00251 break;
00252 }
00253 }
00254
00255 if (!current) {
00256 if (unloading) {
00257
00258 AST_LIST_UNLOCK(&locklist);
00259 return -1;
00260 }
00261
00262
00263 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
00264 if (!current) {
00265 AST_LIST_UNLOCK(&locklist);
00266 return -1;
00267 }
00268
00269 strcpy(current->name, lockname);
00270 if ((res = ast_mutex_init(¤t->mutex))) {
00271 ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
00272 ast_free(current);
00273 AST_LIST_UNLOCK(&locklist);
00274 return -1;
00275 }
00276 if ((res = ast_cond_init(¤t->cond, NULL))) {
00277 ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
00278 ast_mutex_destroy(¤t->mutex);
00279 ast_free(current);
00280 AST_LIST_UNLOCK(&locklist);
00281 return -1;
00282 }
00283 if (!(current->requesters = ao2_container_alloc(1, ast_channel_hash_cb, ast_channel_cmp_cb))) {
00284 ast_mutex_destroy(¤t->mutex);
00285 ast_cond_destroy(¤t->cond);
00286 ast_free(current);
00287 AST_LIST_UNLOCK(&locklist);
00288 return -1;
00289 }
00290 AST_LIST_INSERT_TAIL(&locklist, current, entries);
00291 }
00292 AST_LIST_UNLOCK(&locklist);
00293
00294
00295 AST_LIST_LOCK(list);
00296 AST_LIST_TRAVERSE(list, clframe, list) {
00297 if (clframe->lock_frame == current) {
00298 break;
00299 }
00300 }
00301
00302 if (!clframe) {
00303 if (unloading) {
00304
00305 AST_LIST_UNLOCK(list);
00306 return -1;
00307 }
00308
00309 if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
00310 ast_log(LOG_ERROR, "Unable to allocate channel lock frame. %sLOCK will fail.\n", try ? "TRY" : "");
00311 AST_LIST_UNLOCK(list);
00312 return -1;
00313 }
00314
00315 clframe->lock_frame = current;
00316 clframe->channel = chan;
00317 AST_LIST_INSERT_TAIL(list, clframe, list);
00318 }
00319 AST_LIST_UNLOCK(list);
00320
00321
00322
00323
00324
00325 if (current->owner == chan) {
00326 current->count++;
00327 return 0;
00328 }
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341 AST_LIST_LOCK(&locklist);
00342 ast_mutex_lock(¤t->mutex);
00343
00344 ao2_link(current->requesters, chan);
00345 pthread_kill(broker_tid, SIGURG);
00346 AST_LIST_UNLOCK(&locklist);
00347
00348 if ((!current->owner) ||
00349 (!try && !(res = ast_cond_timedwait(¤t->cond, ¤t->mutex, &three_seconds)))) {
00350 res = 0;
00351 current->owner = chan;
00352 current->count++;
00353 } else {
00354 res = -1;
00355 }
00356
00357 ao2_unlink(current->requesters, chan);
00358 ast_mutex_unlock(¤t->mutex);
00359
00360 return res;
00361 }
00362
00363 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00364 {
00365 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00366 struct channel_lock_frame *clframe;
00367 AST_LIST_HEAD(, channel_lock_frame) *list;
00368
00369 if (!lock_store) {
00370 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
00371 ast_copy_string(buf, "0", len);
00372 return 0;
00373 }
00374
00375 if (!(list = lock_store->data)) {
00376 ast_debug(1, "This should NEVER happen\n");
00377 ast_copy_string(buf, "0", len);
00378 return 0;
00379 }
00380
00381
00382 AST_LIST_LOCK(list);
00383 AST_LIST_TRAVERSE(list, clframe, list) {
00384 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00385 break;
00386 }
00387 }
00388
00389
00390
00391 AST_LIST_UNLOCK(list);
00392
00393 if (!clframe) {
00394
00395 ast_copy_string(buf, "0", len);
00396 return 0;
00397 }
00398
00399 if (--clframe->lock_frame->count == 0) {
00400 clframe->lock_frame->owner = NULL;
00401 }
00402
00403 ast_copy_string(buf, "1", len);
00404 return 0;
00405 }
00406
00407 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00408 {
00409 if (chan)
00410 ast_autoservice_start(chan);
00411
00412 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00413
00414 if (chan)
00415 ast_autoservice_stop(chan);
00416
00417 return 0;
00418 }
00419
00420 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00421 {
00422 if (chan)
00423 ast_autoservice_start(chan);
00424
00425 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00426
00427 if (chan)
00428 ast_autoservice_stop(chan);
00429
00430 return 0;
00431 }
00432
00433 static struct ast_custom_function lock_function = {
00434 .name = "LOCK",
00435 .read = lock_read,
00436 .read_max = 2,
00437 };
00438
00439 static struct ast_custom_function trylock_function = {
00440 .name = "TRYLOCK",
00441 .read = trylock_read,
00442 .read_max = 2,
00443 };
00444
00445 static struct ast_custom_function unlock_function = {
00446 .name = "UNLOCK",
00447 .read = unlock_read,
00448 .read_max = 2,
00449 };
00450
00451 static int unload_module(void)
00452 {
00453 struct lock_frame *current;
00454
00455
00456 unloading = 1;
00457
00458 AST_LIST_LOCK(&locklist);
00459 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00460
00461 if (current->owner || ao2_container_count(current->requesters)) {
00462
00463 AST_LIST_INSERT_HEAD(&locklist, current, entries);
00464 AST_LIST_UNLOCK(&locklist);
00465 unloading = 0;
00466 return -1;
00467 }
00468 ast_mutex_destroy(¤t->mutex);
00469 ao2_ref(current->requesters, -1);
00470 ast_free(current);
00471 }
00472
00473
00474 ast_custom_function_unregister(&lock_function);
00475 ast_custom_function_unregister(&trylock_function);
00476 ast_custom_function_unregister(&unlock_function);
00477
00478 pthread_cancel(broker_tid);
00479 pthread_kill(broker_tid, SIGURG);
00480 pthread_join(broker_tid, NULL);
00481
00482 AST_LIST_UNLOCK(&locklist);
00483
00484 return 0;
00485 }
00486
00487 static int load_module(void)
00488 {
00489 int res = ast_custom_function_register(&lock_function);
00490 res |= ast_custom_function_register(&trylock_function);
00491 res |= ast_custom_function_register(&unlock_function);
00492 ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
00493 return res;
00494 }
00495
00496 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");