Index: apps/app_meetme.c =================================================================== --- apps/app_meetme.c (revision 38951) +++ apps/app_meetme.c (working copy) @@ -54,6 +54,7 @@ #include "asterisk/cli.h" #include "asterisk/say.h" #include "asterisk/utils.h" +#include "asterisk/linkedlists.h" static const char *tdesc = "MeetMe conference bridge"; @@ -128,15 +129,14 @@ LOCAL_USER_DECL; -static struct ast_conference { +struct ast_conference { char confno[AST_MAX_EXTENSION]; /* Conference */ struct ast_channel *chan; /* Announcements channel */ int fd; /* Announcements fd */ int zapconf; /* Zaptel Conf # */ int users; /* Number of active users */ int markedusers; /* Number of marked users */ - struct ast_conf_user *firstuser; /* Pointer to the first user struct */ - struct ast_conf_user *lastuser; /* Pointer to the last user struct */ + AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist; time_t start; /* Start time (s) */ int recording; /* recording status */ int isdynamic; /* Created on the fly? */ @@ -147,9 +147,11 @@ char *recordingformat; /* Format to record the Conference in */ char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */ char pinadmin[AST_MAX_EXTENSION]; /* If protected by a admin PIN */ - struct ast_conference *next; -} *confs; + AST_LIST_ENTRY(ast_conference) list; +}; +static AST_LIST_HEAD_STATIC(confs, ast_conference); + struct volume { int desired; /* Desired volume adjustment */ int actual; /* Actual volume adjustment (for channels that can't adjust) */ @@ -157,8 +159,7 @@ struct ast_conf_user { int user_no; /* User Number */ - struct ast_conf_user *prevuser; /* Pointer to the previous user */ - struct ast_conf_user *nextuser; /* Pointer to the next user */ + AST_LIST_ENTRY(ast_conf_user) list; int userflags; /* Flags as set in the conference */ int adminflags; /* Flags set by the Admin */ struct ast_channel *chan; /* Connected channel */ @@ -187,12 +188,12 @@ VOL_DOWN, }; -AST_MUTEX_DEFINE_STATIC(conflock); - static int admin_exec(struct ast_channel *chan, void *data); static void *recordthread(void *args); +static void dispose_conf(struct ast_conference *conf); + #include "enter.h" #include "leave.h" @@ -409,7 +410,7 @@ if (!chan->_softhangup) res = ast_autoservice_start(chan); - ast_mutex_lock(&conflock); + AST_LIST_LOCK(&confs); switch(sound) { case ENTER: @@ -427,7 +428,7 @@ if (data) careful_write(conf->fd, data, len, 1); - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); if (!res) ast_autoservice_stop(chan); @@ -438,9 +439,8 @@ struct ast_conference *cnf; struct zt_confinfo ztc; - ast_mutex_lock(&conflock); - - for (cnf = confs; cnf; cnf = cnf->next) { + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { if (!strcmp(confno, cnf->confno)) break; } @@ -485,18 +485,19 @@ cnf->start = time(NULL); cnf->zapconf = ztc.confno; cnf->isdynamic = dynamic; - cnf->firstuser = NULL; - cnf->lastuser = NULL; cnf->locked = 0; + AST_LIST_HEAD_INIT_NOLOCK(&cnf->userlist); if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno); - cnf->next = confs; - confs = cnf; + AST_LIST_INSERT_HEAD(&confs, cnf, list); } else ast_log(LOG_WARNING, "Out of memory\n"); } - cnfout: - ast_mutex_unlock(&conflock); +cnfout: + /* Reference counting */ + if (cnf) + cnf->users++; + AST_LIST_UNLOCK(&confs); return cnf; } @@ -535,13 +536,14 @@ if (argc == 1) { /* 'MeetMe': List all the conferences */ now = time(NULL); - cnf = confs; - if (!cnf) { + AST_LIST_LOCK(&confs); + if (!AST_LIST_FIRST(&confs)) { ast_cli(fd, "No active MeetMe conferences.\n"); + AST_LIST_UNLOCK(&confs); return RESULT_SUCCESS; } ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation"); - while(cnf) { + AST_LIST_TRAVERSE(&confs, cnf, list) { if (cnf->markedusers == 0) strcpy(cmdline, "N/A "); else @@ -553,9 +555,9 @@ ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static"); total += cnf->users; - cnf = cnf->next; } ast_cli(fd, "* Total number of MeetMe users: %d\n", total); + AST_LIST_UNLOCK(&confs); return RESULT_SUCCESS; } if (argc < 3) @@ -602,24 +604,22 @@ } } else if(strcmp(argv[1], "list") == 0) { /* List all the users in a conference */ - if (!confs) { + if (!AST_LIST_FIRST(&confs)) { ast_cli(fd, "No active conferences.\n"); return RESULT_SUCCESS; } - cnf = confs; - /* Find the right conference */ - while(cnf) { + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { if (strcmp(cnf->confno, argv[2]) == 0) break; - if (cnf->next) { - cnf = cnf->next; - } else { - ast_cli(fd, "No such conference: %s.\n",argv[2]); - return RESULT_SUCCESS; - } } + if (!cnf) { + ast_cli(fd, "No such conference: %s.\n", argv[2]); + AST_LIST_UNLOCK(&confs); + return RESULT_SUCCESS; + } /* Show all the users */ - for (user = cnf->firstuser; user; user = user->nextuser) + AST_LIST_TRAVERSE(&cnf->userlist, user, list) { ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s\n", user->user_no, user->chan->cid.cid_num ? user->chan->cid.cid_num : "", @@ -629,7 +629,9 @@ user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "", user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : "", istalking(user->talking)); + } ast_cli(fd,"%d users in that conference.\n",cnf->users); + AST_LIST_UNLOCK(&confs); return RESULT_SUCCESS; } else @@ -649,6 +651,7 @@ char usrno[50] = ""; char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"}; char *myline; + char *res = NULL; if (pos == 1) { /* Command */ @@ -661,17 +664,16 @@ } } else if (pos == 2) { /* Conference Number */ - ast_mutex_lock(&conflock); - cnf = confs; - while(cnf) { + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { if (!strncasecmp(word, cnf->confno, strlen(word))) { if (++which > state) break; } - cnf = cnf->next; } - ast_mutex_unlock(&conflock); - return cnf ? strdup(cnf->confno) : NULL; + res = cnf ? strdup(cnf->confno) : NULL; + AST_LIST_UNLOCK(&confs); + return res; } else if (pos == 3) { /* User Number || Conf Command option*/ if (strstr(line, "mute") || strstr(line, "kick")) { @@ -679,7 +681,7 @@ return strdup("all"); } which++; - ast_mutex_lock(&conflock); + AST_LIST_LOCK(&confs); /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */ myline = ast_strdupa(line); @@ -687,15 +689,15 @@ while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0)) ; } - - for (cnf = confs; cnf; cnf = cnf->next) { + + AST_LIST_TRAVERSE(&confs, cnf, list) { if (!strcmp(confno, cnf->confno)) break; } if (cnf) { /* Search for the user */ - for (usr = cnf->firstuser; usr; usr = usr->nextuser) { + AST_LIST_TRAVERSE(&cnf->userlist, usr, list) { snprintf(usrno, sizeof(usrno), "%d", usr->user_no); if (!strncasecmp(word, usrno, strlen(word))) { if (++which > state) @@ -703,7 +705,7 @@ } } } - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); return usr ? strdup(usrno) : NULL; } } @@ -749,34 +751,31 @@ } /* Remove the conference from the list and free it. - We assume that this was called while holding conflock. */ + XXX We assume that this was called while holding the confs list lock. */ static int conf_free(struct ast_conference *conf) { - struct ast_conference *prev = NULL, *cur = confs; + struct ast_conference *cur; - while (cur) { + AST_LIST_TRAVERSE_SAFE_BEGIN(&confs, cur, list) { if (cur == conf) { - if (prev) - prev->next = conf->next; - else - confs = conf->next; + AST_LIST_REMOVE_CURRENT(&confs, list); break; } - prev = cur; - cur = cur->next; } + AST_LIST_TRAVERSE_SAFE_END; if (!cur) ast_log(LOG_WARNING, "Conference not found\n"); if (conf->recording == MEETME_RECORD_ACTIVE) { conf->recording = MEETME_RECORD_TERMINATE; - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); while (1) { - ast_mutex_lock(&conflock); + usleep(1); + AST_LIST_LOCK(&confs); if (conf->recording == MEETME_RECORD_OFF) break; - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); } } @@ -794,6 +793,7 @@ { struct ast_conf_user *user = calloc(1, sizeof(*user)); struct ast_conf_user *usr = NULL; + struct ast_conference *cnf; int fd; struct zt_confinfo ztc, ztc_empty; struct ast_frame *f; @@ -832,6 +832,18 @@ return ret; } + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { + if (cnf == conf) + break; + } + + if (cnf == NULL) { + ast_log(LOG_ERROR, "Conference went away while calling confrun()\n"); + AST_LIST_UNLOCK(&confs); + goto outrun; + } + if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) { conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"); if (!conf->recordingfilename) { @@ -856,39 +868,29 @@ /* Sorry, but this confernce is locked! */ if (!ast_streamfile(chan, "conf-locked", chan->language)) ast_waitstream(chan, ""); + AST_LIST_UNLOCK(&confs); goto outrun; } if (confflags & CONFFLAG_MARKEDUSER) conf->markedusers++; - - ast_mutex_lock(&conflock); - if (!conf->firstuser) { - /* Fill the first new User struct */ + + if (AST_LIST_LAST(&conf->userlist)) + user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1; + else user->user_no = 1; - conf->firstuser = user; - conf->lastuser = user; - } else { - /* Fill the new user struct */ - user->user_no = conf->lastuser->user_no + 1; - user->prevuser = conf->lastuser; - if (conf->lastuser->nextuser) { - ast_log(LOG_WARNING, "Error in User Management!\n"); - ast_mutex_unlock(&conflock); - goto outrun; - } else { - conf->lastuser->nextuser = user; - conf->lastuser = user; - } - } user->chan = chan; user->userflags = confflags; user->adminflags = 0; user->talking = -1; - conf->users++; - ast_mutex_unlock(&conflock); + /* User count is already incremented by virtue of the reference count */ + AST_LIST_INSERT_TAIL(&conf->userlist, user, list); + + /* Since we control a user in the userlist, our conference should never go away now. */ + AST_LIST_UNLOCK(&confs); + if (confflags & CONFFLAG_EXIT_CONTEXT) { if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) ast_copy_string(exitcontext, agifile, sizeof(exitcontext)); @@ -1033,7 +1035,7 @@ ztc.chan = 0; ztc.confno = conf->zapconf; - ast_mutex_lock(&conflock); + AST_LIST_LOCK(&confs); if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) { if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) { @@ -1054,7 +1056,7 @@ if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); close(fd); - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); goto outrun; } ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf); @@ -1073,7 +1075,7 @@ conf_play(chan, conf, ENTER); } - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); conf_flush(fd, chan); @@ -1402,7 +1404,7 @@ break; case '3': /* Eject last user */ menu_active = 0; - usr = conf->lastuser; + usr = AST_LIST_LAST(&conf->userlist); if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) { if(!ast_streamfile(chan, "conf-errormenu", chan->language)) ast_waitstream(chan, ""); @@ -1547,7 +1549,7 @@ reset_volumes(user); - ast_mutex_lock(&conflock); + AST_LIST_LOCK(&confs); if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) conf_play(chan, conf, LEAVE); @@ -1562,14 +1564,14 @@ ast_filedelete(user->namerecloc, NULL); } } - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); outrun: - ast_mutex_lock(&conflock); + AST_LIST_LOCK(&confs); if (confflags & CONFFLAG_MONITORTALKER && dsp) ast_dsp_free(dsp); - + if (user->user_no) { /* Only cleanup users who really joined! */ manager_event(EVENT_FLAG_CALL, "MeetmeLeave", "Channel: %s\r\n" @@ -1577,51 +1579,32 @@ "Meetme: %s\r\n" "Usernum: %d\r\n", chan->name, chan->uniqueid, conf->confno, user->user_no); - conf->users--; if (confflags & CONFFLAG_MARKEDUSER) conf->markedusers--; - if (!conf->users) { - /* No more users -- close this one out */ - conf_free(conf); - } else { - /* Remove the user struct */ - if (user == conf->firstuser) { - if (user->nextuser) { - /* There is another entry */ - user->nextuser->prevuser = NULL; - } else { - /* We are the only entry */ - conf->lastuser = NULL; - } - /* In either case */ - conf->firstuser = user->nextuser; - } else if (user == conf->lastuser){ - if (user->prevuser) - user->prevuser->nextuser = NULL; - else - ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n"); - conf->lastuser = user->prevuser; - } else { - if (user->nextuser) - user->nextuser->prevuser = user->prevuser; - else - ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n"); - if (user->prevuser) - user->prevuser->nextuser = user->nextuser; - else - ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n"); - } - } + + AST_LIST_REMOVE(&conf->userlist, user, list); + + dispose_conf(conf); /* Return the number of seconds the user was in the conf */ snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime)); pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs); } free(user); - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); return ret; } +/* Decrement reference counts, as incremented by find_conf() */ +static void dispose_conf(struct ast_conference *conf) +{ + AST_LIST_LOCK(&confs); + conf->users--; + if (conf->users == 0) + conf_free(conf); + AST_LIST_UNLOCK(&confs); +} + static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin, struct ast_flags *confflags) { @@ -1630,12 +1613,14 @@ struct ast_conference *cnf; /* Check first in the conference list */ - ast_mutex_lock(&conflock); - for (cnf = confs; cnf; cnf = cnf->next) { - if (!strcmp(confno, cnf->confno)) + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { + if (!strcmp(confno, cnf->confno)) { + cnf->users++; break; + } } - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); if (!cnf) { if (dynamic) { @@ -1742,9 +1727,10 @@ confnum = strsep(&localdata,"|"); conf = find_conf(chan, confnum, 0, 0, NULL, NULL); - if (conf) - count = conf->users; - else + if (conf) { + count = conf->users - 1; + dispose_conf(conf); + } else count = 0; if (!ast_strlen_zero(localdata)){ @@ -1824,15 +1810,15 @@ struct ast_variable *var; int confno_int; - ast_mutex_lock(&conflock); - for (cnf = confs; cnf; cnf = cnf->next) { + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { if (sscanf(cnf->confno, "%d", &confno_int) == 1) { /* Disqualify in use conference */ if (confno_int >= 0 && confno_int < 1024) map[confno_int]++; } } - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */ if ((empty_no_pin) || (!dynamic)) { @@ -1854,17 +1840,15 @@ } if (!dynamic) { /* For static: run through the list and see if this conference is empty */ - ast_mutex_lock(&conflock); - cnf = confs; - while (cnf) { + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { if (!strcmp(confno_tmp, cnf->confno)) { /* The conference exists, therefore it's not empty */ found = 1; break; } - cnf = cnf->next; } - ast_mutex_unlock(&conflock); + AST_LIST_UNLOCK(&confs); if (!found) { /* At this point, we have a confno_tmp (static conference) that is empty */ if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) { @@ -1984,12 +1968,6 @@ /* failed when getting the pin */ res = -1; allowretry = 0; - /* see if we need to get rid of the conference */ - ast_mutex_lock(&conflock); - if (!cnf->users) { - conf_free(cnf); - } - ast_mutex_unlock(&conflock); break; } @@ -2005,6 +1983,7 @@ /* Run the conference */ res = conf_run(chan, cnf, confflags.flags); } + dispose_conf(cnf); } } } while (allowretry); @@ -2025,11 +2004,9 @@ sscanf(callerident, "%i", &cid); - user = conf->firstuser; - while (user) { + AST_LIST_TRAVERSE(&conf->userlist, user, list) { if (user->user_no == cid) break; - user = user->nextuser; } return user; @@ -2045,9 +2022,8 @@ LOCAL_USER_ADD(u); - ast_mutex_lock(&conflock); /* The param has the conference number the user and the command to execute */ - if (!ast_strlen_zero(data)) { + if (!ast_strlen_zero(data)) { params = ast_strdupa((char *) data); conf = strsep(¶ms, "|"); command = strsep(¶ms, "|"); @@ -2055,15 +2031,12 @@ if (!command) { ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n"); - ast_mutex_unlock(&conflock); LOCAL_USER_REMOVE(u); return -1; } - for (cnf = confs; cnf; cnf = cnf->next) { - if (!strcmp(cnf->confno, conf)) - break; - } - + + cnf = find_conf(chan, conf, 0, 0, NULL, NULL); + if (caller) user = find_user(cnf, caller); @@ -2076,18 +2049,12 @@ cnf->locked = 0; break; case 75: /* K: kick all users*/ - user = cnf->firstuser; - while(user) { + AST_LIST_TRAVERSE(&cnf->userlist, user, list) { user->adminflags |= ADMINFLAG_KICKME; - if (user->nextuser) { - user = user->nextuser; - } else { - break; - } } break; case 101: /* e: Eject last user*/ - user = cnf->lastuser; + user = AST_LIST_LAST(&cnf->userlist); if (!(user->userflags & CONFFLAG_ADMIN)) { user->adminflags |= ADMINFLAG_KICKME; break; @@ -2102,15 +2069,9 @@ } break; case 78: /* N: Mute all users */ - user = cnf->firstuser; - while(user) { + AST_LIST_TRAVERSE(&cnf->userlist, user, list) { if (user && !(user->userflags & CONFFLAG_ADMIN)) user->adminflags |= ADMINFLAG_MUTED; - if (user->nextuser) { - user = user->nextuser; - } else { - break; - } } break; case 109: /* m: Unmute */ @@ -2121,16 +2082,10 @@ } break; case 110: /* n: Unmute all users */ - user = cnf->firstuser; - while(user) { + AST_LIST_TRAVERSE(&cnf->userlist, user, list) { if (user && (user-> adminflags & ADMINFLAG_MUTED)) { user->adminflags ^= ADMINFLAG_MUTED; } - if (user->nextuser) { - user = user->nextuser; - } else { - break; - } } break; case 107: /* k: Kick user */ @@ -2141,11 +2096,13 @@ } break; } + + /* Reference count */ + dispose_conf(cnf); } else { ast_log(LOG_NOTICE, "Conference Number not found\n"); } } - ast_mutex_unlock(&conflock); LOCAL_USER_REMOVE(u); @@ -2184,12 +2141,12 @@ } ast_frfree(f); if (cnf->recording == MEETME_RECORD_TERMINATE) { - ast_mutex_lock(&conflock); - ast_mutex_unlock(&conflock); break; } } + AST_LIST_LOCK(&confs); cnf->recording = MEETME_RECORD_OFF; + AST_LIST_UNLOCK(&confs); ast_closestream(s); } pthread_exit(0);