add a comment where some memory usage optimization needs to be done
[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                         /* check to see if this file's format can be opened */
222                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) != -1)
223                                 break;
224
225                 }
226         }
227
228         state->pos = state->pos % state->class->total_files;
229         
230         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
231                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
232                 state->pos++;
233                 return -1;
234         }
235
236         if (option_debug)
237                 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
238
239         if (state->samples)
240                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
241
242         return 0;
243 }
244
245
246 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
247 {
248         struct ast_frame *f = NULL;
249         
250         if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
251                 if (!ast_moh_files_next(chan))
252                         f = ast_readframe(chan->stream);
253         }
254
255         return f;
256 }
257
258 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
259 {
260         struct moh_files_state *state = chan->music_state;
261         struct ast_frame *f = NULL;
262         int res = 0;
263
264         state->sample_queue += samples;
265
266         while (state->sample_queue > 0) {
267                 if ((f = moh_files_readframe(chan))) {
268                         state->samples += f->samples;
269                         res = ast_write(chan, f);
270                         state->sample_queue -= f->samples;
271                         ast_frfree(f);
272                         if (res < 0) {
273                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
274                                 return -1;
275                         }
276                 } else
277                         return -1;      
278         }
279         return res;
280 }
281
282
283 static void *moh_files_alloc(struct ast_channel *chan, void *params)
284 {
285         struct moh_files_state *state;
286         struct mohclass *class = params;
287
288         if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
289                 chan->music_state = state;
290                 state->class = class;
291         } else 
292                 state = chan->music_state;
293
294         if (state) {
295                 if (state->class != class) {
296                         /* initialize */
297                         memset(state, 0, sizeof(*state));
298                         state->class = class;
299                 }
300
301                 state->origwfmt = chan->writeformat;
302
303                 if (option_verbose > 2)
304                         ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
305         }
306         
307         return chan->music_state;
308 }
309
310 static struct ast_generator moh_file_stream = 
311 {
312         alloc: moh_files_alloc,
313         release: moh_files_release,
314         generate: moh_files_generator,
315 };
316
317 static int spawn_mp3(struct mohclass *class)
318 {
319         int fds[2];
320         int files = 0;
321         char fns[MAX_MP3S][80];
322         char *argv[MAX_MP3S + 50];
323         char xargs[256];
324         char *argptr;
325         int argc = 0;
326         DIR *dir = NULL;
327         struct dirent *de;
328
329         
330         if (!strcasecmp(class->dir, "nodir")) {
331                 files = 1;
332         } else {
333                 dir = opendir(class->dir);
334                 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
335                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
336                         return -1;
337                 }
338         }
339
340         if (!ast_test_flag(class, MOH_CUSTOM)) {
341                 argv[argc++] = "mpg123";
342                 argv[argc++] = "-q";
343                 argv[argc++] = "-s";
344                 argv[argc++] = "--mono";
345                 argv[argc++] = "-r";
346                 argv[argc++] = "8000";
347                 
348                 if (!ast_test_flag(class, MOH_SINGLE)) {
349                         argv[argc++] = "-b";
350                         argv[argc++] = "2048";
351                 }
352                 
353                 argv[argc++] = "-f";
354                 
355                 if (ast_test_flag(class, MOH_QUIET))
356                         argv[argc++] = "4096";
357                 else
358                         argv[argc++] = "8192";
359                 
360                 /* Look for extra arguments and add them to the list */
361                 ast_copy_string(xargs, class->args, sizeof(xargs));
362                 argptr = xargs;
363                 while (!ast_strlen_zero(argptr)) {
364                         argv[argc++] = argptr;
365                         strsep(&argptr, ",");
366                 }
367         } else  {
368                 /* Format arguments for argv vector */
369                 ast_copy_string(xargs, class->args, sizeof(xargs));
370                 argptr = xargs;
371                 while (!ast_strlen_zero(argptr)) {
372                         argv[argc++] = argptr;
373                         strsep(&argptr, " ");
374                 }
375         }
376
377
378         if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
379                 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
380                 argv[argc++] = fns[files];
381                 files++;
382         } else if (dir) {
383                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
384                         if ((strlen(de->d_name) > 3) && 
385                             ((ast_test_flag(class, MOH_CUSTOM) && 
386                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
387                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
388                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
389                                 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
390                                 argv[argc++] = fns[files];
391                                 files++;
392                         }
393                 }
394         }
395         argv[argc] = NULL;
396         if (dir) {
397                 closedir(dir);
398         }
399         if (pipe(fds)) {        
400                 ast_log(LOG_WARNING, "Pipe failed\n");
401                 return -1;
402         }
403 #if 0
404         printf("%d files total, %d args total\n", files, argc);
405         {
406                 int x;
407                 for (x=0;argv[x];x++)
408                         printf("arg%d: %s\n", x, argv[x]);
409         }
410 #endif  
411         if (!files) {
412                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
413                 close(fds[0]);
414                 close(fds[1]);
415                 return -1;
416         }
417         if (time(NULL) - class->start < respawn_time) {
418                 sleep(respawn_time - (time(NULL) - class->start));
419         }
420         time(&class->start);
421         class->pid = fork();
422         if (class->pid < 0) {
423                 close(fds[0]);
424                 close(fds[1]);
425                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
426                 return -1;
427         }
428         if (!class->pid) {
429                 int x;
430
431                 if (ast_opt_high_priority)
432                         ast_set_priority(0);
433
434                 close(fds[0]);
435                 /* Stdout goes to pipe */
436                 dup2(fds[1], STDOUT_FILENO);
437                 /* Close unused file descriptors */
438                 for (x=3;x<8192;x++) {
439                         if (-1 != fcntl(x, F_GETFL)) {
440                                 close(x);
441                         }
442                 }
443                 /* Child */
444                 chdir(class->dir);
445                 if (ast_test_flag(class, MOH_CUSTOM)) {
446                         execv(argv[0], argv);
447                 } else {
448                         /* Default install is /usr/local/bin */
449                         execv(LOCAL_MPG_123, argv);
450                         /* Many places have it in /usr/bin */
451                         execv(MPG_123, argv);
452                         /* Check PATH as a last-ditch effort */
453                         execvp("mpg123", argv);
454                 }
455                 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
456                 close(fds[1]);
457                 exit(1);
458         } else {
459                 /* Parent */
460                 close(fds[1]);
461         }
462         return fds[0];
463 }
464
465 static void *monmp3thread(void *data)
466 {
467 #define MOH_MS_INTERVAL         100
468
469         struct mohclass *class = data;
470         struct mohdata *moh;
471         char buf[8192];
472         short sbuf[8192];
473         int res, res2;
474         int len;
475         struct timeval tv, tv_tmp;
476
477         tv.tv_sec = 0;
478         tv.tv_usec = 0;
479         for(;/* ever */;) {
480                 pthread_testcancel();
481                 /* Spawn mp3 player if it's not there */
482                 if (class->srcfd < 0) {
483                         if ((class->srcfd = spawn_mp3(class)) < 0) {
484                                 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
485                                 /* Try again later */
486                                 sleep(500);
487                                 pthread_testcancel();
488                         }
489                 }
490                 if (class->pseudofd > -1) {
491                         /* Pause some amount of time */
492                         res = read(class->pseudofd, buf, sizeof(buf));
493                         pthread_testcancel();
494                 } else {
495                         long delta;
496                         /* Reliable sleep */
497                         tv_tmp = ast_tvnow();
498                         if (ast_tvzero(tv))
499                                 tv = tv_tmp;
500                         delta = ast_tvdiff_ms(tv_tmp, tv);
501                         if (delta < MOH_MS_INTERVAL) {  /* too early */
502                                 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
503                                 usleep(1000 * (MOH_MS_INTERVAL - delta));
504                                 pthread_testcancel();
505                         } else {
506                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
507                                 tv = tv_tmp;
508                         }
509                         res = 8 * MOH_MS_INTERVAL;      /* 8 samples per millisecond */
510                 }
511                 if (AST_LIST_EMPTY(&class->members))
512                         continue;
513                 /* Read mp3 audio */
514                 len = ast_codec_get_len(class->format, res);
515                 
516                 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
517                         if (!res2) {
518                                 close(class->srcfd);
519                                 class->srcfd = -1;
520                                 pthread_testcancel();
521                                 if (class->pid) {
522                                         kill(class->pid, SIGHUP);
523                                         usleep(100000);
524                                         kill(class->pid, SIGTERM);
525                                         usleep(100000);
526                                         kill(class->pid, SIGKILL);
527                                         class->pid = 0;
528                                 }
529                         } else
530                                 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
531                         continue;
532                 }
533                 pthread_testcancel();
534                 AST_LIST_LOCK(&mohclasses);
535                 AST_LIST_TRAVERSE(&class->members, moh, list) {
536                         /* Write data */
537                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
538                                 if (option_debug)
539                                         ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
540                         }
541                 }
542                 AST_LIST_UNLOCK(&mohclasses);
543         }
544         return NULL;
545 }
546
547 static int moh0_exec(struct ast_channel *chan, void *data)
548 {
549         if (ast_moh_start(chan, data)) {
550                 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
551                 return -1;
552         }
553         while (!ast_safe_sleep(chan, 10000));
554         ast_moh_stop(chan);
555         return -1;
556 }
557
558 static int moh1_exec(struct ast_channel *chan, void *data)
559 {
560         int res;
561         if (!data || !atoi(data)) {
562                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
563                 return -1;
564         }
565         if (ast_moh_start(chan, NULL)) {
566                 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
567                 return -1;
568         }
569         res = ast_safe_sleep(chan, atoi(data) * 1000);
570         ast_moh_stop(chan);
571         return res;
572 }
573
574 static int moh2_exec(struct ast_channel *chan, void *data)
575 {
576         if (ast_strlen_zero(data)) {
577                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
578                 return -1;
579         }
580         ast_string_field_set(chan, musicclass, data);
581         return 0;
582 }
583
584 static int moh3_exec(struct ast_channel *chan, void *data)
585 {
586         char *class = NULL;
587         if (data && strlen(data))
588                 class = data;
589         if (ast_moh_start(chan, class)) 
590                 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
591
592         return 0;
593 }
594
595 static int moh4_exec(struct ast_channel *chan, void *data)
596 {
597         ast_moh_stop(chan);
598
599         return 0;
600 }
601
602 /*! \note This function should be called with the mohclasses list locked */
603 static struct mohclass *get_mohbyname(const char *name)
604 {
605         struct mohclass *moh = NULL;
606
607         AST_LIST_TRAVERSE(&mohclasses, moh, list) {
608                 if (!strcasecmp(name, moh->name))
609                         break;
610         }
611
612         return moh;
613 }
614
615 static struct mohdata *mohalloc(struct mohclass *cl)
616 {
617         struct mohdata *moh;
618         long flags;     
619         
620         if (!(moh = ast_calloc(1, sizeof(*moh))))
621                 return NULL;
622         
623         if (pipe(moh->pipe)) {
624                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
625                 free(moh);
626                 return NULL;
627         }
628
629         /* Make entirely non-blocking */
630         flags = fcntl(moh->pipe[0], F_GETFL);
631         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
632         flags = fcntl(moh->pipe[1], F_GETFL);
633         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
634
635         moh->f.frametype = AST_FRAME_VOICE;
636         moh->f.subclass = cl->format;
637         moh->f.offset = AST_FRIENDLY_OFFSET;
638
639         moh->parent = cl;
640         AST_LIST_INSERT_HEAD(&cl->members, moh, list);
641         
642         return moh;
643 }
644
645 static void moh_release(struct ast_channel *chan, void *data)
646 {
647         struct mohdata *moh = data;
648         int oldwfmt;
649
650         AST_LIST_LOCK(&mohclasses);
651         AST_LIST_REMOVE(&moh->parent->members, moh, list);      
652         AST_LIST_UNLOCK(&mohclasses);
653         
654         close(moh->pipe[0]);
655         close(moh->pipe[1]);
656         oldwfmt = moh->origwfmt;
657         free(moh);
658         if (chan) {
659                 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
660                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
661                 if (option_verbose > 2)
662                         ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
663         }
664 }
665
666 static void *moh_alloc(struct ast_channel *chan, void *params)
667 {
668         struct mohdata *res;
669         struct mohclass *class = params;
670
671         if ((res = mohalloc(class))) {
672                 res->origwfmt = chan->writeformat;
673                 if (ast_set_write_format(chan, class->format)) {
674                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
675                         moh_release(NULL, res);
676                         res = NULL;
677                 }
678                 if (option_verbose > 2)
679                         ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
680         }
681         return res;
682 }
683
684 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
685 {
686         struct mohdata *moh = data;
687         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
688         int res;
689
690         if (!moh->parent->pid)
691                 return -1;
692
693         len = ast_codec_get_len(moh->parent->format, samples);
694
695         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
696                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
697                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
698         }
699         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
700         if (res <= 0)
701                 return 0;
702
703         moh->f.datalen = res;
704         moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
705         moh->f.samples = ast_codec_get_samples(&moh->f);
706
707         if (ast_write(chan, &moh->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                 /* If the new total files is equal to the maximum allowed, stop adding new ones */
770                 if (class->total_files == MAX_MOHFILES)
771                         break;
772
773         }
774
775         closedir(files_DIR);
776         chdir(path);
777         return class->total_files;
778 }
779
780 static int moh_register(struct mohclass *moh, int reload)
781 {
782 #ifdef HAVE_ZAPTEL
783         int x;
784 #endif
785         AST_LIST_LOCK(&mohclasses);
786         if (get_mohbyname(moh->name)) {
787                 if (reload) {
788                         ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
789                 } else {
790                         ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
791                 }
792                 free(moh);      
793                 AST_LIST_UNLOCK(&mohclasses);
794                 return -1;
795         }
796         AST_LIST_UNLOCK(&mohclasses);
797
798         time(&moh->start);
799         moh->start -= respawn_time;
800         
801         if (!strcasecmp(moh->mode, "files")) {
802                 if (!moh_scan_files(moh)) {
803                         ast_moh_free_class(&moh);
804                         return -1;
805                 }
806                 if (strchr(moh->args, 'r'))
807                         ast_set_flag(moh, MOH_RANDOMIZE);
808         } 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")) {
809
810                 if (!strcasecmp(moh->mode, "custom"))
811                         ast_set_flag(moh, MOH_CUSTOM);
812                 else if (!strcasecmp(moh->mode, "mp3nb"))
813                         ast_set_flag(moh, MOH_SINGLE);
814                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
815                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
816                 else if (!strcasecmp(moh->mode, "quietmp3"))
817                         ast_set_flag(moh, MOH_QUIET);
818                 
819                 moh->srcfd = -1;
820 #ifdef HAVE_ZAPTEL
821                 /* Open /dev/zap/pseudo for timing...  Is
822                    there a better, yet reliable way to do this? */
823                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
824                 if (moh->pseudofd < 0) {
825                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
826                 } else {
827                         x = 320;
828                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
829                 }
830 #else
831                 moh->pseudofd = -1;
832 #endif
833                 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
834                         ast_log(LOG_WARNING, "Unable to create moh...\n");
835                         if (moh->pseudofd > -1)
836                                 close(moh->pseudofd);
837                         ast_moh_free_class(&moh);
838                         return -1;
839                 }
840         } else {
841                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
842                 ast_moh_free_class(&moh);
843                 return -1;
844         }
845
846         AST_LIST_LOCK(&mohclasses);
847         AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
848         AST_LIST_UNLOCK(&mohclasses);
849         
850         return 0;
851 }
852
853 static void local_ast_moh_cleanup(struct ast_channel *chan)
854 {
855         if (chan->music_state) {
856                 free(chan->music_state);
857                 chan->music_state = NULL;
858         }
859 }
860
861 static int local_ast_moh_start(struct ast_channel *chan, const char *class)
862 {
863         struct mohclass *mohclass;
864
865         if (ast_strlen_zero(class))
866                 class = chan->musicclass;
867         if (ast_strlen_zero(class))
868                 class = "default";
869         AST_LIST_LOCK(&mohclasses);
870         mohclass = get_mohbyname(class);
871         AST_LIST_UNLOCK(&mohclasses);
872
873         if (!mohclass) {
874                 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
875                 return -1;
876         }
877
878         ast_set_flag(chan, AST_FLAG_MOH);
879         if (mohclass->total_files) {
880                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
881         } else
882                 return ast_activate_generator(chan, &mohgen, mohclass);
883 }
884
885 static void local_ast_moh_stop(struct ast_channel *chan)
886 {
887         ast_clear_flag(chan, AST_FLAG_MOH);
888         ast_deactivate_generator(chan);
889
890         if (chan->music_state) {
891                 if (chan->stream) {
892                         ast_closestream(chan->stream);
893                         chan->stream = NULL;
894                 }
895         }
896 }
897
898 static struct mohclass *moh_class_malloc(void)
899 {
900         struct mohclass *class;
901
902         if ((class = ast_calloc(1, sizeof(*class))))            
903                 class->format = AST_FORMAT_SLINEAR;
904
905         return class;
906 }
907
908 static int load_moh_classes(int reload)
909 {
910         struct ast_config *cfg;
911         struct ast_variable *var;
912         struct mohclass *class; 
913         char *data;
914         char *args;
915         char *cat;
916         int numclasses = 0;
917         static int dep_warning = 0;
918
919         cfg = ast_config_load("musiconhold.conf");
920
921         if (!cfg)
922                 return 0;
923
924         cat = ast_category_browse(cfg, NULL);
925         for (; cat; cat = ast_category_browse(cfg, cat)) {
926                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {                       
927                         if (!(class = moh_class_malloc())) {
928                                 break;
929                         }                               
930                         ast_copy_string(class->name, cat, sizeof(class->name)); 
931                         var = ast_variable_browse(cfg, cat);
932                         while (var) {
933                                 if (!strcasecmp(var->name, "mode"))
934                                         ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
935                                 else if (!strcasecmp(var->name, "directory"))
936                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
937                                 else if (!strcasecmp(var->name, "application"))
938                                         ast_copy_string(class->args, var->value, sizeof(class->args));
939                                 else if (!strcasecmp(var->name, "random"))
940                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
941                                 else if (!strcasecmp(var->name, "format")) {
942                                         class->format = ast_getformatbyname(var->value);
943                                         if (!class->format) {
944                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
945                                                 class->format = AST_FORMAT_SLINEAR;
946                                         }
947                                 }
948                                 var = var->next;
949                         }
950
951                         if (ast_strlen_zero(class->dir)) {
952                                 if (!strcasecmp(class->mode, "custom")) {
953                                         strcpy(class->dir, "nodir");
954                                 } else {
955                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
956                                         free(class);
957                                         continue;
958                                 }
959                         }
960                         if (ast_strlen_zero(class->mode)) {
961                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
962                                 free(class);
963                                 continue;
964                         }
965                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
966                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
967                                 free(class);
968                                 continue;
969                         }
970
971                         /* Don't leak a class when it's already registered */
972                         moh_register(class, reload);
973
974                         numclasses++;
975                 }
976         }
977         
978
979         /* Deprecated Old-School Configuration */
980         var = ast_variable_browse(cfg, "classes");
981         while (var) {
982                 if (!dep_warning) {
983                         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");
984                         dep_warning = 1;
985                 }
986                 data = strchr(var->value, ':');
987                 if (data) {
988                         *data++ = '\0';
989                         args = strchr(data, ',');
990                         if (args)
991                                 *args++ = '\0';
992                         if (!(get_mohbyname(var->name))) {                      
993                                 if (!(class = moh_class_malloc())) {
994                                         return numclasses;
995                                 }
996                                 
997                                 ast_copy_string(class->name, var->name, sizeof(class->name));
998                                 ast_copy_string(class->dir, data, sizeof(class->dir));
999                                 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1000                                 if (args)
1001                                         ast_copy_string(class->args, args, sizeof(class->args));
1002                                 
1003                                 moh_register(class, reload);
1004                                 numclasses++;
1005                         }
1006                 }
1007                 var = var->next;
1008         }
1009         var = ast_variable_browse(cfg, "moh_files");
1010         while (var) {
1011                 if (!dep_warning) {
1012                         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");
1013                         dep_warning = 1;
1014                 }
1015                 if (!(get_mohbyname(var->name))) {
1016                         args = strchr(var->value, ',');
1017                         if (args)
1018                                 *args++ = '\0';                 
1019                         if (!(class = moh_class_malloc())) {
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, reload);
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;
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
1049         AST_LIST_LOCK(&mohclasses);
1050         while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
1051                 if (moh->pid) {
1052                         ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1053                         stime = time(NULL) + 2;
1054                         pid = moh->pid;
1055                         moh->pid = 0;
1056                         /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1057                          * to give the process a reason and time enough to kill off its
1058                          * children. */
1059                         kill(pid, SIGHUP);
1060                         usleep(100000);
1061                         kill(pid, SIGTERM);
1062                         usleep(100000);
1063                         kill(pid, SIGKILL);
1064                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1065                                 tbytes = tbytes + bytes;
1066                         ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1067                         close(moh->srcfd);
1068                 }
1069                 ast_moh_free_class(&moh);
1070         }
1071         AST_LIST_UNLOCK(&mohclasses);
1072 }
1073
1074 static void moh_on_off(int on)
1075 {
1076         struct ast_channel *chan = NULL;
1077
1078         while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1079                 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1080                         if (on)
1081                                 local_ast_moh_start(chan, NULL);
1082                         else
1083                                 ast_deactivate_generator(chan);
1084                 }
1085                 ast_channel_unlock(chan);
1086         }
1087 }
1088
1089 static int moh_cli(int fd, int argc, char *argv[]) 
1090 {
1091         int x;
1092
1093         moh_on_off(0);
1094         ast_moh_destroy();
1095         x = load_moh_classes(1);
1096         moh_on_off(1);
1097         ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1098         return 0;
1099 }
1100
1101 static int cli_files_show(int fd, int argc, char *argv[])
1102 {
1103         int i;
1104         struct mohclass *class;
1105
1106         AST_LIST_LOCK(&mohclasses);
1107         AST_LIST_TRAVERSE(&mohclasses, class, list) {
1108                 if (!class->total_files)
1109                         continue;
1110
1111                 ast_cli(fd, "Class: %s\n", class->name);
1112                 for (i = 0; i < class->total_files; i++)
1113                         ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1114         }
1115         AST_LIST_UNLOCK(&mohclasses);
1116
1117         return 0;
1118 }
1119
1120 static int moh_classes_show(int fd, int argc, char *argv[])
1121 {
1122         struct mohclass *class;
1123
1124         AST_LIST_LOCK(&mohclasses);
1125         AST_LIST_TRAVERSE(&mohclasses, class, list) {
1126                 ast_cli(fd, "Class: %s\n", class->name);
1127                 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1128                 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1129                 if (ast_test_flag(class, MOH_CUSTOM))
1130                         ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1131                 if (strcasecmp(class->mode, "files"))
1132                         ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1133         }
1134         AST_LIST_UNLOCK(&mohclasses);
1135
1136         return 0;
1137 }
1138
1139 static struct ast_cli_entry  cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1140
1141 static struct ast_cli_entry  cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1142
1143 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};
1144
1145 static int init_classes(int reload) 
1146 {
1147         struct mohclass *moh;
1148     
1149         if (!load_moh_classes(reload))          /* Load classes from config */
1150                 return 0;                       /* Return if nothing is found */
1151
1152         AST_LIST_LOCK(&mohclasses);
1153         AST_LIST_TRAVERSE(&mohclasses, moh, list) {
1154                 if (moh->total_files)
1155                         moh_scan_files(moh);
1156         }
1157         AST_LIST_UNLOCK(&mohclasses);
1158
1159         return 1;
1160 }
1161
1162 static int load_module(void *mod)
1163 {
1164         int res;
1165
1166         res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1167         ast_register_atexit(ast_moh_destroy);
1168         ast_cli_register(&cli_moh);
1169         ast_cli_register(&cli_moh_files_show);
1170         ast_cli_register(&cli_moh_classes_show);
1171         if (!res)
1172                 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1173         if (!res)
1174                 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1175         if (!res)
1176                 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1177         if (!res)
1178                 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1179
1180         if (!init_classes(0)) {         /* No music classes configured, so skip it */
1181                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
1182         } else {
1183                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1184         }
1185
1186         return 0;
1187 }
1188
1189 static int reload(void *mod)
1190 {
1191         if (init_classes(1))
1192                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1193
1194         return 0;
1195 }
1196
1197 static int unload_module(void *mod)
1198 {
1199         return -1;
1200 }
1201
1202 static const char *description(void)
1203 {
1204         return "Music On Hold Resource";
1205 }
1206
1207 static const char *key(void)
1208 {
1209         return ASTERISK_GPL_KEY;
1210 }
1211
1212 STD_MOD(MOD_0 | NO_USECOUNT | NO_UNLOAD, reload, NULL, NULL);
1213