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 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 232015 $")
00032
00033 #include <signal.h>
00034
00035 #include "asterisk/lock.h"
00036 #include "asterisk/file.h"
00037 #include "asterisk/channel.h"
00038 #include "asterisk/pbx.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/linkedlists.h"
00041 #include "asterisk/astobj2.h"
00042 #include "asterisk/utils.h"
00043
00044
00045
00046
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 AST_LIST_HEAD_STATIC(locklist, lock_frame);
00092
00093 static void lock_free(void *data);
00094 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
00095 static int unloading = 0;
00096 static pthread_t broker_tid = AST_PTHREADT_NULL;
00097
00098 static struct ast_datastore_info lock_info = {
00099 .type = "MUTEX",
00100 .destroy = lock_free,
00101 .chan_fixup = lock_fixup,
00102 };
00103
00104 struct lock_frame {
00105 AST_LIST_ENTRY(lock_frame) entries;
00106 ast_mutex_t mutex;
00107 ast_cond_t cond;
00108
00109 unsigned int count;
00110
00111 struct ao2_container *requesters;
00112
00113 struct ast_channel *owner;
00114
00115 char name[0];
00116 };
00117
00118 struct channel_lock_frame {
00119 AST_LIST_ENTRY(channel_lock_frame) list;
00120
00121 struct ast_channel *channel;
00122 struct lock_frame *lock_frame;
00123 };
00124
00125 static void lock_free(void *data)
00126 {
00127 AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
00128 struct channel_lock_frame *clframe;
00129 AST_LIST_LOCK(oldlist);
00130 while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00131
00132 if (clframe->channel == clframe->lock_frame->owner) {
00133 clframe->lock_frame->count = 0;
00134 clframe->lock_frame->owner = NULL;
00135 }
00136 ast_free(clframe);
00137 }
00138 AST_LIST_UNLOCK(oldlist);
00139 AST_LIST_HEAD_DESTROY(oldlist);
00140 ast_free(oldlist);
00141 }
00142
00143 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
00144 {
00145 struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
00146 AST_LIST_HEAD(, channel_lock_frame) *list;
00147 struct channel_lock_frame *clframe = NULL;
00148
00149 if (!lock_store) {
00150 return;
00151 }
00152 list = lock_store->data;
00153
00154 AST_LIST_LOCK(list);
00155 AST_LIST_TRAVERSE(list, clframe, list) {
00156 if (clframe->lock_frame->owner == oldchan) {
00157 clframe->lock_frame->owner = newchan;
00158 }
00159
00160 clframe->channel = newchan;
00161 }
00162 AST_LIST_UNLOCK(list);
00163 }
00164
00165 static void *lock_broker(void *unused)
00166 {
00167 struct lock_frame *frame;
00168 struct timespec forever = { 1000000, 0 };
00169 for (;;) {
00170 int found_requester = 0;
00171
00172
00173 pthread_testcancel();
00174 AST_LIST_LOCK(&locklist);
00175
00176 AST_LIST_TRAVERSE(&locklist, frame, entries) {
00177 if (ao2_container_count(frame->requesters)) {
00178 found_requester++;
00179 ast_mutex_lock(&frame->mutex);
00180 if (!frame->owner) {
00181 ast_cond_signal(&frame->cond);
00182 }
00183 ast_mutex_unlock(&frame->mutex);
00184 }
00185 }
00186
00187 AST_LIST_UNLOCK(&locklist);
00188 pthread_testcancel();
00189
00190
00191 if (!found_requester) {
00192 nanosleep(&forever, NULL);
00193 } else {
00194 sched_yield();
00195 }
00196 }
00197
00198 return NULL;
00199 }
00200
00201 static int null_hash_cb(const void *obj, const int flags)
00202 {
00203 return (int)(long) obj;
00204 }
00205
00206 static int null_cmp_cb(void *obj, void *arg, int flags)
00207 {
00208 return obj == arg ? CMP_MATCH : 0;
00209 }
00210
00211 static int get_lock(struct ast_channel *chan, char *lockname, int try)
00212 {
00213 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00214 struct lock_frame *current;
00215 struct channel_lock_frame *clframe = NULL;
00216 AST_LIST_HEAD(, channel_lock_frame) *list;
00217 int res = 0, *link;
00218 struct timespec three_seconds = { .tv_sec = 3 };
00219
00220 if (!lock_store) {
00221 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
00222 lock_store = ast_datastore_alloc(&lock_info, NULL);
00223 if (!lock_store) {
00224 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
00225 return -1;
00226 }
00227
00228 list = ast_calloc(1, sizeof(*list));
00229 if (!list) {
00230 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %sLOCK will fail.\n", try ? "TRY" : "");
00231 ast_datastore_free(lock_store);
00232 return -1;
00233 }
00234
00235 lock_store->data = list;
00236 AST_LIST_HEAD_INIT(list);
00237 ast_channel_datastore_add(chan, lock_store);
00238 } else
00239 list = lock_store->data;
00240
00241
00242 AST_LIST_LOCK(&locklist);
00243 AST_LIST_TRAVERSE(&locklist, current, entries) {
00244 if (strcmp(current->name, lockname) == 0) {
00245 break;
00246 }
00247 }
00248
00249 if (!current) {
00250 if (unloading) {
00251
00252 AST_LIST_UNLOCK(&locklist);
00253 return -1;
00254 }
00255
00256
00257 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
00258 if (!current) {
00259 AST_LIST_UNLOCK(&locklist);
00260 return -1;
00261 }
00262
00263 strcpy(current->name, lockname);
00264 if ((res = ast_mutex_init(¤t->mutex))) {
00265 ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
00266 ast_free(current);
00267 AST_LIST_UNLOCK(&locklist);
00268 return -1;
00269 }
00270 if ((res = ast_cond_init(¤t->cond, NULL))) {
00271 ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
00272 ast_mutex_destroy(¤t->mutex);
00273 ast_free(current);
00274 AST_LIST_UNLOCK(&locklist);
00275 return -1;
00276 }
00277 if (!(current->requesters = ao2_container_alloc(7, null_hash_cb, null_cmp_cb))) {
00278 ast_mutex_destroy(¤t->mutex);
00279 ast_cond_destroy(¤t->cond);
00280 ast_free(current);
00281 AST_LIST_UNLOCK(&locklist);
00282 return -1;
00283 }
00284 AST_LIST_INSERT_TAIL(&locklist, current, entries);
00285 }
00286 AST_LIST_UNLOCK(&locklist);
00287
00288
00289 AST_LIST_LOCK(list);
00290 AST_LIST_TRAVERSE(list, clframe, list) {
00291 if (clframe->lock_frame == current) {
00292 break;
00293 }
00294 }
00295
00296 if (!clframe) {
00297 if (unloading) {
00298
00299 AST_LIST_UNLOCK(list);
00300 return -1;
00301 }
00302
00303 if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
00304 ast_log(LOG_ERROR, "Unable to allocate channel lock frame. %sLOCK will fail.\n", try ? "TRY" : "");
00305 AST_LIST_UNLOCK(list);
00306 return -1;
00307 }
00308
00309 clframe->lock_frame = current;
00310 clframe->channel = chan;
00311 AST_LIST_INSERT_TAIL(list, clframe, list);
00312 }
00313 AST_LIST_UNLOCK(list);
00314
00315
00316
00317
00318
00319 if (current->owner == chan) {
00320 current->count++;
00321 return 0;
00322 }
00323
00324
00325
00326 if (!(link = ao2_alloc(sizeof(*link), NULL))) {
00327 return -1;
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, link);
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, link);
00358 ao2_ref(link, -1);
00359 ast_mutex_unlock(¤t->mutex);
00360
00361 return res;
00362 }
00363
00364 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00365 {
00366 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00367 struct channel_lock_frame *clframe;
00368 AST_LIST_HEAD(, channel_lock_frame) *list;
00369
00370 if (!lock_store) {
00371 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
00372 ast_copy_string(buf, "0", len);
00373 return 0;
00374 }
00375
00376 if (!(list = lock_store->data)) {
00377 ast_debug(1, "This should NEVER happen\n");
00378 ast_copy_string(buf, "0", len);
00379 return 0;
00380 }
00381
00382
00383 AST_LIST_LOCK(list);
00384 AST_LIST_TRAVERSE(list, clframe, list) {
00385 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00386 break;
00387 }
00388 }
00389
00390
00391
00392 AST_LIST_UNLOCK(list);
00393
00394 if (!clframe) {
00395
00396 ast_copy_string(buf, "0", len);
00397 return 0;
00398 }
00399
00400 if (--clframe->lock_frame->count == 0) {
00401 clframe->lock_frame->owner = NULL;
00402 }
00403
00404 ast_copy_string(buf, "1", len);
00405 return 0;
00406 }
00407
00408 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00409 {
00410 if (chan)
00411 ast_autoservice_start(chan);
00412
00413 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00414
00415 if (chan)
00416 ast_autoservice_stop(chan);
00417
00418 return 0;
00419 }
00420
00421 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00422 {
00423 if (chan)
00424 ast_autoservice_start(chan);
00425
00426 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00427
00428 if (chan)
00429 ast_autoservice_stop(chan);
00430
00431 return 0;
00432 }
00433
00434 static struct ast_custom_function lock_function = {
00435 .name = "LOCK",
00436 .read = lock_read,
00437 };
00438
00439 static struct ast_custom_function trylock_function = {
00440 .name = "TRYLOCK",
00441 .read = trylock_read,
00442 };
00443
00444 static struct ast_custom_function unlock_function = {
00445 .name = "UNLOCK",
00446 .read = unlock_read,
00447 };
00448
00449 static int unload_module(void)
00450 {
00451 struct lock_frame *current;
00452
00453
00454 unloading = 1;
00455
00456 AST_LIST_LOCK(&locklist);
00457 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00458
00459 if (current->owner || ao2_container_count(current->requesters)) {
00460
00461 AST_LIST_INSERT_HEAD(&locklist, current, entries);
00462 AST_LIST_UNLOCK(&locklist);
00463 unloading = 0;
00464 return -1;
00465 }
00466 ast_mutex_destroy(¤t->mutex);
00467 ao2_ref(current->requesters, -1);
00468 ast_free(current);
00469 }
00470
00471
00472 ast_custom_function_unregister(&lock_function);
00473 ast_custom_function_unregister(&trylock_function);
00474 ast_custom_function_unregister(&unlock_function);
00475
00476 pthread_cancel(broker_tid);
00477 pthread_kill(broker_tid, SIGURG);
00478 pthread_join(broker_tid, NULL);
00479
00480 AST_LIST_UNLOCK(&locklist);
00481
00482 return 0;
00483 }
00484
00485 static int load_module(void)
00486 {
00487 int res = ast_custom_function_register(&lock_function);
00488 res |= ast_custom_function_register(&trylock_function);
00489 res |= ast_custom_function_register(&unlock_function);
00490 ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
00491 return res;
00492 }
00493
00494 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");