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