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