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