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: 350311 $")
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 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 = ast_channel_datastore_find(chan, &lock_info, NULL);
00377 struct channel_lock_frame *clframe;
00378 AST_LIST_HEAD(, channel_lock_frame) *list;
00379
00380 if (!lock_store) {
00381 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
00382 ast_copy_string(buf, "0", len);
00383 return 0;
00384 }
00385
00386 if (!(list = lock_store->data)) {
00387 ast_debug(1, "This should NEVER happen\n");
00388 ast_copy_string(buf, "0", len);
00389 return 0;
00390 }
00391
00392
00393 AST_LIST_LOCK(list);
00394 AST_LIST_TRAVERSE(list, clframe, list) {
00395 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00396 break;
00397 }
00398 }
00399
00400
00401
00402 AST_LIST_UNLOCK(list);
00403
00404 if (!clframe) {
00405
00406 ast_copy_string(buf, "0", len);
00407 return 0;
00408 }
00409
00410 if (--clframe->lock_frame->count == 0) {
00411 clframe->lock_frame->owner = NULL;
00412 }
00413
00414 ast_copy_string(buf, "1", len);
00415 return 0;
00416 }
00417
00418 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00419 {
00420 if (chan)
00421 ast_autoservice_start(chan);
00422
00423 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00424
00425 if (chan)
00426 ast_autoservice_stop(chan);
00427
00428 return 0;
00429 }
00430
00431 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00432 {
00433 if (chan)
00434 ast_autoservice_start(chan);
00435
00436 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00437
00438 if (chan)
00439 ast_autoservice_stop(chan);
00440
00441 return 0;
00442 }
00443
00444 static struct ast_custom_function lock_function = {
00445 .name = "LOCK",
00446 .read = lock_read,
00447 .read_max = 2,
00448 };
00449
00450 static struct ast_custom_function trylock_function = {
00451 .name = "TRYLOCK",
00452 .read = trylock_read,
00453 .read_max = 2,
00454 };
00455
00456 static struct ast_custom_function unlock_function = {
00457 .name = "UNLOCK",
00458 .read = unlock_read,
00459 .read_max = 2,
00460 };
00461
00462 static int unload_module(void)
00463 {
00464 struct lock_frame *current;
00465
00466
00467 unloading = 1;
00468
00469 AST_LIST_LOCK(&locklist);
00470 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00471
00472 if (current->owner || ao2_container_count(current->requesters)) {
00473
00474 AST_LIST_INSERT_HEAD(&locklist, current, entries);
00475 AST_LIST_UNLOCK(&locklist);
00476 unloading = 0;
00477 return -1;
00478 }
00479 ast_mutex_destroy(¤t->mutex);
00480 ao2_ref(current->requesters, -1);
00481 ast_free(current);
00482 }
00483
00484
00485 ast_custom_function_unregister(&lock_function);
00486 ast_custom_function_unregister(&trylock_function);
00487 ast_custom_function_unregister(&unlock_function);
00488
00489 pthread_cancel(broker_tid);
00490 pthread_kill(broker_tid, SIGURG);
00491 pthread_join(broker_tid, NULL);
00492
00493 AST_LIST_UNLOCK(&locklist);
00494
00495 return 0;
00496 }
00497
00498 static int load_module(void)
00499 {
00500 int res = ast_custom_function_register(&lock_function);
00501 res |= ast_custom_function_register(&trylock_function);
00502 res |= ast_custom_function_register(&unlock_function);
00503 ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
00504 return res;
00505 }
00506
00507 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");