Index: res/res_musiconhold.c =================================================================== --- res/res_musiconhold.c (revision 231886) +++ res/res_musiconhold.c (working copy) @@ -50,6 +50,8 @@ #include #endif +#define REF_DEBUG + #include "asterisk/lock.h" #include "asterisk/file.h" #include "asterisk/channel.h" @@ -178,6 +180,8 @@ }; static struct ao2_container *mohclasses; +static struct ao2_container *deleted_classes; +static pthread_t deleted_thread; #define LOCAL_MPG_123 "/usr/local/bin/mpg123" #define MPG_123 "/usr/bin/mpg123" @@ -514,6 +518,43 @@ return fds[0]; } +static void *deleted_monitor(void *data) +{ + struct ao2_iterator iter; + struct mohclass *class; + struct ast_module *mod = NULL; + + for (;;) { + pthread_testcancel(); + if (ao2_container_count(deleted_classes) == 0) { + if (mod) { + ast_module_unref(mod); + mod = NULL; + } + poll(NULL, 0, -1); + } else { + /* While deleted classes are still in use, prohibit unloading */ + mod = ast_module_ref(ast_module_info->self); + } + pthread_testcancel(); + iter = ao2_iterator_init(deleted_classes, 0); + while ((class = ao2_iterator_next(&iter))) { + if (ao2_ref(class, 0) == 2) { + ao2_unlink(deleted_classes, class); + } + ao2_ref(class, -1); + } + ao2_iterator_destroy(&iter); + if (ao2_container_count(deleted_classes) == 0 && mod) { + ast_module_unref(mod); + mod = NULL; + } + pthread_testcancel(); + sleep(60); + } + return NULL; +} + static void *monmp3thread(void *data) { #define MOH_MS_INTERVAL 100 @@ -574,11 +615,17 @@ class->srcfd = -1; pthread_testcancel(); if (class->pid > 1) { - killpg(class->pid, SIGHUP); + if (killpg(class->pid, SIGHUP) < 0 && kill(class->pid, SIGHUP) < 0) { + ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno)); + } usleep(100000); - killpg(class->pid, SIGTERM); + if (killpg(class->pid, SIGTERM) < 0 && kill(class->pid, SIGTERM) < 0) { + ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno)); + } usleep(100000); - killpg(class->pid, SIGKILL); + if (killpg(class->pid, SIGKILL) < 0 && kill(class->pid, SIGKILL) < 0) { + ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno)); + } class->pid = 0; } } else { @@ -707,7 +754,9 @@ return 0; } -static struct mohclass *get_mohbyname(const char *name, int warn) +#define get_mohbyname(a,b) _get_mohbyname(a,b,__FILE__,__LINE__,__PRETTY_FUNCTION__) + +static struct mohclass *_get_mohbyname(const char *name, int warn, const char *file, int lineno, const char *funcname) { struct mohclass *moh = NULL; struct mohclass tmp_class = { @@ -716,7 +765,11 @@ ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name)); - moh = ao2_t_find(mohclasses, &tmp_class, 0, "Finding by name"); +#ifdef REF_DEBUG + moh = _ao2_find_debug(mohclasses, &tmp_class, 0, file, lineno, funcname); +#else + moh = _ao2_find(mohclasses, &tmp_class, 0); +#endif if (!moh && warn) { ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name); @@ -1101,6 +1154,9 @@ struct moh_files_state *state = chan->music_state; if (state) { + if (state->class) { + state->class = mohclass_unref(state->class, "Channel MOH state destruction"); + } ast_free(chan->music_state); chan->music_state = NULL; } @@ -1353,9 +1409,20 @@ { struct mohclass *class = obj; struct mohdata *member; + pthread_t tid = 0; ast_debug(1, "Destroying MOH class '%s'\n", class->name); + /* Kill the thread first, so it cannot restart the child process while the + * class is being destroyed */ + if (class->thread > 0) { + tid = class->thread; + class->thread = AST_PTHREADT_NULL; + pthread_cancel(tid); + /* We'll collect the exit status later, after we ensure all the readers + * are dead. */ + } + if (class->pid > 1) { char buff[8192]; int bytes, tbytes = 0, stime = 0, pid = 0; @@ -1369,11 +1436,17 @@ /* Back when this was just mpg123, SIGKILL was fine. Now we need * to give the process a reason and time enough to kill off its * children. */ - killpg(pid, SIGHUP); + if (killpg(class->pid, SIGHUP) < 0 && kill(class->pid, SIGHUP) < 0) { + ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno)); + } usleep(100000); - killpg(pid, SIGTERM); + if (killpg(class->pid, SIGTERM) < 0 && kill(class->pid, SIGTERM) < 0) { + ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno)); + } usleep(100000); - killpg(pid, SIGKILL); + if (killpg(class->pid, SIGKILL) < 0 && kill(class->pid, SIGKILL) < 0) { + ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno)); + } while ((ast_wait_for_input(class->srcfd, 100) > 0) && (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) { @@ -1389,12 +1462,6 @@ free(member); } - if (class->thread) { - pthread_cancel(class->thread); - pthread_join(class->thread, NULL); - class->thread = AST_PTHREADT_NULL; - } - if (class->filearray) { int i; for (i = 0; i < class->total_files; i++) { @@ -1403,6 +1470,11 @@ free(class->filearray); class->filearray = NULL; } + + /* Finally, collect the exit status of the monitor thread */ + if (tid > 0) { + pthread_join(tid, NULL); + } } static int moh_class_mark(void *obj, void *arg, int flags) @@ -1418,7 +1490,12 @@ { struct mohclass *class = obj; - return class->delete ? CMP_MATCH : 0; + if (class->delete) { + ao2_link(deleted_classes, obj); + pthread_kill(deleted_thread, SIGURG); + return CMP_MATCH; + } + return 0; } static int load_moh_classes(int reload) @@ -1620,6 +1697,19 @@ } } ao2_iterator_destroy(&i); + i = ao2_iterator_init(deleted_classes, 0); + for (; (class = ao2_t_iterator_next(&i, "Show deleted classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) { + ast_cli(a->fd, "(Deleted) Class: %s (%d)\n", class->name, ao2_ref(class, 0) - 2); + ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "")); + ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "")); + if (ast_test_flag(class, MOH_CUSTOM)) { + ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "")); + } + if (strcasecmp(class->mode, "files")) { + ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format)); + } + } + ao2_iterator_destroy(&i); return CLI_SUCCESS; } @@ -1652,6 +1742,14 @@ return AST_MODULE_LOAD_DECLINE; } + if (!(deleted_classes = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh deleted class container"))) { + return AST_MODULE_LOAD_DECLINE; + } + + if (ast_pthread_create_background(&deleted_thread, NULL, deleted_monitor, NULL)) { + return AST_MODULE_LOAD_DECLINE; + } + if (!load_moh_classes(0)) { /* No music classes configured, so skip it */ ast_log(LOG_WARNING, "No music on hold classes configured, " "disabling music on hold.\n"); @@ -1720,6 +1818,10 @@ ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry)); ast_unregister_atexit(ast_moh_destroy); + pthread_cancel(deleted_thread); + pthread_kill(deleted_thread, SIGURG); + pthread_join(deleted_thread, NULL); + return res; }