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