don't access freed memory if the frame was malloc'd
[asterisk/asterisk.git] / res / res_musiconhold.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Routines implementing music on hold
5  * 
6  * Copyright (C) 1999 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdlib.h>
15 #include <errno.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <signal.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <sys/time.h>
22 #include <sys/signal.h>
23 #include <netinet/in.h>
24 #include <sys/stat.h>
25 #include <dirent.h>
26 #ifdef ZAPATA_MOH
27 #ifdef __linux__
28 #include <linux/zaptel.h>
29 #else
30 #include <zaptel.h>
31 #endif /* __linux__ */
32 #endif
33 #include <unistd.h>
34 #include <sys/ioctl.h>
35
36 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include "asterisk/lock.h"
41 #include "asterisk/file.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/options.h"
46 #include "asterisk/module.h"
47 #include "asterisk/translate.h"
48 #include "asterisk/say.h"
49 #include "asterisk/musiconhold.h"
50 #include "asterisk/config.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/cli.h"
53
54 #define MAX_MOHFILES 512
55 #define MAX_MOHFILE_LEN 128
56
57 static char *app0 = "MusicOnHold";
58 static char *app1 = "WaitMusicOnHold";
59 static char *app2 = "SetMusicOnHold";
60 static char *app3 = "StartMusicOnHold";
61 static char *app4 = "StopMusicOnHold";
62
63 static char *synopsis0 = "Play Music On Hold indefinitely";
64 static char *synopsis1 = "Wait, playing Music On Hold";
65 static char *synopsis2 = "Set default Music On Hold class";
66 static char *synopsis3 = "Play Music On Hold";
67 static char *synopsis4 = "Stop Playing Music On Hold";
68
69 static char *descrip0 = "MusicOnHold(class): "
70 "Plays hold music specified by class.  If omitted, the default\n"
71 "music source for the channel will be used. Set the default \n"
72 "class with the SetMusicOnHold() application.\n"
73 "Returns -1 on hangup.\n"
74 "Never returns otherwise.\n";
75
76 static char *descrip1 = "WaitMusicOnHold(delay): "
77 "Plays hold music specified number of seconds.  Returns 0 when\n"
78 "done, or -1 on hangup.  If no hold music is available, the delay will\n"
79 "still occur with no sound.\n";
80
81 static char *descrip2 = "SetMusicOnHold(class): "
82 "Sets the default class for music on hold for a given channel.  When\n"
83 "music on hold is activated, this class will be used to select which\n"
84 "music is played.\n";
85
86 static char *descrip3 = "StartMusicOnHold(class): "
87 "Starts playing music on hold, uses default music class for channel.\n"
88 "Starts playing music specified by class.  If omitted, the default\n"
89 "music source for the channel will be used.  Always returns 0.\n";
90
91 static char *descrip4 = "StopMusicOnHold: "
92 "Stops playing music on hold.\n";
93
94 static int respawn_time = 20;
95
96 struct moh_files_state {
97         struct mohclass *class;
98         int origwfmt;
99         int samples;
100         int sample_queue;
101         unsigned char pos;
102         unsigned char save_pos;
103 };
104
105 #define MOH_QUIET               (1 << 0)
106 #define MOH_SINGLE              (1 << 1)
107 #define MOH_CUSTOM              (1 << 2)
108 #define MOH_RANDOMIZE           (1 << 3)
109
110 struct mohclass {
111         char name[MAX_MUSICCLASS];
112         char dir[256];
113         char args[256];
114         char mode[80];
115         char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
116         unsigned int flags;
117         int total_files;
118         int format;
119         int pid;                /* PID of mpg123 */
120         time_t start;
121         pthread_t thread;
122         struct mohdata *members;
123         /* Source of audio */
124         int srcfd;
125         /* FD for timing source */
126         int pseudofd;
127         struct mohclass *next;
128 };
129
130 struct mohdata {
131         int pipe[2];
132         int origwfmt;
133         struct mohclass *parent;
134         struct mohdata *next;
135 };
136
137 static struct mohclass *mohclasses;
138
139 AST_MUTEX_DEFINE_STATIC(moh_lock);
140
141 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
142 #define MPG_123 "/usr/bin/mpg123"
143 #define MAX_MP3S 256
144
145
146 static void ast_moh_free_class(struct mohclass **class) 
147 {
148         struct mohdata *members, *mtmp;
149         
150         members = (*class)->members;
151         while(members) {
152                 mtmp = members;
153                 members = members->next;
154                 free(mtmp);
155         }
156         free(*class);
157         *class = NULL;
158 }
159
160
161 static void moh_files_release(struct ast_channel *chan, void *data)
162 {
163         struct moh_files_state *state = chan->music_state;
164
165         if (chan && state) {
166                 if (option_verbose > 2)
167                         ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
168
169                 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
170                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
171                 }
172                 state->save_pos = state->pos + 1;
173         }
174 }
175
176
177 static int ast_moh_files_next(struct ast_channel *chan) 
178 {
179         struct moh_files_state *state = chan->music_state;
180         int tries;
181
182         if (state->save_pos) {
183                 state->pos = state->save_pos - 1;
184                 state->save_pos = 0;
185         } else {
186                 /* Try 20 times to find something good */
187                 for (tries=0;tries < 20;tries++) {
188                         state->samples = 0;
189                         if (chan->stream) {
190                                 ast_closestream(chan->stream);
191                                 chan->stream = NULL;
192                                 state->pos++;
193                         }
194
195                         if (ast_test_flag(state->class, MOH_RANDOMIZE))
196                                 state->pos = rand();
197
198                         /* check to see if this file's format can be opened */
199                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) != -1)
200                                 break;
201
202                 }
203         }
204
205         state->pos = state->pos % state->class->total_files;
206         
207         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
208                 ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
209                 return -1;
210         }
211         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
212                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
213                 state->pos++;
214                 return -1;
215         }
216
217         if (option_debug)
218                 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
219
220         if (state->samples)
221                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
222
223         return 0;
224 }
225
226
227 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
228 {
229         struct ast_frame *f = NULL;
230         
231         if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
232                 if (!ast_moh_files_next(chan))
233                         f = ast_readframe(chan->stream);
234         }
235
236         return f;
237 }
238
239 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
240 {
241         struct moh_files_state *state = chan->music_state;
242         struct ast_frame *f = NULL;
243         int res = 0;
244
245         state->sample_queue += samples;
246
247         while (state->sample_queue > 0) {
248                 if ((f = moh_files_readframe(chan))) {
249                         state->samples += f->samples;
250                         res = ast_write(chan, f);
251                         state->sample_queue -= f->samples;
252                         ast_frfree(f);
253                         if (res < 0) {
254                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
255                                 return -1;
256                         }
257                 } else
258                         return -1;      
259         }
260         return res;
261 }
262
263
264 static void *moh_files_alloc(struct ast_channel *chan, void *params)
265 {
266         struct moh_files_state *state;
267         struct mohclass *class = params;
268         int allocated = 0;
269
270         if (!chan->music_state && (state = malloc(sizeof(struct moh_files_state)))) {
271                 chan->music_state = state;
272                 allocated = 1;
273         } else 
274                 state = chan->music_state;
275
276         if (state) {
277                 if (allocated || state->class != class) {
278                         /* initialize */
279                         memset(state, 0, sizeof(struct moh_files_state));
280                         state->class = class;
281                 }
282
283                 state->origwfmt = chan->writeformat;
284
285                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
286                         ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
287                         free(chan->music_state);
288                         chan->music_state = NULL;
289                 } else {
290                         if (option_verbose > 2)
291                                 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
292                 }
293         }
294         
295         return chan->music_state;
296 }
297
298 static struct ast_generator moh_file_stream = 
299 {
300         alloc: moh_files_alloc,
301         release: moh_files_release,
302         generate: moh_files_generator,
303 };
304
305 static int spawn_mp3(struct mohclass *class)
306 {
307         int fds[2];
308         int files = 0;
309         char fns[MAX_MP3S][80];
310         char *argv[MAX_MP3S + 50];
311         char xargs[256];
312         char *argptr;
313         int argc = 0;
314         DIR *dir = NULL;
315         struct dirent *de;
316
317         
318         if (!strcasecmp(class->dir, "nodir")) {
319                 files = 1;
320         } else {
321                 dir = opendir(class->dir);
322                 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
323                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
324                         return -1;
325                 }
326         }
327
328         if (!ast_test_flag(class, MOH_CUSTOM)) {
329                 argv[argc++] = "mpg123";
330                 argv[argc++] = "-q";
331                 argv[argc++] = "-s";
332                 argv[argc++] = "--mono";
333                 argv[argc++] = "-r";
334                 argv[argc++] = "8000";
335                 
336                 if (!ast_test_flag(class, MOH_SINGLE)) {
337                         argv[argc++] = "-b";
338                         argv[argc++] = "2048";
339                 }
340                 
341                 argv[argc++] = "-f";
342                 
343                 if (ast_test_flag(class, MOH_QUIET))
344                         argv[argc++] = "4096";
345                 else
346                         argv[argc++] = "8192";
347                 
348                 /* Look for extra arguments and add them to the list */
349                 strncpy(xargs, class->args, sizeof(xargs) - 1);
350                 argptr = xargs;
351                 while (argptr && !ast_strlen_zero(argptr)) {
352                         argv[argc++] = argptr;
353                         argptr = strchr(argptr, ',');
354                         if (argptr) {
355                                 *argptr = '\0';
356                                 argptr++;
357                         }
358                 }
359         } else  {
360                 /* Format arguments for argv vector */
361                 strncpy(xargs, class->args, sizeof(xargs) - 1);
362                 argptr = xargs;
363                 while (argptr && !ast_strlen_zero(argptr)) {
364                         argv[argc++] = argptr;
365                         argptr = strchr(argptr, ' ');
366                         if (argptr) {
367                                 *argptr = '\0';
368                                 argptr++;
369                         }
370                 }
371         }
372
373
374         if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
375                 strncpy(fns[files], class->dir, sizeof(fns[files]) - 1);
376                 argv[argc++] = fns[files];
377                 files++;
378         } else if (dir) {
379                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
380                         if ((strlen(de->d_name) > 3) && 
381                             ((ast_test_flag(class, MOH_CUSTOM) && 
382                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
383                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
384                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
385                                 strncpy(fns[files], de->d_name, sizeof(fns[files]) - 1);
386                                 argv[argc++] = fns[files];
387                                 files++;
388                         }
389                 }
390         }
391         argv[argc] = NULL;
392         if (dir) {
393                 closedir(dir);
394         }
395         if (pipe(fds)) {        
396                 ast_log(LOG_WARNING, "Pipe failed\n");
397                 return -1;
398         }
399 #if 0
400         printf("%d files total, %d args total\n", files, argc);
401         {
402                 int x;
403                 for (x=0;argv[x];x++)
404                         printf("arg%d: %s\n", x, argv[x]);
405         }
406 #endif  
407         if (!files) {
408                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
409                 close(fds[0]);
410                 close(fds[1]);
411                 return -1;
412         }
413         if (time(NULL) - class->start < respawn_time) {
414                 sleep(respawn_time - (time(NULL) - class->start));
415         }
416         time(&class->start);
417         class->pid = fork();
418         if (class->pid < 0) {
419                 close(fds[0]);
420                 close(fds[1]);
421                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
422                 return -1;
423         }
424         if (!class->pid) {
425                 int x;
426                 close(fds[0]);
427                 /* Stdout goes to pipe */
428                 dup2(fds[1], STDOUT_FILENO);
429                 /* Close unused file descriptors */
430                 for (x=3;x<8192;x++) {
431                         if (-1 != fcntl(x, F_GETFL)) {
432                                 close(x);
433                         }
434                 }
435                 /* Child */
436                 chdir(class->dir);
437                 if (ast_test_flag(class, MOH_CUSTOM)) {
438                         execv(argv[0], argv);
439                 } else {
440                         /* Default install is /usr/local/bin */
441                         execv(LOCAL_MPG_123, argv);
442                         /* Many places have it in /usr/bin */
443                         execv(MPG_123, argv);
444                         /* Check PATH as a last-ditch effort */
445                         execvp("mpg123", argv);
446                 }
447                 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
448                 close(fds[1]);
449                 exit(1);
450         } else {
451                 /* Parent */
452                 close(fds[1]);
453         }
454         return fds[0];
455 }
456
457 static void *monmp3thread(void *data)
458 {
459 #define MOH_MS_INTERVAL         100
460
461         struct mohclass *class = data;
462         struct mohdata *moh;
463         char buf[8192];
464         short sbuf[8192];
465         int res, res2;
466         int len;
467         struct timeval tv, tv_tmp;
468
469         tv.tv_sec = 0;
470         tv.tv_usec = 0;
471         for(;/* ever */;) {
472                 /* Spawn mp3 player if it's not there */
473                 if (class->srcfd < 0) {
474                         if ((class->srcfd = spawn_mp3(class)) < 0) {
475                                 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
476                                 /* Try again later */
477                                 sleep(500);
478                         }
479                 }
480                 if (class->pseudofd > -1) {
481                         /* Pause some amount of time */
482                         res = read(class->pseudofd, buf, sizeof(buf));
483                 } else {
484                         long delta;
485                         /* Reliable sleep */
486                         tv_tmp = ast_tvnow();
487                         if (ast_tvzero(tv))
488                                 tv = tv_tmp;
489                         delta = ast_tvdiff_ms(tv_tmp, tv);
490                         if (delta < MOH_MS_INTERVAL) {  /* too early */
491                                 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
492                                 usleep(1000 * (MOH_MS_INTERVAL - delta));
493                         } else {
494                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
495                                 tv = tv_tmp;
496                         }
497                         res = 8 * MOH_MS_INTERVAL;      /* 8 samples per millisecond */
498                 }
499                 if (!class->members)
500                         continue;
501                 /* Read mp3 audio */
502                 len = ast_codec_get_len(class->format, res);
503                 
504                 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
505                         if (!res2) {
506                                 close(class->srcfd);
507                                 class->srcfd = -1;
508                                 if (class->pid) {
509                                         kill(class->pid, SIGKILL);
510                                         class->pid = 0;
511                                 }
512                         } else
513                                 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
514                         continue;
515                 }
516                 ast_mutex_lock(&moh_lock);
517                 moh = class->members;
518                 while (moh) {
519                         /* Write data */
520                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) 
521                                 if (option_debug)
522                                         ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
523                         moh = moh->next;
524                 }
525                 ast_mutex_unlock(&moh_lock);
526         }
527         return NULL;
528 }
529
530 static int moh0_exec(struct ast_channel *chan, void *data)
531 {
532         if (ast_moh_start(chan, data)) {
533                 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
534                 return -1;
535         }
536         while (!ast_safe_sleep(chan, 10000));
537         ast_moh_stop(chan);
538         return -1;
539 }
540
541 static int moh1_exec(struct ast_channel *chan, void *data)
542 {
543         int res;
544         if (!data || !atoi(data)) {
545                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
546                 return -1;
547         }
548         if (ast_moh_start(chan, NULL)) {
549                 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
550                 return -1;
551         }
552         res = ast_safe_sleep(chan, atoi(data) * 1000);
553         ast_moh_stop(chan);
554         return res;
555 }
556
557 static int moh2_exec(struct ast_channel *chan, void *data)
558 {
559         if (!data || ast_strlen_zero(data)) {
560                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
561                 return -1;
562         }
563         strncpy(chan->musicclass, data, sizeof(chan->musicclass) - 1);
564         return 0;
565 }
566
567 static int moh3_exec(struct ast_channel *chan, void *data)
568 {
569         char *class = NULL;
570         if (data && strlen(data))
571                 class = data;
572         if (ast_moh_start(chan, class)) 
573                 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
574
575         return 0;
576 }
577
578 static int moh4_exec(struct ast_channel *chan, void *data)
579 {
580         ast_moh_stop(chan);
581
582         return 0;
583 }
584
585 static struct mohclass *get_mohbyname(char *name)
586 {
587         struct mohclass *moh;
588         moh = mohclasses;
589         while (moh) {
590                 if (!strcasecmp(name, moh->name))
591                         return moh;
592                 moh = moh->next;
593         }
594         return NULL;
595 }
596
597 static struct mohdata *mohalloc(struct mohclass *cl)
598 {
599         struct mohdata *moh;
600         long flags;
601         moh = malloc(sizeof(struct mohdata));
602         if (!moh)
603                 return NULL;
604         memset(moh, 0, sizeof(struct mohdata));
605         if (pipe(moh->pipe)) {
606                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
607                 free(moh);
608                 return NULL;
609         }
610         /* Make entirely non-blocking */
611         flags = fcntl(moh->pipe[0], F_GETFL);
612         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
613         flags = fcntl(moh->pipe[1], F_GETFL);
614         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
615         moh->parent = cl;
616         moh->next = cl->members;
617         cl->members = moh;
618         return moh;
619 }
620
621 static void moh_release(struct ast_channel *chan, void *data)
622 {
623         struct mohdata *moh = data, *prev, *cur;
624         int oldwfmt;
625         ast_mutex_lock(&moh_lock);
626         /* Unlink */
627         prev = NULL;
628         cur = moh->parent->members;
629         while (cur) {
630                 if (cur == moh) {
631                         if (prev)
632                                 prev->next = cur->next;
633                         else
634                                 moh->parent->members = cur->next;
635                         break;
636                 }
637                 prev = cur;
638                 cur = cur->next;
639         }
640         ast_mutex_unlock(&moh_lock);
641         close(moh->pipe[0]);
642         close(moh->pipe[1]);
643         oldwfmt = moh->origwfmt;
644         free(moh);
645         if (chan) {
646                 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
647                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
648                 if (option_verbose > 2)
649                         ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
650         }
651 }
652
653 static void *moh_alloc(struct ast_channel *chan, void *params)
654 {
655         struct mohdata *res;
656         struct mohclass *class = params;
657
658         res = mohalloc(class);
659         if (res) {
660                 res->origwfmt = chan->writeformat;
661                 if (ast_set_write_format(chan, class->format)) {
662                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
663                         moh_release(NULL, res);
664                         res = NULL;
665                 }
666                 if (option_verbose > 2)
667                         ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
668         }
669         return res;
670 }
671
672 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
673 {
674         struct ast_frame f;
675         struct mohdata *moh = data;
676         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
677         int res;
678
679         if (!moh->parent->pid)
680                 return -1;
681
682         len = ast_codec_get_len(moh->parent->format, samples);
683
684         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
685                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
686                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
687         }
688         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
689 #if 0
690         if (res != len) {
691                 ast_log(LOG_WARNING, "Read only %d of %d bytes: %s\n", res, len, strerror(errno));
692         }
693 #endif
694         if (res <= 0)
695                 return 0;
696
697         memset(&f, 0, sizeof(f));
698         
699         f.frametype = AST_FRAME_VOICE;
700         f.subclass = moh->parent->format;
701         f.mallocd = 0;
702         f.datalen = res;
703         f.data = buf + AST_FRIENDLY_OFFSET / 2;
704         f.offset = AST_FRIENDLY_OFFSET;
705         f.samples = ast_codec_get_samples(&f);
706
707         if (ast_write(chan, &f) < 0) {
708                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
709                 return -1;
710         }
711
712         return 0;
713 }
714
715 static struct ast_generator mohgen = 
716 {
717         alloc: moh_alloc,
718         release: moh_release,
719         generate: moh_generate,
720 };
721
722 static int moh_scan_files(struct mohclass *class) {
723
724         DIR *files_DIR;
725         struct dirent *files_dirent;
726         char path[512];
727         char filepath[MAX_MOHFILE_LEN];
728         char *ext;
729         struct stat statbuf;
730         int dirnamelen;
731         int i;
732         
733         files_DIR = opendir(class->dir);
734         if (!files_DIR) {
735                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
736                 return -1;
737         }
738
739         class->total_files = 0;
740         dirnamelen = strlen(class->dir) + 2;
741         getcwd(path, 512);
742         chdir(class->dir);
743         memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
744         while ((files_dirent = readdir(files_DIR))) {
745                 if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
746                         continue;
747
748                 snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
749
750                 if (stat(filepath, &statbuf))
751                         continue;
752
753                 if (!S_ISREG(statbuf.st_mode))
754                         continue;
755
756                 if ((ext = strrchr(filepath, '.'))) {
757                         *ext = '\0';
758                         ext++;
759                 }
760
761                 /* if the file is present in multiple formats, ensure we only put it into the list once */
762                 for (i = 0; i < class->total_files; i++)
763                         if (!strcmp(filepath, class->filearray[i]))
764                                 break;
765
766                 if (i == class->total_files)
767                         strcpy(class->filearray[class->total_files++], filepath);
768         }
769
770         closedir(files_DIR);
771         chdir(path);
772         return class->total_files;
773 }
774
775 static int moh_register(struct mohclass *moh)
776 {
777 #ifdef ZAPATA_MOH
778         int x;
779 #endif
780         ast_mutex_lock(&moh_lock);
781         if (get_mohbyname(moh->name)) {
782                 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
783                 free(moh);      
784                 ast_mutex_unlock(&moh_lock);
785                 return -1;
786         }
787         ast_mutex_unlock(&moh_lock);
788
789         time(&moh->start);
790         moh->start -= respawn_time;
791         
792         if (!strcasecmp(moh->mode, "files")) {
793                 if (!moh_scan_files(moh)) {
794                         ast_moh_free_class(&moh);
795                         return -1;
796                 }
797                 if (strchr(moh->args, 'r'))
798                         ast_set_flag(moh, MOH_RANDOMIZE);
799         } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
800
801                 if (!strcasecmp(moh->mode, "custom"))
802                         ast_set_flag(moh, MOH_CUSTOM);
803                 else if (!strcasecmp(moh->mode, "mp3nb"))
804                         ast_set_flag(moh, MOH_SINGLE);
805                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
806                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
807                 else if (!strcasecmp(moh->mode, "quietmp3"))
808                         ast_set_flag(moh, MOH_QUIET);
809                 
810                 moh->srcfd = -1;
811 #ifdef ZAPATA_MOH
812                 /* Open /dev/zap/pseudo for timing...  Is
813                    there a better, yet reliable way to do this? */
814                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
815                 if (moh->pseudofd < 0) {
816                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
817                 } else {
818                         x = 320;
819                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
820                 }
821 #else
822                 moh->pseudofd = -1;
823 #endif
824                 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
825                         ast_log(LOG_WARNING, "Unable to create moh...\n");
826                         if (moh->pseudofd > -1)
827                                 close(moh->pseudofd);
828                         ast_moh_free_class(&moh);
829                         return -1;
830                 }
831         } else {
832                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
833                 ast_moh_free_class(&moh);
834                 return -1;
835         }
836         ast_mutex_lock(&moh_lock);
837         moh->next = mohclasses;
838         mohclasses = moh;
839         ast_mutex_unlock(&moh_lock);
840         return 0;
841 }
842
843 static void local_ast_moh_cleanup(struct ast_channel *chan)
844 {
845         if (chan->music_state) {
846                 free(chan->music_state);
847                 chan->music_state = NULL;
848         }
849 }
850
851 static int local_ast_moh_start(struct ast_channel *chan, char *class)
852 {
853         struct mohclass *mohclass;
854
855         if (!class || ast_strlen_zero(class))
856                 class = chan->musicclass;
857         if (!class || ast_strlen_zero(class))
858                 class = "default";
859         ast_mutex_lock(&moh_lock);
860         mohclass = get_mohbyname(class);
861         ast_mutex_unlock(&moh_lock);
862
863         if (!mohclass) {
864                 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
865                 return -1;
866         }
867
868         ast_set_flag(chan, AST_FLAG_MOH);
869         if (mohclass->total_files) {
870                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
871         } else
872                 return ast_activate_generator(chan, &mohgen, mohclass);
873 }
874
875 static void local_ast_moh_stop(struct ast_channel *chan)
876 {
877         ast_clear_flag(chan, AST_FLAG_MOH);
878         ast_deactivate_generator(chan);
879
880         if (chan->music_state) {
881                 if (chan->stream) {
882                         ast_closestream(chan->stream);
883                         chan->stream = NULL;
884                 }
885         }
886 }
887
888 static struct mohclass *moh_class_malloc(void)
889 {
890         struct mohclass *class;
891
892         class = malloc(sizeof(struct mohclass));
893
894         if (!class)
895                 return NULL;
896
897         memset(class, 0, sizeof(struct mohclass));
898
899         class->format = AST_FORMAT_SLINEAR;
900
901         return class;
902 }
903
904 static int load_moh_classes(void)
905 {
906         struct ast_config *cfg;
907         struct ast_variable *var;
908         struct mohclass *class; 
909         char *data;
910         char *args;
911         char *cat;
912         int numclasses = 0;
913         static int dep_warning = 0;
914
915         cfg = ast_config_load("musiconhold.conf");
916
917         if (!cfg)
918                 return 0;
919
920         cat = ast_category_browse(cfg, NULL);
921         for (; cat; cat = ast_category_browse(cfg, cat)) {
922                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
923                         class = moh_class_malloc();
924                         if (!class) {
925                                 ast_log(LOG_WARNING, "Out of memory!\n");
926                                 break;
927                         }                               
928                         ast_copy_string(class->name, cat, sizeof(class->name)); 
929                         var = ast_variable_browse(cfg, cat);
930                         while (var) {
931                                 if (!strcasecmp(var->name, "mode"))
932                                         ast_copy_string(class->mode, var->value, sizeof(class->name)); 
933                                 else if (!strcasecmp(var->name, "directory"))
934                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
935                                 else if (!strcasecmp(var->name, "application"))
936                                         ast_copy_string(class->args, var->value, sizeof(class->args));
937                                 else if (!strcasecmp(var->name, "random"))
938                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
939                                 else if (!strcasecmp(var->name, "format")) {
940                                         class->format = ast_getformatbyname(var->value);
941                                         if (!class->format) {
942                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
943                                                 class->format = AST_FORMAT_SLINEAR;
944                                         }
945                                 }
946                                         var = var->next;
947                         }
948
949                         if (ast_strlen_zero(class->dir)) {
950                                 if (!strcasecmp(class->mode, "custom")) {
951                                         strcpy(class->dir, "nodir");
952                                 } else {
953                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
954                                         free(class);
955                                         continue;
956                                 }
957                         }
958                         if (ast_strlen_zero(class->mode)) {
959                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
960                                 free(class);
961                                 continue;
962                         }
963                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
964                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
965                                 free(class);
966                                 continue;
967                         }
968
969                         moh_register(class);
970                         numclasses++;
971                 }
972         }
973         
974
975         /* Deprecated Old-School Configuration */
976         var = ast_variable_browse(cfg, "classes");
977         while (var) {
978                 if (!dep_warning) {
979                         ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
980                         dep_warning = 1;
981                 }
982                 data = strchr(var->value, ':');
983                 if (data) {
984                         *data++ = '\0';
985                         args = strchr(data, ',');
986                         if (args)
987                                 *args++ = '\0';
988                         if (!(get_mohbyname(var->name))) {
989                                 class = moh_class_malloc();
990                                 if (!class) {
991                                         ast_log(LOG_WARNING, "Out of memory!\n");
992                                         return numclasses;
993                                 }
994                                 
995                                 ast_copy_string(class->name, var->name, sizeof(class->name));
996                                 ast_copy_string(class->dir, data, sizeof(class->dir));
997                                 ast_copy_string(class->mode, var->value, sizeof(class->mode));
998                                 if (args)
999                                         ast_copy_string(class->args, args, sizeof(class->args));
1000                                 
1001                                 moh_register(class);
1002                                 numclasses++;
1003                         }
1004                 }
1005                 var = var->next;
1006         }
1007         var = ast_variable_browse(cfg, "moh_files");
1008         while (var) {
1009                 if (!dep_warning) {
1010                         ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
1011                         dep_warning = 1;
1012                 }
1013                 if (!(get_mohbyname(var->name))) {
1014                         args = strchr(var->value, ',');
1015                         if (args)
1016                                 *args++ = '\0';
1017                         class = moh_class_malloc();
1018                         if (!class) {
1019                                 ast_log(LOG_WARNING, "Out of memory!\n");
1020                                 return numclasses;
1021                         }
1022                         
1023                         ast_copy_string(class->name, var->name, sizeof(class->name));
1024                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1025                         strcpy(class->mode, "files");
1026                         if (args)       
1027                                 ast_copy_string(class->args, args, sizeof(class->args));
1028                         
1029                         moh_register(class);
1030                         numclasses++;
1031                 }
1032                 var = var->next;
1033         }
1034
1035         ast_config_destroy(cfg);
1036
1037         return numclasses;
1038 }
1039
1040 static void ast_moh_destroy(void)
1041 {
1042         struct mohclass *moh, *tmp;
1043         char buff[8192];
1044         int bytes, tbytes=0, stime = 0, pid = 0;
1045
1046         if (option_verbose > 1)
1047                 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1048         ast_mutex_lock(&moh_lock);
1049         moh = mohclasses;
1050
1051         while (moh) {
1052                 if (moh->pid) {
1053                         ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1054                         stime = time(NULL) + 2;
1055                         pid = moh->pid;
1056                         moh->pid = 0;
1057                         kill(pid, SIGKILL);
1058                         while ((ast_wait_for_input(moh->srcfd, 100) > -1) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
1059                                 tbytes = tbytes + bytes;
1060                         }
1061                         ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1062                         close(moh->srcfd);
1063                 }
1064                 tmp = moh;
1065                 moh = moh->next;
1066                 ast_moh_free_class(&tmp);
1067         }
1068         mohclasses = NULL;
1069         ast_mutex_unlock(&moh_lock);
1070 }
1071
1072 static void moh_on_off(int on)
1073 {
1074         struct ast_channel *chan = NULL;
1075
1076         while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1077                 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1078                         if (on)
1079                                 local_ast_moh_start(chan, NULL);
1080                         else
1081                                 ast_deactivate_generator(chan);
1082                 }
1083                 ast_mutex_unlock(&chan->lock);
1084         }
1085 }
1086
1087 static int moh_cli(int fd, int argc, char *argv[]) 
1088 {
1089         int x;
1090
1091         moh_on_off(0);
1092         ast_moh_destroy();
1093         x = load_moh_classes();
1094         moh_on_off(1);
1095         ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1096         return 0;
1097 }
1098
1099 static int cli_files_show(int fd, int argc, char *argv[])
1100 {
1101         int i;
1102         struct mohclass *class;
1103
1104         ast_mutex_lock(&moh_lock);
1105         for (class = mohclasses; class; class = class->next) {
1106                 if (!class->total_files)
1107                         continue;
1108
1109                 ast_cli(fd, "Class: %s\n", class->name);
1110                 for (i = 0; i < class->total_files; i++)
1111                         ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1112         }
1113         ast_mutex_unlock(&moh_lock);
1114
1115         return 0;
1116 }
1117
1118 static int moh_classes_show(int fd, int argc, char *argv[])
1119 {
1120         struct mohclass *class;
1121
1122         ast_mutex_lock(&moh_lock);
1123         for (class = mohclasses; class; class = class->next) {
1124                 ast_cli(fd, "Class: %s\n", class->name);
1125                 ast_cli(fd, "\tMode: %s\n", ast_strlen_zero(class->mode) ? "<none>" : class->mode);
1126                 ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "<none>" : class->dir);
1127                 if (ast_test_flag(class, MOH_CUSTOM))
1128                         ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "<none>" : class->args);
1129                 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1130         }
1131         ast_mutex_unlock(&moh_lock);
1132
1133         return 0;
1134 }
1135
1136 static struct ast_cli_entry  cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1137
1138 static struct ast_cli_entry  cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1139
1140 static struct ast_cli_entry  cli_moh_files_show = { { "moh", "files", "show"}, cli_files_show, "List MOH file-based classes", "Lists all loaded file-based MOH classes and their files", NULL};
1141
1142 static int init_classes(void) 
1143 {
1144         struct mohclass *moh;
1145     
1146         if (!load_moh_classes())                /* Load classes from config */
1147                 return 0;                       /* Return if nothing is found */
1148         moh = mohclasses;
1149         while (moh) {
1150                 if (moh->total_files)
1151                         moh_scan_files(moh);
1152                 moh = moh->next;
1153         }
1154         return 1;
1155 }
1156
1157 int load_module(void)
1158 {
1159         int res;
1160
1161         res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1162         ast_register_atexit(ast_moh_destroy);
1163         ast_cli_register(&cli_moh);
1164         ast_cli_register(&cli_moh_files_show);
1165         ast_cli_register(&cli_moh_classes_show);
1166         if (!res)
1167                 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1168         if (!res)
1169                 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1170         if (!res)
1171                 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1172         if (!res)
1173                 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1174
1175         if (!init_classes()) {  /* No music classes configured, so skip it */
1176                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
1177         } else {
1178                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1179         }
1180
1181         return 0;
1182 }
1183
1184 int reload(void)
1185 {
1186         if (init_classes())
1187                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1188
1189         return 0;
1190 }
1191
1192 int unload_module(void)
1193 {
1194         return -1;
1195 }
1196
1197 char *description(void)
1198 {
1199         return "Music On Hold Resource";
1200 }
1201
1202 int usecount(void)
1203 {
1204         /* Never allow Music On Hold to be unloaded
1205            unresolve needed symbols in the dialer */
1206 #if 0
1207         int res;
1208         STANDARD_USECOUNT(res);
1209         return res;
1210 #else
1211         return 1;
1212 #endif
1213 }
1214
1215 char *key()
1216 {
1217         return ASTERISK_GPL_KEY;
1218 }