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