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