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