Avoid cppcheck warnings; removing unused vars and a bit of cleanup.
[asterisk/asterisk.git] / res / res_musiconhold.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Routines implementing music on hold
22  *
23  * \arg See also \ref Config_moh
24  * 
25  * \author Mark Spencer <markster@digium.com>
26  */
27
28 /*** MODULEINFO
29         <conflict>win32</conflict>
30         <support_level>core</support_level>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include <ctype.h>
38 #include <signal.h>
39 #include <sys/time.h>
40 #include <sys/signal.h>
41 #include <netinet/in.h>
42 #include <sys/stat.h>
43 #include <dirent.h>
44
45 #ifdef SOLARIS
46 #include <thread.h>
47 #endif
48
49 #include "asterisk/lock.h"
50 #include "asterisk/file.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/app.h"
54 #include "asterisk/module.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/say.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/config.h"
59 #include "asterisk/utils.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/stringfields.h"
62 #include "asterisk/linkedlists.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/paths.h"
65 #include "asterisk/astobj2.h"
66 #include "asterisk/timing.h"
67 #include "asterisk/time.h"
68 #include "asterisk/poll-compat.h"
69
70 #define INITIAL_NUM_FILES   8
71 #define HANDLE_REF      1
72 #define DONT_UNREF      0
73
74 /*** DOCUMENTATION
75         <application name="MusicOnHold" language="en_US">
76                 <synopsis>
77                         Play Music On Hold indefinitely.
78                 </synopsis>
79                 <syntax>
80                         <parameter name="class" required="true" />
81                         <parameter name="duration" />
82                 </syntax>
83                 <description>
84                         <para>Plays hold music specified by class. If omitted, the default music
85                         source for the channel will be used. Change the default class with
86                         Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
87                         specified number of seconds. If duration is ommited, music plays indefinitely.
88                         Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
89                         <para>This application does not automatically answer and should be preceeded by
90                         an application such as Answer() or Progress().</para>
91                 </description>
92         </application>
93         <application name="WaitMusicOnHold" language="en_US">
94                 <synopsis>
95                         Wait, playing Music On Hold.
96                 </synopsis>
97                 <syntax>
98                         <parameter name="delay" required="true" />
99                 </syntax>
100                 <description>
101                         <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
102                         <para>Plays hold music specified number of seconds. Returns <literal>0</literal> when done,
103                         or <literal>-1</literal> on hangup. If no hold music is available, the delay will still occur
104                         with no sound.</para>
105                         <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
106                 </description>
107         </application>
108         <application name="SetMusicOnHold" language="en_US">
109                 <synopsis>
110                         Set default Music On Hold class.
111                 </synopsis>
112                 <syntax>
113                         <parameter name="class" required="yes" />
114                 </syntax>
115                 <description>
116                         <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
117                         <para>Sets the default class for music on hold for a given channel.
118                         When music on hold is activated, this class will be used to select which
119                         music is played.</para>
120                         <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
121                 </description>
122         </application>
123         <application name="StartMusicOnHold" language="en_US">
124                 <synopsis>
125                         Play Music On Hold.
126                 </synopsis>
127                 <syntax>
128                         <parameter name="class" required="true" />
129                 </syntax>
130                 <description>
131                         <para>Starts playing music on hold, uses default music class for channel.
132                         Starts playing music specified by class. If omitted, the default music
133                         source for the channel will be used. Always returns <literal>0</literal>.</para>
134                 </description>
135         </application>
136         <application name="StopMusicOnHold" language="en_US">
137                 <synopsis>
138                         Stop playing Music On Hold.
139                 </synopsis>
140                 <syntax />
141                 <description>
142                         <para>Stops playing music on hold.</para>
143                 </description>
144         </application>
145  ***/
146
147 static const char play_moh[] = "MusicOnHold";
148 static const char wait_moh[] = "WaitMusicOnHold";
149 static const char set_moh[] = "SetMusicOnHold";
150 static const char start_moh[] = "StartMusicOnHold";
151 static const char stop_moh[] = "StopMusicOnHold";
152
153 static int respawn_time = 20;
154
155 struct moh_files_state {
156         /*! Holds a reference to the MOH class. */
157         struct mohclass *class;
158         char name[MAX_MUSICCLASS];
159         struct ast_format origwfmt;
160         struct ast_format mohwfmt;
161         int announcement;
162         int samples;
163         int sample_queue;
164         int pos;
165         int save_pos;
166         int save_total;
167         char save_pos_filename[PATH_MAX];
168 };
169
170 #define MOH_QUIET               (1 << 0)
171 #define MOH_SINGLE              (1 << 1)
172 #define MOH_CUSTOM              (1 << 2)
173 #define MOH_RANDOMIZE           (1 << 3)
174 #define MOH_SORTALPHA           (1 << 4)
175
176 #define MOH_CACHERTCLASSES      (1 << 5)        /*!< Should we use a separate instance of MOH for each user or not */
177 #define MOH_ANNOUNCEMENT        (1 << 6)                        /*!< Do we play announcement files between songs on this channel? */
178
179 /* Custom astobj2 flag */
180 #define MOH_NOTDELETED          (1 << 30)       /*!< Find only records that aren't deleted? */
181
182 static struct ast_flags global_flags[1] = {{0}};        /*!< global MOH_ flags */
183
184 struct mohclass {
185         char name[MAX_MUSICCLASS];
186         char dir[256];
187         char args[256];
188         char announcement[256];
189         char mode[80];
190         char digit;
191         /*! A dynamically sized array to hold the list of filenames in "files" mode */
192         char **filearray;
193         /*! The current size of the filearray */
194         int allowed_files;
195         /*! The current number of files loaded into the filearray */
196         int total_files;
197         unsigned int flags;
198         /*! The format from the MOH source, not applicable to "files" mode */
199         struct ast_format format;
200         /*! The pid of the external application delivering MOH */
201         int pid;
202         time_t start;
203         pthread_t thread;
204         /*! Source of audio */
205         int srcfd;
206         /*! Generic timer */
207         struct ast_timer *timer;
208         /*! Created on the fly, from RT engine */
209         unsigned int realtime:1;
210         unsigned int delete:1;
211         AST_LIST_HEAD_NOLOCK(, mohdata) members;
212         AST_LIST_ENTRY(mohclass) list;
213 };
214
215 struct mohdata {
216         int pipe[2];
217         struct ast_format origwfmt;
218         struct mohclass *parent;
219         struct ast_frame f;
220         AST_LIST_ENTRY(mohdata) list;
221 };
222
223 static struct ao2_container *mohclasses;
224
225 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
226 #define MPG_123 "/usr/bin/mpg123"
227 #define MAX_MP3S 256
228
229 static int reload(void);
230
231 #define mohclass_ref(class,string)   (ao2_t_ref((class), +1, (string)), class)
232
233 #ifndef REF_DEBUG
234 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
235 #else
236 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
237 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
238 {
239         struct mohclass *dup;
240         if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
241                 if (__ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
242                         FILE *ref = fopen("/tmp/refs", "a");
243                         if (ref) {
244                                 fprintf(ref, "%p =1   %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
245                                 fclose(ref);
246                         }
247                         ast_log(LOG_WARNING, "Attempt to unref mohclass %p (%s) when only 1 ref remained, and class is still in a container! (at %s:%d (%s))\n",
248                                 class, class->name, file, line, funcname);
249                 } else {
250                         ao2_ref(class, -1);
251                 }
252         } else {
253                 ao2_t_ref(class, -1, (char *) tag);
254         }
255         return NULL;
256 }
257 #endif
258
259 static void moh_files_release(struct ast_channel *chan, void *data)
260 {
261         struct moh_files_state *state;
262
263         if (!chan || !ast_channel_music_state(chan)) {
264                 return;
265         }
266
267         state = ast_channel_music_state(chan);
268
269         if (ast_channel_stream(chan)) {
270                 ast_closestream(ast_channel_stream(chan));
271                 ast_channel_stream_set(chan, NULL);
272         }
273         
274         ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
275
276         ast_format_clear(&state->mohwfmt); /* make sure to clear this format before restoring the original format. */
277         if (state->origwfmt.id && ast_set_write_format(chan, &state->origwfmt)) {
278                 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan), ast_getformatname(&state->origwfmt));
279         }
280
281         state->save_pos = state->pos;
282         state->announcement = 0;
283
284         state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
285 }
286
287 static int ast_moh_files_next(struct ast_channel *chan) 
288 {
289         struct moh_files_state *state = ast_channel_music_state(chan);
290         int tries;
291
292         /* Discontinue a stream if it is running already */
293         if (ast_channel_stream(chan)) {
294                 ast_closestream(ast_channel_stream(chan));
295                 ast_channel_stream_set(chan, NULL);
296         }
297
298         if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
299                 state->announcement = 1;
300                 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
301                         ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
302                         return 0;
303                 }
304         } else {
305                 state->announcement = 0;
306         }
307
308         if (!state->class->total_files) {
309                 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
310                 return -1;
311         }
312
313         if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
314                 /* First time so lets play the file. */
315                 state->save_pos = -1;
316         } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) {
317                 /* If a specific file has been saved confirm it still exists and that it is still valid */
318                 state->pos = state->save_pos;
319                 state->save_pos = -1;
320         } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
321                 /* Get a random file and ensure we can open it */
322                 for (tries = 0; tries < 20; tries++) {
323                         state->pos = ast_random() % state->class->total_files;
324                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
325                                 break;
326                         }
327                 }
328                 state->save_pos = -1;
329                 state->samples = 0;
330         } else {
331                 /* This is easy, just increment our position and make sure we don't exceed the total file count */
332                 state->pos++;
333                 state->pos %= state->class->total_files;
334                 state->save_pos = -1;
335                 state->samples = 0;
336         }
337
338         for (tries = 0; tries < state->class->total_files; ++tries) {
339                 if (ast_openstream_full(chan, state->class->filearray[state->pos], ast_channel_language(chan), 1)) {
340                         break;
341                 }
342
343                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
344                 state->pos++;
345                 state->pos %= state->class->total_files;
346         }
347
348         if (tries == state->class->total_files) {
349                 return -1;
350         }
351
352         /* Record the pointer to the filename for position resuming later */
353         ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));
354
355         ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->class->filearray[state->pos]);
356
357         if (state->samples) {
358                 size_t loc;
359                 /* seek *SHOULD* be good since it's from a known location */
360                 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
361                 /* if the seek failed then recover because if there is not a valid read,
362                  * moh_files_generate will return -1 and MOH will stop */
363                 loc = ast_tellstream(ast_channel_stream(chan));
364                 if (state->samples > loc && loc) {
365                         /* seek one sample from the end for one guaranteed valid read */
366                         ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
367                 }
368         }
369
370         return 0;
371 }
372
373 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
374 {
375         struct ast_frame *f = NULL;
376
377         if (!(ast_channel_stream(chan) && (f = ast_readframe(ast_channel_stream(chan))))) {
378                 if (!ast_moh_files_next(chan))
379                         f = ast_readframe(ast_channel_stream(chan));
380         }
381
382         return f;
383 }
384
385 static void moh_files_write_format_change(struct ast_channel *chan, void *data)
386 {
387         struct moh_files_state *state = ast_channel_music_state(chan);
388
389         /* In order to prevent a recursive call to this function as a result
390          * of setting the moh write format back on the channel. Clear
391          * the moh write format before setting the write format on the channel.*/
392         if (&state->origwfmt.id) {
393                 struct ast_format tmp;
394
395                 ast_format_copy(&tmp, ast_channel_writeformat(chan));
396                 if (state->mohwfmt.id) {
397                         ast_format_clear(&state->origwfmt);
398                         ast_set_write_format(chan, &state->mohwfmt);
399                 }
400                 ast_format_copy(&state->origwfmt, &tmp);
401         }
402 }
403
404 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
405 {
406         struct moh_files_state *state = ast_channel_music_state(chan);
407         struct ast_frame *f = NULL;
408         int res = 0;
409
410         state->sample_queue += samples;
411
412         while (state->sample_queue > 0) {
413                 ast_channel_lock(chan);
414                 if ((f = moh_files_readframe(chan))) {
415                         /* We need to be sure that we unlock
416                          * the channel prior to calling
417                          * ast_write. Otherwise, the recursive locking
418                          * that occurs can cause deadlocks when using
419                          * indirect channels, like local channels
420                          */
421                         ast_channel_unlock(chan);
422                         state->samples += f->samples;
423                         state->sample_queue -= f->samples;
424                         if (ast_format_cmp(&f->subclass.format, &state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
425                                 ast_format_copy(&state->mohwfmt, &f->subclass.format);
426                         }
427                         res = ast_write(chan, f);
428                         ast_frfree(f);
429                         if (res < 0) {
430                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
431                                 return -1;
432                         }
433                 } else {
434                         ast_channel_unlock(chan);
435                         return -1;      
436                 }
437         }
438         return res;
439 }
440
441 static void *moh_files_alloc(struct ast_channel *chan, void *params)
442 {
443         struct moh_files_state *state;
444         struct mohclass *class = params;
445
446         if (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
447                 ast_channel_music_state_set(chan, state);
448                 ast_module_ref(ast_module_info->self);
449         } else {
450                 state = ast_channel_music_state(chan);
451                 if (!state) {
452                         return NULL;
453                 }
454                 if (state->class) {
455                         mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
456                         ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
457                 }
458         }
459
460         /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
461          * malloc may allocate a different class to the same memory block.  This
462          * might only happen when two reloads are generated in a short period of
463          * time, but it's still important to protect against.
464          * PROG: Compare the quick operation first, to save CPU. */
465         if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
466                 memset(state, 0, sizeof(*state));
467                 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
468                         state->pos = ast_random() % class->total_files;
469                 }
470         }
471
472         state->class = mohclass_ref(class, "Reffing music class for channel");
473         ast_format_copy(&state->origwfmt, ast_channel_writeformat(chan));
474         ast_format_copy(&state->mohwfmt, ast_channel_writeformat(chan));
475
476         /* For comparison on restart of MOH (see above) */
477         ast_copy_string(state->name, class->name, sizeof(state->name));
478         state->save_total = class->total_files;
479
480         ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, ast_channel_name(chan));
481         
482         return ast_channel_music_state(chan);
483 }
484
485 static int moh_digit_match(void *obj, void *arg, int flags)
486 {
487         char *digit = arg;
488         struct mohclass *class = obj;
489
490         return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
491 }
492
493 /*! \note This function should be called with the mohclasses list locked */
494 static struct mohclass *get_mohbydigit(char digit)
495 {
496         return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
497 }
498
499 static void moh_handle_digit(struct ast_channel *chan, char digit)
500 {
501         struct mohclass *class;
502         const char *classname = NULL;
503
504         if ((class = get_mohbydigit(digit))) {
505                 classname = ast_strdupa(class->name);
506                 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
507                 ast_channel_musicclass_set(chan, classname);
508                 ast_moh_stop(chan);
509                 ast_moh_start(chan, classname, NULL);
510         }
511 }
512
513 static struct ast_generator moh_file_stream = 
514 {
515         .alloc    = moh_files_alloc,
516         .release  = moh_files_release,
517         .generate = moh_files_generator,
518         .digit    = moh_handle_digit,
519         .write_format_change = moh_files_write_format_change,
520 };
521
522 static int spawn_mp3(struct mohclass *class)
523 {
524         int fds[2];
525         int files = 0;
526         char fns[MAX_MP3S][80];
527         char *argv[MAX_MP3S + 50];
528         char xargs[256];
529         char *argptr;
530         int argc = 0;
531         DIR *dir = NULL;
532         struct dirent *de;
533
534         
535         if (!strcasecmp(class->dir, "nodir")) {
536                 files = 1;
537         } else {
538                 dir = opendir(class->dir);
539                 if (!dir && strncasecmp(class->dir, "http://", 7)) {
540                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
541                         return -1;
542                 }
543         }
544
545         if (!ast_test_flag(class, MOH_CUSTOM)) {
546                 argv[argc++] = "mpg123";
547                 argv[argc++] = "-q";
548                 argv[argc++] = "-s";
549                 argv[argc++] = "--mono";
550                 argv[argc++] = "-r";
551                 argv[argc++] = "8000";
552                 
553                 if (!ast_test_flag(class, MOH_SINGLE)) {
554                         argv[argc++] = "-b";
555                         argv[argc++] = "2048";
556                 }
557                 
558                 argv[argc++] = "-f";
559                 
560                 if (ast_test_flag(class, MOH_QUIET))
561                         argv[argc++] = "4096";
562                 else
563                         argv[argc++] = "8192";
564                 
565                 /* Look for extra arguments and add them to the list */
566                 ast_copy_string(xargs, class->args, sizeof(xargs));
567                 argptr = xargs;
568                 while (!ast_strlen_zero(argptr)) {
569                         argv[argc++] = argptr;
570                         strsep(&argptr, ",");
571                 }
572         } else  {
573                 /* Format arguments for argv vector */
574                 ast_copy_string(xargs, class->args, sizeof(xargs));
575                 argptr = xargs;
576                 while (!ast_strlen_zero(argptr)) {
577                         argv[argc++] = argptr;
578                         strsep(&argptr, " ");
579                 }
580         }
581
582         if (!strncasecmp(class->dir, "http://", 7)) {
583                 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
584                 argv[argc++] = fns[files];
585                 files++;
586         } else if (dir) {
587                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
588                         if ((strlen(de->d_name) > 3) && 
589                             ((ast_test_flag(class, MOH_CUSTOM) && 
590                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
591                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
592                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
593                                 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
594                                 argv[argc++] = fns[files];
595                                 files++;
596                         }
597                 }
598         }
599         argv[argc] = NULL;
600         if (dir) {
601                 closedir(dir);
602         }
603         if (pipe(fds)) {        
604                 ast_log(LOG_WARNING, "Pipe failed\n");
605                 return -1;
606         }
607         if (!files) {
608                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
609                 close(fds[0]);
610                 close(fds[1]);
611                 return -1;
612         }
613         if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
614                 sleep(respawn_time - (time(NULL) - class->start));
615         }
616
617         time(&class->start);
618         class->pid = ast_safe_fork(0);
619         if (class->pid < 0) {
620                 close(fds[0]);
621                 close(fds[1]);
622                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
623                 return -1;
624         }
625         if (!class->pid) {
626                 if (ast_opt_high_priority)
627                         ast_set_priority(0);
628
629                 close(fds[0]);
630                 /* Stdout goes to pipe */
631                 dup2(fds[1], STDOUT_FILENO);
632
633                 /* Close everything else */
634                 ast_close_fds_above_n(STDERR_FILENO);
635
636                 /* Child */
637                 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
638                         ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
639                         _exit(1);
640                 }
641                 setpgid(0, getpid());
642                 if (ast_test_flag(class, MOH_CUSTOM)) {
643                         execv(argv[0], argv);
644                 } else {
645                         /* Default install is /usr/local/bin */
646                         execv(LOCAL_MPG_123, argv);
647                         /* Many places have it in /usr/bin */
648                         execv(MPG_123, argv);
649                         /* Check PATH as a last-ditch effort */
650                         execvp("mpg123", argv);
651                 }
652                 /* Can't use logger, since log FDs are closed */
653                 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
654                 close(fds[1]);
655                 _exit(1);
656         } else {
657                 /* Parent */
658                 close(fds[1]);
659         }
660         return fds[0];
661 }
662
663 static void *monmp3thread(void *data)
664 {
665 #define MOH_MS_INTERVAL         100
666
667         struct mohclass *class = data;
668         struct mohdata *moh;
669         short sbuf[8192];
670         int res = 0, res2;
671         int len;
672         struct timeval deadline, tv_tmp;
673
674         deadline.tv_sec = 0;
675         deadline.tv_usec = 0;
676         for(;/* ever */;) {
677                 pthread_testcancel();
678                 /* Spawn mp3 player if it's not there */
679                 if (class->srcfd < 0) {
680                         if ((class->srcfd = spawn_mp3(class)) < 0) {
681                                 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
682                                 /* Try again later */
683                                 sleep(500);
684                                 pthread_testcancel();
685                         }
686                 }
687                 if (class->timer) {
688                         struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
689
690 #ifdef SOLARIS
691                         thr_yield();
692 #endif
693                         /* Pause some amount of time */
694                         if (ast_poll(&pfd, 1, -1) > 0) {
695                                 ast_timer_ack(class->timer, 1);
696                                 res = 320;
697                         } else {
698                                 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
699                                 res = 0;
700                         }
701                         pthread_testcancel();
702                 } else {
703                         long delta;
704                         /* Reliable sleep */
705                         tv_tmp = ast_tvnow();
706                         if (ast_tvzero(deadline))
707                                 deadline = tv_tmp;
708                         delta = ast_tvdiff_ms(tv_tmp, deadline);
709                         if (delta < MOH_MS_INTERVAL) {  /* too early */
710                                 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));     /* next deadline */
711                                 usleep(1000 * (MOH_MS_INTERVAL - delta));
712                                 pthread_testcancel();
713                         } else {
714                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
715                                 deadline = tv_tmp;
716                         }
717                         res = 8 * MOH_MS_INTERVAL;      /* 8 samples per millisecond */
718                 }
719                 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
720                         continue;
721                 /* Read mp3 audio */
722                 len = ast_codec_get_len(&class->format, res);
723
724                 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
725                         if (!res2) {
726                                 close(class->srcfd);
727                                 class->srcfd = -1;
728                                 pthread_testcancel();
729                                 if (class->pid > 1) {
730                                         do {
731                                                 if (killpg(class->pid, SIGHUP) < 0) {
732                                                         if (errno == ESRCH) {
733                                                                 break;
734                                                         }
735                                                         ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
736                                                 }
737                                                 usleep(100000);
738                                                 if (killpg(class->pid, SIGTERM) < 0) {
739                                                         if (errno == ESRCH) {
740                                                                 break;
741                                                         }
742                                                         ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
743                                                 }
744                                                 usleep(100000);
745                                                 if (killpg(class->pid, SIGKILL) < 0) {
746                                                         if (errno == ESRCH) {
747                                                                 break;
748                                                         }
749                                                         ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
750                                                 }
751                                         } while (0);
752                                         class->pid = 0;
753                                 }
754                         } else {
755                                 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
756                         }
757                         continue;
758                 }
759
760                 pthread_testcancel();
761
762                 ao2_lock(class);
763                 AST_LIST_TRAVERSE(&class->members, moh, list) {
764                         /* Write data */
765                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
766                                 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
767                         }
768                 }
769                 ao2_unlock(class);
770         }
771         return NULL;
772 }
773
774 static int play_moh_exec(struct ast_channel *chan, const char *data)
775 {
776         char *parse;
777         char *class;
778         int timeout = -1;
779         int res;
780         AST_DECLARE_APP_ARGS(args,
781                 AST_APP_ARG(class);
782                 AST_APP_ARG(duration);
783         );
784
785         parse = ast_strdupa(data);
786
787         AST_STANDARD_APP_ARGS(args, parse);
788
789         if (!ast_strlen_zero(args.duration)) {
790                 if (sscanf(args.duration, "%30d", &timeout) == 1) {
791                         timeout *= 1000;
792                 } else {
793                         ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
794                 }
795         }
796
797         class = S_OR(args.class, NULL);
798         if (ast_moh_start(chan, class, NULL)) {
799                 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
800                 return 0;
801         }
802
803         if (timeout > 0)
804                 res = ast_safe_sleep(chan, timeout);
805         else {
806                 while (!(res = ast_safe_sleep(chan, 10000)));
807         }
808
809         ast_moh_stop(chan);
810
811         return res;
812 }
813
814 static int wait_moh_exec(struct ast_channel *chan, const char *data)
815 {
816         static int deprecation_warning = 0;
817         int res;
818
819         if (!deprecation_warning) {
820                 deprecation_warning = 1;
821                 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
822         }
823
824         if (!data || !atoi(data)) {
825                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
826                 return -1;
827         }
828         if (ast_moh_start(chan, NULL, NULL)) {
829                 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), ast_channel_name(chan));
830                 return 0;
831         }
832         res = ast_safe_sleep(chan, atoi(data) * 1000);
833         ast_moh_stop(chan);
834         return res;
835 }
836
837 static int set_moh_exec(struct ast_channel *chan, const char *data)
838 {
839         static int deprecation_warning = 0;
840
841         if (!deprecation_warning) {
842                 deprecation_warning = 1;
843                 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
844         }
845
846         if (ast_strlen_zero(data)) {
847                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
848                 return -1;
849         }
850         ast_channel_musicclass_set(chan, data);
851         return 0;
852 }
853
854 static int start_moh_exec(struct ast_channel *chan, const char *data)
855 {
856         char *parse;
857         char *class;
858         AST_DECLARE_APP_ARGS(args,
859                 AST_APP_ARG(class);
860         );
861
862         parse = ast_strdupa(data);
863
864         AST_STANDARD_APP_ARGS(args, parse);
865
866         class = S_OR(args.class, NULL);
867         if (ast_moh_start(chan, class, NULL)) 
868                 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
869
870         return 0;
871 }
872
873 static int stop_moh_exec(struct ast_channel *chan, const char *data)
874 {
875         ast_moh_stop(chan);
876
877         return 0;
878 }
879
880 #define get_mohbyname(a,b,c)    _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
881
882 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
883 {
884         struct mohclass *moh = NULL;
885         struct mohclass tmp_class = {
886                 .flags = 0,
887         };
888
889         ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
890
891 #ifdef REF_DEBUG
892         moh = __ao2_find_debug(mohclasses, &tmp_class, flags,
893                 "get_mohbyname", file, lineno, funcname);
894 #else
895         moh = __ao2_find(mohclasses, &tmp_class, flags);
896 #endif
897
898         if (!moh && warn) {
899                 ast_debug(1, "Music on Hold class '%s' not found in memory\n", name);
900         }
901
902         return moh;
903 }
904
905 static struct mohdata *mohalloc(struct mohclass *cl)
906 {
907         struct mohdata *moh;
908         long flags;
909
910         if (!(moh = ast_calloc(1, sizeof(*moh))))
911                 return NULL;
912
913         if (pipe(moh->pipe)) {
914                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
915                 ast_free(moh);
916                 return NULL;
917         }
918
919         /* Make entirely non-blocking */
920         flags = fcntl(moh->pipe[0], F_GETFL);
921         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
922         flags = fcntl(moh->pipe[1], F_GETFL);
923         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
924
925         moh->f.frametype = AST_FRAME_VOICE;
926         ast_format_copy(&moh->f.subclass.format, &cl->format);
927         moh->f.offset = AST_FRIENDLY_OFFSET;
928
929         moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
930
931         ao2_lock(cl);
932         AST_LIST_INSERT_HEAD(&cl->members, moh, list);
933         ao2_unlock(cl);
934         
935         return moh;
936 }
937
938 static void moh_release(struct ast_channel *chan, void *data)
939 {
940         struct mohdata *moh = data;
941         struct mohclass *class = moh->parent;
942         struct ast_format oldwfmt;
943
944         ao2_lock(class);
945         AST_LIST_REMOVE(&moh->parent->members, moh, list);      
946         ao2_unlock(class);
947         
948         close(moh->pipe[0]);
949         close(moh->pipe[1]);
950
951         ast_format_copy(&oldwfmt, &moh->origwfmt);
952
953         moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
954
955         ast_free(moh);
956
957         if (chan) {
958                 struct moh_files_state *state;
959
960                 state = ast_channel_music_state(chan);
961                 if (state && state->class) {
962                         state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
963                 }
964                 if (oldwfmt.id && ast_set_write_format(chan, &oldwfmt)) {
965                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
966                                         ast_channel_name(chan), ast_getformatname(&oldwfmt));
967                 }
968
969                 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
970         }
971 }
972
973 static void *moh_alloc(struct ast_channel *chan, void *params)
974 {
975         struct mohdata *res;
976         struct mohclass *class = params;
977         struct moh_files_state *state;
978
979         /* Initiating music_state for current channel. Channel should know name of moh class */
980         if (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
981                 ast_channel_music_state_set(chan, state);
982                 ast_module_ref(ast_module_info->self);
983         } else {
984                 state = ast_channel_music_state(chan);
985                 if (!state) {
986                         return NULL;
987                 }
988                 if (state->class) {
989                         mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
990                         ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
991                 }
992                 memset(state, 0, sizeof(*state));
993         }
994
995         if ((res = mohalloc(class))) {
996                 ast_format_copy(&res->origwfmt, ast_channel_writeformat(chan));
997                 if (ast_set_write_format(chan, &class->format)) {
998                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan), ast_codec2str(&class->format));
999                         moh_release(NULL, res);
1000                         res = NULL;
1001                 } else {
1002                         state->class = mohclass_ref(class, "Placing reference into state container");
1003                 }
1004                 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, ast_channel_name(chan));
1005         }
1006         return res;
1007 }
1008
1009 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
1010 {
1011         struct mohdata *moh = data;
1012         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1013         int res;
1014
1015         len = ast_codec_get_len(&moh->parent->format, samples);
1016
1017         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1018                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1019                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1020         }
1021         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1022         if (res <= 0)
1023                 return 0;
1024
1025         moh->f.datalen = res;
1026         moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1027         moh->f.samples = ast_codec_get_samples(&moh->f);
1028
1029         if (ast_write(chan, &moh->f) < 0) {
1030                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1031                 return -1;
1032         }
1033
1034         return 0;
1035 }
1036
1037 static struct ast_generator mohgen = {
1038         .alloc    = moh_alloc,
1039         .release  = moh_release,
1040         .generate = moh_generate,
1041         .digit    = moh_handle_digit,
1042 };
1043
1044 static int moh_add_file(struct mohclass *class, const char *filepath)
1045 {
1046         if (!class->allowed_files) {
1047                 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
1048                         return -1;
1049                 class->allowed_files = INITIAL_NUM_FILES;
1050         } else if (class->total_files == class->allowed_files) {
1051                 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
1052                         class->allowed_files = 0;
1053                         class->total_files = 0;
1054                         return -1;
1055                 }
1056                 class->allowed_files *= 2;
1057         }
1058
1059         if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
1060                 return -1;
1061
1062         class->total_files++;
1063
1064         return 0;
1065 }
1066
1067 static int moh_sort_compare(const void *i1, const void *i2)
1068 {
1069         char *s1, *s2;
1070
1071         s1 = ((char **)i1)[0];
1072         s2 = ((char **)i2)[0];
1073
1074         return strcasecmp(s1, s2);
1075 }
1076
1077 static int moh_scan_files(struct mohclass *class) {
1078
1079         DIR *files_DIR;
1080         struct dirent *files_dirent;
1081         char dir_path[PATH_MAX];
1082         char path[PATH_MAX];
1083         char filepath[PATH_MAX];
1084         char *ext;
1085         struct stat statbuf;
1086         int i;
1087
1088         if (class->dir[0] != '/') {
1089                 ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
1090                 strncat(dir_path, "/", sizeof(dir_path) - 1);
1091                 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
1092         } else {
1093                 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1094         }
1095         ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1096         files_DIR = opendir(dir_path);
1097         if (!files_DIR) {
1098                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
1099                 return -1;
1100         }
1101
1102         for (i = 0; i < class->total_files; i++) {
1103                 ast_free(class->filearray[i]);
1104         }
1105         class->total_files = 0;
1106
1107         if (!getcwd(path, sizeof(path))) {
1108                 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
1109                 closedir(files_DIR);
1110                 return -1;
1111         }
1112         if (chdir(dir_path) < 0) {
1113                 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1114                 closedir(files_DIR);
1115                 return -1;
1116         }
1117         while ((files_dirent = readdir(files_DIR))) {
1118                 /* The file name must be at least long enough to have the file type extension */
1119                 if ((strlen(files_dirent->d_name) < 4))
1120                         continue;
1121
1122                 /* Skip files that starts with a dot */
1123                 if (files_dirent->d_name[0] == '.')
1124                         continue;
1125
1126                 /* Skip files without extensions... they are not audio */
1127                 if (!strchr(files_dirent->d_name, '.'))
1128                         continue;
1129
1130                 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
1131
1132                 if (stat(filepath, &statbuf))
1133                         continue;
1134
1135                 if (!S_ISREG(statbuf.st_mode))
1136                         continue;
1137
1138                 if ((ext = strrchr(filepath, '.')))
1139                         *ext = '\0';
1140
1141                 /* if the file is present in multiple formats, ensure we only put it into the list once */
1142                 for (i = 0; i < class->total_files; i++)
1143                         if (!strcmp(filepath, class->filearray[i]))
1144                                 break;
1145
1146                 if (i == class->total_files) {
1147                         if (moh_add_file(class, filepath))
1148                                 break;
1149                 }
1150         }
1151
1152         closedir(files_DIR);
1153         if (chdir(path) < 0) {
1154                 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1155                 return -1;
1156         }
1157         if (ast_test_flag(class, MOH_SORTALPHA))
1158                 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
1159         return class->total_files;
1160 }
1161
1162 static int init_files_class(struct mohclass *class)
1163 {
1164         int res;
1165
1166         res = moh_scan_files(class);
1167
1168         if (res < 0) {
1169                 return -1;
1170         }
1171
1172         if (!res) {
1173                 ast_verb(3, "Files not found in %s for moh class:%s\n",
1174                         class->dir, class->name);
1175                 return -1;
1176         }
1177
1178 #if 0
1179         /* XXX This isn't correct.  Args is an application for custom mode. XXX */
1180         if (strchr(class->args, 'r')) {
1181                 ast_set_flag(class, MOH_RANDOMIZE);
1182         }
1183 #endif
1184
1185         return 0;
1186 }
1187
1188 static void moh_rescan_files(void) {
1189         struct ao2_iterator i;
1190         struct mohclass *c;
1191
1192         i = ao2_iterator_init(mohclasses, 0);
1193
1194         while ((c = ao2_iterator_next(&i))) {
1195                 if (!strcasecmp(c->mode, "files")) {
1196                         moh_scan_files(c);
1197                 }
1198                 ao2_ref(c, -1);
1199         }
1200
1201         ao2_iterator_destroy(&i);
1202 }
1203
1204 static int moh_diff(struct mohclass *old, struct mohclass *new)
1205 {
1206         if (!old || !new) {
1207                 return -1;
1208         }
1209
1210         if (strcmp(old->dir, new->dir)) {
1211                 return -1;
1212         } else if (strcmp(old->mode, new->mode)) {
1213                 return -1;
1214         } else if (strcmp(old->args, new->args)) {
1215                 return -1;
1216         } else if (old->flags != new->flags) {
1217                 return -1;
1218         }
1219
1220         return 0;
1221 }
1222
1223 static int init_app_class(struct mohclass *class)
1224 {
1225         if (!strcasecmp(class->mode, "custom")) {
1226                 ast_set_flag(class, MOH_CUSTOM);
1227         } else if (!strcasecmp(class->mode, "mp3nb")) {
1228                 ast_set_flag(class, MOH_SINGLE);
1229         } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1230                 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1231         } else if (!strcasecmp(class->mode, "quietmp3")) {
1232                 ast_set_flag(class, MOH_QUIET);
1233         }
1234
1235         class->srcfd = -1;
1236
1237         if (!(class->timer = ast_timer_open())) {
1238                 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1239                 return -1;
1240         }
1241         if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1242                 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1243                 ast_timer_close(class->timer);
1244                 class->timer = NULL;
1245         }
1246
1247         if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1248                 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1249                 if (class->timer) {
1250                         ast_timer_close(class->timer);
1251                         class->timer = NULL;
1252                 }
1253                 return -1;
1254         }
1255
1256         return 0;
1257 }
1258
1259 /*!
1260  * \note This function owns the reference it gets to moh if unref is true
1261  */
1262 #define moh_register(a,b,c)     _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1263 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1264 {
1265         struct mohclass *mohclass = NULL;
1266
1267         mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1268
1269         if (mohclass && !moh_diff(mohclass, moh)) {
1270                 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1271                 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1272                 if (unref) {
1273                         moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1274                 }
1275                 return -1;
1276         } else if (mohclass) {
1277                 /* Found a class, but it's different from the one being registered */
1278                 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1279         }
1280
1281         time(&moh->start);
1282         moh->start -= respawn_time;
1283
1284         if (!strcasecmp(moh->mode, "files")) {
1285                 if (init_files_class(moh)) {
1286                         if (unref) {
1287                                 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1288                         }
1289                         return -1;
1290                 }
1291         } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
1292                         !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
1293                         !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1294                 if (init_app_class(moh)) {
1295                         if (unref) {
1296                                 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1297                         }
1298                         return -1;
1299                 }
1300         } else {
1301                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1302                 if (unref) {
1303                         moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1304                 }
1305                 return -1;
1306         }
1307
1308         ao2_t_link(mohclasses, moh, "Adding class to container");
1309
1310         if (unref) {
1311                 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1312         }
1313
1314         return 0;
1315 }
1316
1317 static void local_ast_moh_cleanup(struct ast_channel *chan)
1318 {
1319         struct moh_files_state *state = ast_channel_music_state(chan);
1320
1321         if (state) {
1322                 if (state->class) {
1323                         /* This should never happen.  We likely just leaked some resource. */
1324                         state->class =
1325                                 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1326                         ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1327                 }
1328                 ast_free(ast_channel_music_state(chan));
1329                 ast_channel_music_state_set(chan, NULL);
1330                 /* Only held a module reference if we had a music state */
1331                 ast_module_unref(ast_module_info->self);
1332         }
1333 }
1334
1335 static void moh_class_destructor(void *obj);
1336
1337 #define moh_class_malloc()      _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1338
1339 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1340 {
1341         struct mohclass *class;
1342
1343         if ((class =
1344 #ifdef REF_DEBUG
1345                         __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
1346                                 AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 1)
1347 #elif defined(__AST_DEBUG_MALLOC)
1348                         __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
1349                                 AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 0)
1350 #else
1351                         ao2_alloc(sizeof(*class), moh_class_destructor)
1352 #endif
1353                 )) {
1354                 ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
1355                 class->srcfd = -1;
1356         }
1357
1358         return class;
1359 }
1360
1361 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1362 {
1363         struct mohclass *mohclass = NULL;
1364         struct moh_files_state *state = ast_channel_music_state(chan);
1365         struct ast_variable *var = NULL;
1366         int res;
1367         int realtime_possible = ast_check_realtime("musiconhold");
1368
1369         /* The following is the order of preference for which class to use:
1370          * 1) The channels explicitly set musicclass, which should *only* be
1371          *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1372          * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1373          *    result of receiving a HOLD control frame, this should be the
1374          *    payload that came with the frame.
1375          * 3) The interpclass argument. This would be from the mohinterpret
1376          *    option from channel drivers. This is the same as the old musicclass
1377          *    option.
1378          * 4) The default class.
1379          */
1380         if (!ast_strlen_zero(ast_channel_musicclass(chan))) {
1381                 mohclass = get_mohbyname(ast_channel_musicclass(chan), 1, 0);
1382                 if (!mohclass && realtime_possible) {
1383                         var = ast_load_realtime("musiconhold", "name", ast_channel_musicclass(chan), SENTINEL);
1384                 }
1385         }
1386         if (!mohclass && !var && !ast_strlen_zero(mclass)) {
1387                 mohclass = get_mohbyname(mclass, 1, 0);
1388                 if (!mohclass && realtime_possible) {
1389                         var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
1390                 }
1391         }
1392         if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
1393                 mohclass = get_mohbyname(interpclass, 1, 0);
1394                 if (!mohclass && realtime_possible) {
1395                         var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
1396                 }
1397         }
1398
1399         if (!mohclass && !var) {
1400                 mohclass = get_mohbyname("default", 1, 0);
1401                 if (!mohclass && realtime_possible) {
1402                         var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
1403                 }
1404         }
1405
1406         /* If no moh class found in memory, then check RT. Note that the logic used
1407          * above guarantees that if var is non-NULL, then mohclass must be NULL.
1408          */
1409         if (var) {
1410                 struct ast_variable *tmp = NULL;
1411
1412                 if ((mohclass = moh_class_malloc())) {
1413                         mohclass->realtime = 1;
1414                         for (tmp = var; tmp; tmp = tmp->next) {
1415                                 if (!strcasecmp(tmp->name, "name"))
1416                                         ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1417                                 else if (!strcasecmp(tmp->name, "mode"))
1418                                         ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
1419                                 else if (!strcasecmp(tmp->name, "directory"))
1420                                         ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1421                                 else if (!strcasecmp(tmp->name, "application"))
1422                                         ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1423                                 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1424                                         mohclass->digit = *tmp->value;
1425                                 else if (!strcasecmp(tmp->name, "random"))
1426                                         ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1427                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1428                                         ast_set_flag(mohclass, MOH_RANDOMIZE);
1429                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
1430                                         ast_set_flag(mohclass, MOH_SORTALPHA);
1431                                 else if (!strcasecmp(tmp->name, "format")) {
1432                                         ast_getformatbyname(tmp->value, &mohclass->format);
1433                                         if (!mohclass->format.id) {
1434                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1435                                                 ast_format_set(&mohclass->format, AST_FORMAT_SLINEAR, 0);
1436                                         }
1437                                 }
1438                         }
1439                         ast_variables_destroy(var);
1440                         if (ast_strlen_zero(mohclass->dir)) {
1441                                 if (!strcasecmp(mohclass->mode, "custom")) {
1442                                         strcpy(mohclass->dir, "nodir");
1443                                 } else {
1444                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1445                                         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1446                                         return -1;
1447                                 }
1448                         }
1449                         if (ast_strlen_zero(mohclass->mode)) {
1450                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1451                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1452                                 return -1;
1453                         }
1454                         if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1455                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1456                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1457                                 return -1;
1458                         }
1459
1460                         if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1461                                 /* CACHERTCLASSES enabled, let's add this class to default tree */
1462                                 if (state && state->class) {
1463                                         /* Class already exist for this channel */
1464                                         ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1465                                         if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1466                                                 /* we found RT class with the same name, seems like we should continue playing existing one */
1467                                                 /* XXX This code is impossible to reach */
1468                                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
1469                                                 mohclass = state->class;
1470                                         }
1471                                 }
1472                                 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1473                                  * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1474                                  * be that the destructor would be called when the generator on the channel is deactivated. The container then
1475                                  * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1476                                  * invalid memory.
1477                                  */
1478                                 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1479                                         mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1480                                         return -1;
1481                                 }
1482                         } else {
1483                                 /* We don't register RT moh class, so let's init it manualy */
1484
1485                                 time(&mohclass->start);
1486                                 mohclass->start -= respawn_time;
1487
1488                                 if (!strcasecmp(mohclass->mode, "files")) {
1489                                         if (!moh_scan_files(mohclass)) {
1490                                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1491                                                 return -1;
1492                                         }
1493                                         if (strchr(mohclass->args, 'r'))
1494                                                 ast_set_flag(mohclass, MOH_RANDOMIZE);
1495                                 } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {
1496
1497                                         if (!strcasecmp(mohclass->mode, "custom"))
1498                                                 ast_set_flag(mohclass, MOH_CUSTOM);
1499                                         else if (!strcasecmp(mohclass->mode, "mp3nb"))
1500                                                 ast_set_flag(mohclass, MOH_SINGLE);
1501                                         else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1502                                                 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1503                                         else if (!strcasecmp(mohclass->mode, "quietmp3"))
1504                                                 ast_set_flag(mohclass, MOH_QUIET);
1505
1506                                         mohclass->srcfd = -1;
1507                                         if (!(mohclass->timer = ast_timer_open())) {
1508                                                 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1509                                         }
1510                                         if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
1511                                                 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1512                                                 ast_timer_close(mohclass->timer);
1513                                                 mohclass->timer = NULL;
1514                                         }
1515
1516                                         /* Let's check if this channel already had a moh class before */
1517                                         if (state && state->class) {
1518                                                 /* Class already exist for this channel */
1519                                                 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1520                                                 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1521                                                         /* we found RT class with the same name, seems like we should continue playing existing one */
1522                                                         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1523                                                         mohclass = state->class;
1524                                                 }
1525                                         } else {
1526                                                 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1527                                                         ast_log(LOG_WARNING, "Unable to create moh...\n");
1528                                                         if (mohclass->timer) {
1529                                                                 ast_timer_close(mohclass->timer);
1530                                                                 mohclass->timer = NULL;
1531                                                         }
1532                                                         mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1533                                                         return -1;
1534                                                 }
1535                                         }
1536                                 } else {
1537                                         ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1538                                         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1539                                         return -1;
1540                                 }
1541                         }
1542                 } else {
1543                         ast_variables_destroy(var);
1544                         var = NULL;
1545                 }
1546         }
1547
1548         if (!mohclass) {
1549                 return -1;
1550         }
1551
1552         /* If we are using a cached realtime class with files, re-scan the files */
1553         if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1554                 if (!moh_scan_files(mohclass)) {
1555                         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1556                         return -1;
1557                 }
1558         }
1559
1560         ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1561                 "State: Start\r\n"
1562                 "Channel: %s\r\n"
1563                 "UniqueID: %s\r\n"
1564                 "Class: %s\r\n",
1565                 ast_channel_name(chan), ast_channel_uniqueid(chan),
1566                 mohclass->name);
1567
1568         ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH);
1569
1570         if (mohclass->total_files) {
1571                 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1572         } else {
1573                 res = ast_activate_generator(chan, &mohgen, mohclass);
1574         }
1575
1576         mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1577
1578         return res;
1579 }
1580
1581 static void local_ast_moh_stop(struct ast_channel *chan)
1582 {
1583         ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH);
1584         ast_deactivate_generator(chan);
1585
1586         ast_channel_lock(chan);
1587         if (ast_channel_music_state(chan)) {
1588                 if (ast_channel_stream(chan)) {
1589                         ast_closestream(ast_channel_stream(chan));
1590                         ast_channel_stream_set(chan, NULL);
1591                 }
1592         }
1593
1594         ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1595                 "State: Stop\r\n"
1596                 "Channel: %s\r\n"
1597                 "UniqueID: %s\r\n",
1598                 ast_channel_name(chan), ast_channel_uniqueid(chan));
1599         ast_channel_unlock(chan);
1600 }
1601
1602 static void moh_class_destructor(void *obj)
1603 {
1604         struct mohclass *class = obj;
1605         struct mohdata *member;
1606         pthread_t tid = 0;
1607
1608         ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1609
1610         ao2_lock(class);
1611         while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1612                 free(member);
1613         }
1614         ao2_unlock(class);
1615
1616         /* Kill the thread first, so it cannot restart the child process while the
1617          * class is being destroyed */
1618         if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1619                 tid = class->thread;
1620                 class->thread = AST_PTHREADT_NULL;
1621                 pthread_cancel(tid);
1622                 /* We'll collect the exit status later, after we ensure all the readers
1623                  * are dead. */
1624         }
1625
1626         if (class->pid > 1) {
1627                 char buff[8192];
1628                 int bytes, tbytes = 0, stime = 0, pid = 0;
1629
1630                 ast_debug(1, "killing %d!\n", class->pid);
1631
1632                 stime = time(NULL) + 2;
1633                 pid = class->pid;
1634                 class->pid = 0;
1635
1636                 /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1637                  * to give the process a reason and time enough to kill off its
1638                  * children. */
1639                 do {
1640                         if (killpg(pid, SIGHUP) < 0) {
1641                                 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
1642                         }
1643                         usleep(100000);
1644                         if (killpg(pid, SIGTERM) < 0) {
1645                                 if (errno == ESRCH) {
1646                                         break;
1647                                 }
1648                                 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
1649                         }
1650                         usleep(100000);
1651                         if (killpg(pid, SIGKILL) < 0) {
1652                                 if (errno == ESRCH) {
1653                                         break;
1654                                 }
1655                                 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
1656                         }
1657                 } while (0);
1658
1659                 while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
1660                                 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1661                         tbytes = tbytes + bytes;
1662                 }
1663
1664                 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1665
1666                 close(class->srcfd);
1667                 class->srcfd = -1;
1668         }
1669
1670         if (class->filearray) {
1671                 int i;
1672                 for (i = 0; i < class->total_files; i++) {
1673                         free(class->filearray[i]);
1674                 }
1675                 free(class->filearray);
1676                 class->filearray = NULL;
1677         }
1678
1679         if (class->timer) {
1680                 ast_timer_close(class->timer);
1681                 class->timer = NULL;
1682         }
1683
1684         /* Finally, collect the exit status of the monitor thread */
1685         if (tid > 0) {
1686                 pthread_join(tid, NULL);
1687         }
1688
1689 }
1690
1691 static int moh_class_mark(void *obj, void *arg, int flags)
1692 {
1693         struct mohclass *class = obj;
1694
1695         class->delete = 1;
1696
1697         return 0;
1698 }
1699
1700 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1701 {
1702         struct mohclass *class = obj;
1703
1704         return class->delete ? CMP_MATCH : 0;
1705 }
1706
1707 static int load_moh_classes(int reload)
1708 {
1709         struct ast_config *cfg;
1710         struct ast_variable *var;
1711         struct mohclass *class; 
1712         char *cat;
1713         int numclasses = 0;
1714         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1715
1716         cfg = ast_config_load("musiconhold.conf", config_flags);
1717
1718         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1719                 if (ast_check_realtime("musiconhold") && reload) {
1720                         ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1721                         ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
1722                 }
1723                 return 0;
1724         }
1725         if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1726                 moh_rescan_files();
1727                 return 0;
1728         }
1729
1730         if (reload) {
1731                 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1732         }
1733
1734         ast_clear_flag(global_flags, AST_FLAGS_ALL);
1735
1736         cat = ast_category_browse(cfg, NULL);
1737         for (; cat; cat = ast_category_browse(cfg, cat)) {
1738                 /* Setup common options from [general] section */
1739                 if (!strcasecmp(cat, "general")) {
1740                         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1741                                 if (!strcasecmp(var->name, "cachertclasses")) {
1742                                         ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1743                                 } else {
1744                                         ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1745                                 }
1746                         }
1747                 }
1748                 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1749                 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
1750                                 !strcasecmp(cat, "general")) {
1751                         continue;
1752                 }
1753
1754                 if (!(class = moh_class_malloc())) {
1755                         break;
1756                 }
1757
1758                 ast_copy_string(class->name, cat, sizeof(class->name));
1759                 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1760                         if (!strcasecmp(var->name, "mode")) {
1761                                 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1762                         } else if (!strcasecmp(var->name, "directory")) {
1763                                 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1764                         } else if (!strcasecmp(var->name, "application")) {
1765                                 ast_copy_string(class->args, var->value, sizeof(class->args));
1766                         } else if (!strcasecmp(var->name, "announcement")) {
1767                                 ast_copy_string(class->announcement, var->value, sizeof(class->announcement));
1768                                 ast_set_flag(class, MOH_ANNOUNCEMENT);
1769                         } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1770                                 class->digit = *var->value;
1771                         } else if (!strcasecmp(var->name, "random")) {
1772                                 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1773                         } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) {
1774                                 ast_set_flag(class, MOH_RANDOMIZE);
1775                         } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) {
1776                                 ast_set_flag(class, MOH_SORTALPHA);
1777                         } else if (!strcasecmp(var->name, "format")) {
1778                                 ast_getformatbyname(var->value, &class->format);
1779                                 if (!class->format.id) {
1780                                         ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1781                                         ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
1782                                 }
1783                         }
1784                 }
1785
1786                 if (ast_strlen_zero(class->dir)) {
1787                         if (!strcasecmp(class->mode, "custom")) {
1788                                 strcpy(class->dir, "nodir");
1789                         } else {
1790                                 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1791                                 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
1792                                 continue;
1793                         }
1794                 }
1795                 if (ast_strlen_zero(class->mode)) {
1796                         ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1797                         class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
1798                         continue;
1799                 }
1800                 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1801                         ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1802                         class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
1803                         continue;
1804                 }
1805
1806                 /* Don't leak a class when it's already registered */
1807                 if (!moh_register(class, reload, HANDLE_REF)) {
1808                         numclasses++;
1809                 }
1810         }
1811
1812         ast_config_destroy(cfg);
1813
1814         ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
1815                         moh_classes_delete_marked, NULL, "Purge marked classes");
1816
1817         return numclasses;
1818 }
1819
1820 static void ast_moh_destroy(void)
1821 {
1822         ast_verb(2, "Destroying musiconhold processes\n");
1823         ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
1824 }
1825
1826 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1827 {
1828         switch (cmd) {
1829         case CLI_INIT:
1830                 e->command = "moh reload";
1831                 e->usage =
1832                         "Usage: moh reload\n"
1833                         "       Reloads the MusicOnHold module.\n"
1834                         "       Alias for 'module reload res_musiconhold.so'\n";
1835                 return NULL;
1836         case CLI_GENERATE:
1837                 return NULL;
1838         }
1839
1840         if (a->argc != e->args)
1841                 return CLI_SHOWUSAGE;
1842
1843         reload();
1844
1845         return CLI_SUCCESS;
1846 }
1847
1848 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1849 {
1850         struct mohclass *class;
1851         struct ao2_iterator i;
1852
1853         switch (cmd) {
1854         case CLI_INIT:
1855                 e->command = "moh show files";
1856                 e->usage =
1857                         "Usage: moh show files\n"
1858                         "       Lists all loaded file-based MusicOnHold classes and their\n"
1859                         "       files.\n";
1860                 return NULL;
1861         case CLI_GENERATE:
1862                 return NULL;
1863         }
1864
1865         if (a->argc != e->args)
1866                 return CLI_SHOWUSAGE;
1867
1868         i = ao2_iterator_init(mohclasses, 0);
1869         for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
1870                 int x;
1871
1872                 if (!class->total_files) {
1873                         continue;
1874                 }
1875
1876                 ast_cli(a->fd, "Class: %s\n", class->name);
1877                 for (x = 0; x < class->total_files; x++) {
1878                         ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
1879                 }
1880         }
1881         ao2_iterator_destroy(&i);
1882
1883         return CLI_SUCCESS;
1884 }
1885
1886 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1887 {
1888         struct mohclass *class;
1889         struct ao2_iterator i;
1890
1891         switch (cmd) {
1892         case CLI_INIT:
1893                 e->command = "moh show classes";
1894                 e->usage =
1895                         "Usage: moh show classes\n"
1896                         "       Lists all MusicOnHold classes.\n";
1897                 return NULL;
1898         case CLI_GENERATE:
1899                 return NULL;
1900         }
1901
1902         if (a->argc != e->args)
1903                 return CLI_SHOWUSAGE;
1904
1905         i = ao2_iterator_init(mohclasses, 0);
1906         for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
1907                 ast_cli(a->fd, "Class: %s\n", class->name);
1908                 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1909                 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1910                 if (ast_test_flag(class, MOH_CUSTOM)) {
1911                         ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1912                 }
1913                 if (strcasecmp(class->mode, "files")) {
1914                         ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(&class->format));
1915                 }
1916         }
1917         ao2_iterator_destroy(&i);
1918
1919         return CLI_SUCCESS;
1920 }
1921
1922 static struct ast_cli_entry cli_moh[] = {
1923         AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
1924         AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1925         AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
1926 };
1927
1928 static int moh_class_hash(const void *obj, const int flags)
1929 {
1930         const struct mohclass *class = obj;
1931
1932         return ast_str_case_hash(class->name);
1933 }
1934
1935 static int moh_class_cmp(void *obj, void *arg, int flags)
1936 {
1937         struct mohclass *class = obj, *class2 = arg;
1938
1939         return strcasecmp(class->name, class2->name) ? 0 :
1940                 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
1941                 CMP_MATCH | CMP_STOP;
1942 }
1943
1944 static int load_module(void)
1945 {
1946         int res;
1947
1948         if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
1949                 return AST_MODULE_LOAD_DECLINE;
1950         }
1951
1952         if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
1953                 ast_log(LOG_WARNING, "No music on hold classes configured, "
1954                                 "disabling music on hold.\n");
1955         } else {
1956                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1957                                 local_ast_moh_cleanup);
1958         }
1959
1960         res = ast_register_application_xml(play_moh, play_moh_exec);
1961         ast_register_atexit(ast_moh_destroy);
1962         ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
1963         if (!res)
1964                 res = ast_register_application_xml(wait_moh, wait_moh_exec);
1965         if (!res)
1966                 res = ast_register_application_xml(set_moh, set_moh_exec);
1967         if (!res)
1968                 res = ast_register_application_xml(start_moh, start_moh_exec);
1969         if (!res)
1970                 res = ast_register_application_xml(stop_moh, stop_moh_exec);
1971
1972         return AST_MODULE_LOAD_SUCCESS;
1973 }
1974
1975 static int reload(void)
1976 {
1977         if (load_moh_classes(1)) {
1978                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1979                                 local_ast_moh_cleanup);
1980         }
1981
1982         return AST_MODULE_LOAD_SUCCESS;
1983 }
1984
1985 static int moh_class_inuse(void *obj, void *arg, int flags)
1986 {
1987         struct mohclass *class = obj;
1988
1989         return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
1990 }
1991
1992 static int unload_module(void)
1993 {
1994         int res = 0;
1995         struct mohclass *class = NULL;
1996
1997         /* XXX This check shouldn't be required if module ref counting was being used
1998          * properly ... */
1999         if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2000                 class = mohclass_unref(class, "unref of class from module unload callback");
2001                 res = -1;
2002         }
2003
2004         if (res < 0) {
2005                 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2006                 return res;
2007         }
2008
2009         ast_uninstall_music_functions();
2010
2011         ast_moh_destroy();
2012         res = ast_unregister_application(play_moh);
2013         res |= ast_unregister_application(wait_moh);
2014         res |= ast_unregister_application(set_moh);
2015         res |= ast_unregister_application(start_moh);
2016         res |= ast_unregister_application(stop_moh);
2017         ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
2018         ast_unregister_atexit(ast_moh_destroy);
2019
2020         return res;
2021 }
2022
2023 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
2024         .load = load_module,
2025         .unload = unload_module,
2026         .reload = reload,
2027         .load_pri = AST_MODPRI_CHANNEL_DEPEND,
2028 );