Index: channels/chan_alsa.c =================================================================== --- channels/chan_alsa.c (revision 140020) +++ channels/chan_alsa.c (working copy) @@ -279,6 +279,7 @@ int max, res; for (;;) { + pthread_testcancel(); FD_ZERO(&rfds); FD_ZERO(&wfds); max = sndcmd[0]; @@ -1283,6 +1284,23 @@ autoanswer_usage, autoanswer_complete, &cli_alsa_autoanswer_deprecated }, }; +static void cleanup(void) +{ + if (alsa.icard) { + snd_pcm_close(alsa.icard); + alsa.icard = NULL; + } + if (alsa.ocard) { + snd_pcm_close(alsa.ocard); + alsa.ocard = NULL; + } + if (sndcmd[0] > 0) { + close(sndcmd[0]); + close(sndcmd[1]); + sndcmd[0] = sndcmd[1] = -1; + } +} + static int load_module(void) { int res; @@ -1325,7 +1343,7 @@ res = pipe(sndcmd); if (res) { ast_log(LOG_ERROR, "Unable to create pipe\n"); - return -1; + return AST_MODULE_LOAD_DECLINE; } res = soundcard_init(); if (res < 0) { @@ -1333,13 +1351,15 @@ ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n"); ast_verbose(VERBOSE_PREFIX_2 "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n"); } - return 0; + cleanup(); + return AST_MODULE_LOAD_DECLINE; } res = ast_channel_register(&alsa_tech); if (res < 0) { ast_log(LOG_ERROR, "Unable to register channel class 'Console'\n"); - return -1; + cleanup(); + return AST_MODULE_LOAD_DECLINE; } ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry)); @@ -1348,7 +1368,7 @@ if (alsa_monitor_start()) ast_log(LOG_ERROR, "Problem starting Monitoring\n"); #endif - return 0; + return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) @@ -1356,18 +1376,27 @@ ast_channel_unregister(&alsa_tech); ast_cli_unregister_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry)); - if (alsa.icard) - snd_pcm_close(alsa.icard); - if (alsa.ocard) - snd_pcm_close(alsa.ocard); - if (sndcmd[0] > 0) { - close(sndcmd[0]); - close(sndcmd[1]); + if (alsa.owner) { + ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD); + usleep(1); } - if (alsa.owner) - ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD); - if (alsa.owner) + if (alsa.owner) { + /* Leave in a consistent state */ + ast_channel_register(&alsa_tech); + ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry)); return -1; + } + + pthread_cancel(sthread); + usleep(1); + if (pthread_cancel(sthread) != ESRCH) { + /* Leave in a consistent state */ + ast_channel_register(&alsa_tech); + ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry)); + return -1; + } + cleanup(); + return 0; } Index: channels/chan_oss.c =================================================================== --- channels/chan_oss.c (revision 140020) +++ channels/chan_oss.c (working copy) @@ -1823,6 +1823,49 @@ return NULL; } +static int unload_config(void) +{ + struct chan_oss_pvt *o, *next; + + for (o = oss_default.next, next = (o ? o->next : NULL); o; o = next, next = (o ? o->next : NULL)) { + /* Stop any existing audio */ + if (o->owner) { + ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD); + /* Give it time to die */ + usleep(1); + } + + /* If the channel is still there now, something is very wrong */ + if (o->owner) { /* XXX how ??? */ + return -1; + } + + /* Then, tell the maintenance thread to go away */ + pthread_cancel(o->sthread); + usleep(1); + if (pthread_cancel(o->sthread) != ESRCH) { + /* Cancel unsuccessful? */ + return -1; + } + + /* Now we can cleanup our file handles */ + close(o->sounddev); + if (o->sndcmd[0] > 0) { + close(o->sndcmd[0]); + close(o->sndcmd[1]); + } + + /* And finally, free memory */ + if (o->mixer_cmd) { + ast_free(o->mixer_cmd); + o->mixer_cmd = NULL; + } + ast_free(o->name); + ast_free(o); + } + return 0; +} + static int load_module(void) { struct ast_config *cfg = NULL; @@ -1846,13 +1889,20 @@ if (find_desc(oss_active) == NULL) { ast_log(LOG_NOTICE, "Device %s not found\n", oss_active); /* XXX we could default to 'dsp' perhaps ? */ - /* XXX should cleanup allocated memory etc. */ - return AST_MODULE_LOAD_FAILURE; + + /* Due to thread generation/cancellation semantics, we must allow a + * thread time to actually start up before it becomes safely + * cancellable. We could crash, otherwise. */ + usleep(1); + return unload_config() ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE; } if (ast_channel_register(&oss_tech)) { ast_log(LOG_ERROR, "Unable to register channel type 'OSS'\n"); - return AST_MODULE_LOAD_FAILURE; + + /* Same logic as above. */ + usleep(1); + return unload_config() ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE; } ast_cli_register_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry)); @@ -1863,25 +1913,20 @@ static int unload_module(void) { - struct chan_oss_pvt *o; - + /* First, make sure nothing can start using our resources while + * we're trying to destroy them. */ ast_channel_unregister(&oss_tech); ast_cli_unregister_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry)); - for (o = oss_default.next; o; o = o->next) { - close(o->sounddev); - if (o->sndcmd[0] > 0) { - close(o->sndcmd[0]); - close(o->sndcmd[1]); - } - if (o->owner) - ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD); - if (o->owner) /* XXX how ??? */ - return -1; - /* XXX what about the thread ? */ - /* XXX what about the memory allocated ? */ + if (unload_config()) { + /* Restore pointers, for consistency */ + ast_channel_register(&oss_tech); + ast_cli_register_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry)); + /* and decline to unload */ + return -1; + } else { + return 0; } - return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OSS Console Channel Driver");