tweak to make music load classes more elegantly
[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         struct ast_filestream *stream;
83         int origwfmt;
84         int samples;
85         int sample_queue;
86         unsigned char pos;
87         unsigned char save_pos;
88 };
89
90 struct mohclass {
91         char class[80];
92         char dir[256];
93         char miscargs[256];
94         char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
95         int total_files;
96         int destroyme;
97         int pid;                /* PID of mpg123 */
98         int quiet;
99         int single;
100         int custom;
101         int randomize;
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         struct moh_files_state *state = chan->music_state;
145
146         if(state->save_pos) {
147                 state->pos = state->save_pos - 1;
148                 state->save_pos = 0;
149         } else {
150                 state->samples = 0;
151                 if (chan->stream) {
152                         ast_closestream(chan->stream);
153                         chan->stream = NULL;
154                         state->pos++;
155                 }
156
157                 if (state->class->randomize) {
158                         srand(time(NULL)+getpid()+strlen(chan->name)-state->class->total_files);
159                         state->pos = rand();
160                 }
161         }
162
163         state->pos = state->pos % state->class->total_files;
164         
165         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
166                 ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
167                 return -1;
168         }
169         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
170                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
171                 state->pos++;
172                 return -1;
173         }
174
175         if (option_verbose > 2)
176                 ast_log(LOG_NOTICE, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
177
178
179         if (state->samples)
180                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
181
182         return state->pos;
183 }
184
185
186 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) {
187         struct ast_frame *f = NULL;
188         
189         if (!chan->stream || !(f = ast_readframe(chan->stream))) {
190                 if (ast_moh_files_next(chan) > -1)
191                         f = ast_readframe(chan->stream);
192         }
193
194         return f;
195 }
196
197 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
198 {
199         struct moh_files_state *state = chan->music_state;
200         struct ast_frame *f = NULL;
201         int res = 0;
202         state->sample_queue += samples;
203
204         while(state->sample_queue > 0) {
205                 if ((f = moh_files_readframe(chan))) {
206                         state->samples += f->samples;
207                         res = ast_write(chan, f);
208                         ast_frfree(f);
209                         if(res < 0) {
210                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
211                                 return -1;
212                         }
213                         state->sample_queue -= f->samples;
214                 } else
215                         return -1;      
216         }
217         return res;
218 }
219
220
221 static void *moh_files_alloc(struct ast_channel *chan, void *params)
222 {
223         struct moh_files_state *state;
224         struct mohclass *class = params;
225         int allocated = 0;
226
227         if ((!chan->music_state) && ((state = malloc(sizeof(struct moh_files_state))))) {
228                 chan->music_state = state;
229                 allocated = 1;
230         } else 
231                 state = chan->music_state;
232
233
234         if(state) {
235                 if (allocated || state->class != class) {
236                         /* initialize */
237                         memset(state, 0, sizeof(struct moh_files_state));
238                         state->class = class;
239                 }
240
241                 state->origwfmt = chan->writeformat;
242
243                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
244                         ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
245                         free(chan->music_state);
246                         chan->music_state = NULL;
247                 } else {
248                         if (option_verbose > 2)
249                                 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", (char *)params, chan->name);
250                 }
251
252
253         }
254         
255         return chan->music_state;
256 }
257
258 static struct ast_generator moh_file_stream = 
259 {
260         alloc: moh_files_alloc,
261         release: moh_files_release,
262         generate: moh_files_generator,
263 };
264
265 static int spawn_mp3(struct mohclass *class)
266 {
267         int fds[2];
268         int files=0;
269         char fns[MAX_MP3S][80];
270         char *argv[MAX_MP3S + 50];
271         char xargs[256];
272         char *argptr;
273         int argc = 0;
274         DIR *dir;
275         struct dirent *de;
276
277         
278         dir = opendir(class->dir);
279         if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
280                 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
281                 return -1;
282         }
283
284         if (!class->custom) {
285                 argv[argc++] = "mpg123";
286                 argv[argc++] = "-q";
287                 argv[argc++] = "-s";
288                 argv[argc++] = "--mono";
289                 argv[argc++] = "-r";
290                 argv[argc++] = "8000";
291                 
292                 if (!class->single) {
293                         argv[argc++] = "-b";
294                         argv[argc++] = "2048";
295                 }
296                 
297                 argv[argc++] = "-f";
298                 
299                 if (class->quiet)
300                         argv[argc++] = "4096";
301                 else
302                         argv[argc++] = "8192";
303                 
304                 /* Look for extra arguments and add them to the list */
305                 strncpy(xargs, class->miscargs, sizeof(xargs) - 1);
306                 argptr = xargs;
307                 while(argptr && !ast_strlen_zero(argptr)) {
308                         argv[argc++] = argptr;
309                         argptr = strchr(argptr, ',');
310                         if (argptr) {
311                                 *argptr = '\0';
312                                 argptr++;
313                         }
314                 }
315         } else  {
316                 /* Format arguments for argv vector */
317                 strncpy(xargs, class->miscargs, sizeof(xargs) - 1);
318                 argptr = xargs;
319                 while(argptr && !ast_strlen_zero(argptr)) {
320                         argv[argc++] = argptr;
321                         argptr = strchr(argptr, ' ');
322                         if (argptr) {
323                                 *argptr = '\0';
324                                 argptr++;
325                         }
326                 }
327         }
328
329         files = 0;
330         if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
331                 strncpy(fns[files], class->dir, sizeof(fns[files]) - 1);
332                 argv[argc++] = fns[files];
333                 files++;
334         } else {
335                 while((de = readdir(dir)) && (files < MAX_MP3S)) {
336                         if ((strlen(de->d_name) > 3) && 
337                                 ((class->custom && 
338                                   (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
339                                    !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln")))
340                                  || !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
341                                 strncpy(fns[files], de->d_name, sizeof(fns[files]) - 1);
342                                 argv[argc++] = fns[files];
343                                 files++;
344                         }
345                 }
346         }
347         argv[argc] = NULL;
348         closedir(dir);
349         
350         if (pipe(fds)) {        
351                 ast_log(LOG_WARNING, "Pipe failed\n");
352                 return -1;
353         }
354 #if 0
355         printf("%d files total, %d args total\n", files, argc);
356         {
357                 int x;
358                 for (x=0;argv[x];x++)
359                         printf("arg%d: %s\n", x, argv[x]);
360         }
361 #endif  
362         if (!files) {
363                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
364                 close(fds[0]);
365                 close(fds[1]);
366                 return -1;
367         }
368         if (time(NULL) - class->start < respawn_time) {
369                 sleep(respawn_time - (time(NULL) - class->start));
370         }
371         time(&class->start);
372         class->pid = fork();
373         if (class->pid < 0) {
374                 close(fds[0]);
375                 close(fds[1]);
376                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
377                 return -1;
378         }
379         if (!class->pid) {
380                 int x;
381                 close(fds[0]);
382                 /* Stdout goes to pipe */
383                 dup2(fds[1], STDOUT_FILENO);
384                 /* Close unused file descriptors */
385                 for (x=3;x<8192;x++) {
386                         if (-1 != fcntl(x, F_GETFL)) {
387                                 close(x);
388                         }
389                 }
390         /* Child */
391                 chdir(class->dir);
392                 if(class->custom) {
393                         execv(argv[0], argv);
394         } else {
395             /* Default install is /usr/local/bin */
396             execv(LOCAL_MPG_123, argv);
397             /* Many places have it in /usr/bin */
398             execv(MPG_123, argv);
399             /* Check PATH as a last-ditch effort */
400             execvp("mpg123", argv);
401         }
402                 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
403                 close(fds[1]);
404                 exit(1);
405         } else {
406                 /* Parent */
407                 close(fds[1]);
408         }
409         return fds[0];
410 }
411
412 static void *monmp3thread(void *data)
413 {
414 #define MOH_MS_INTERVAL         100
415
416         struct mohclass *class = data;
417         struct mohdata *moh;
418         char buf[8192];
419         short sbuf[8192];
420         int res, res2;
421         struct timeval tv;
422         struct timeval tv_tmp;
423         long error_sec, error_usec;
424         long delay;
425
426         tv_tmp.tv_sec = 0;
427         tv_tmp.tv_usec = 0;
428         tv.tv_sec = 0;
429         tv.tv_usec = 0;
430         error_sec = 0;
431         error_usec = 0;
432         for(;/* ever */;) {
433                 /* Spawn mp3 player if it's not there */
434                 if (class->srcfd < 0) {
435                         if ((class->srcfd = spawn_mp3(class)) < 0) {
436                                 ast_log(LOG_WARNING, "unable to spawn mp3player\n");
437                                 /* Try again later */
438                                 sleep(500);
439                         }
440                 }
441                 if (class->pseudofd > -1) {
442                         /* Pause some amount of time */
443                         res = read(class->pseudofd, buf, sizeof(buf));
444                 } else {
445                         /* Reliable sleep */
446                         if (gettimeofday(&tv_tmp, NULL) < 0) {
447                                 ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
448                                 return NULL;
449                         }
450                         if (((unsigned long)(tv.tv_sec) > 0)&&((unsigned long)(tv.tv_usec) > 0)) {
451                                 if ((unsigned long)(tv_tmp.tv_usec) < (unsigned long)(tv.tv_usec)) {
452                                         tv_tmp.tv_usec += 1000000;
453                                         tv_tmp.tv_sec -= 1;
454                                 }
455                                 error_sec = (unsigned long)(tv_tmp.tv_sec) - (unsigned long)(tv.tv_sec);
456                                 error_usec = (unsigned long)(tv_tmp.tv_usec) - (unsigned long)(tv.tv_usec);
457                         } else {
458                                 error_sec = 0;
459                                 error_usec = 0;
460                         }
461                         if (error_sec * 1000 + error_usec / 1000 < MOH_MS_INTERVAL) {
462                                 tv.tv_sec = tv_tmp.tv_sec + (MOH_MS_INTERVAL/1000 - error_sec);
463                                 tv.tv_usec = tv_tmp.tv_usec + ((MOH_MS_INTERVAL % 1000) * 1000 - error_usec);
464                                 delay = (MOH_MS_INTERVAL/1000 - error_sec) * 1000 +
465                                                         ((MOH_MS_INTERVAL % 1000) * 1000 - error_usec) / 1000;
466                         } else {
467                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
468                                 tv.tv_sec = tv_tmp.tv_sec;
469                                 tv.tv_usec = tv_tmp.tv_usec;
470                                 delay = 0;
471                         }
472                         if (tv.tv_usec > 1000000) {
473                                 tv.tv_sec++;
474                                 tv.tv_usec-= 1000000;
475                         }
476                         if (delay > 0)
477                                 usleep(delay * 1000);
478                         res = 800;              /* 800 samples */
479                 }
480                 if (!class->members)
481                         continue;
482                 /* Read mp3 audio */
483                 if ((res2 = read(class->srcfd, sbuf, res * 2)) != res * 2) {
484                         if (!res2) {
485                                 close(class->srcfd);
486                                 class->srcfd = -1;
487                                 if (class->pid) {
488                                         kill(class->pid, SIGKILL);
489                                         class->pid = 0;
490                                 }
491                         } else
492                                 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, res * 2);
493                         continue;
494                 }
495                 ast_mutex_lock(&moh_lock);
496                 moh = class->members;
497                 while(moh) {
498                         /* Write data */
499                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) 
500                                 if (option_debug)
501                                         ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
502                         moh = moh->next;
503                 }
504                 ast_mutex_unlock(&moh_lock);
505         }
506         return NULL;
507 }
508
509 static int moh0_exec(struct ast_channel *chan, void *data)
510 {
511         if (ast_moh_start(chan, data)) {
512                 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
513                 return -1;
514         }
515         while(!ast_safe_sleep(chan, 10000));
516         return -1;
517 }
518
519 static int moh1_exec(struct ast_channel *chan, void *data)
520 {
521         int res;
522         if (!data || !atoi(data)) {
523                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
524                 return -1;
525         }
526         if (ast_moh_start(chan, NULL)) {
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         res = ast_safe_sleep(chan, atoi(data) * 1000);
531         ast_moh_stop(chan);
532         return res;
533 }
534
535 static int moh2_exec(struct ast_channel *chan, void *data)
536 {
537         if (!data || ast_strlen_zero(data)) {
538                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
539                 return -1;
540         }
541         strncpy(chan->musicclass, data, sizeof(chan->musicclass) - 1);
542         return 0;
543 }
544
545 static struct mohclass *get_mohbyname(char *name)
546 {
547         struct mohclass *moh;
548         moh = mohclasses;
549         while(moh) {
550                 if (!strcasecmp(name, moh->class))
551                         return moh;
552                 moh = moh->next;
553         }
554         return NULL;
555 }
556
557 static struct mohdata *mohalloc(struct mohclass *cl)
558 {
559         struct mohdata *moh;
560         long flags;
561         moh = malloc(sizeof(struct mohdata));
562         if (!moh)
563                 return NULL;
564         memset(moh, 0, sizeof(struct mohdata));
565         if (pipe(moh->pipe)) {
566                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
567                 free(moh);
568                 return NULL;
569         }
570         /* Make entirely non-blocking */
571         flags = fcntl(moh->pipe[0], F_GETFL);
572         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
573         flags = fcntl(moh->pipe[1], F_GETFL);
574         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
575         moh->parent = cl;
576         moh->next = cl->members;
577         cl->members = moh;
578         return moh;
579 }
580
581 static void moh_release(struct ast_channel *chan, void *data)
582 {
583         struct mohdata *moh = data, *prev, *cur;
584         int oldwfmt;
585         ast_mutex_lock(&moh_lock);
586         /* Unlink */
587         prev = NULL;
588         cur = moh->parent->members;
589         while(cur) {
590                 if (cur == moh) {
591                         if (prev)
592                                 prev->next = cur->next;
593                         else
594                                 moh->parent->members = cur->next;
595                         break;
596                 }
597                 prev = cur;
598                 cur = cur->next;
599         }
600         ast_mutex_unlock(&moh_lock);
601         close(moh->pipe[0]);
602         close(moh->pipe[1]);
603         oldwfmt = moh->origwfmt;
604         free(moh);
605         if (chan) {
606                 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
607                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
608                 if (option_verbose > 2)
609                         ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
610         }
611 }
612
613 static void *moh_alloc(struct ast_channel *chan, void *params)
614 {
615         struct mohdata *res;
616         struct mohclass *class;
617         class = params;
618         if (class)
619                 res = mohalloc(class);
620         else {
621                 if (strcasecmp(params, "default"))
622                         ast_log(LOG_WARNING, "No class: %s\n", (char *)params);
623                 res = NULL;
624         }
625         if (res) {
626                 res->origwfmt = chan->writeformat;
627                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
628                         ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format\n", chan->name);
629                         moh_release(NULL, res);
630                         res = NULL;
631                 }
632                 if (option_verbose > 2)
633                         ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", (char *)params, chan->name);
634         }
635         return res;
636 }
637
638 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
639 {
640         struct ast_frame f;
641         struct mohdata *moh = data;
642         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
643         int res;
644         if(!moh->parent->pid)
645                 return - 1;
646         len = samples * 2;
647         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
648                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), (int)len, chan->name);
649                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
650         }
651         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
652 #if 0
653         if (res != len) {
654                 ast_log(LOG_WARNING, "Read only %d of %d bytes: %s\n", res, len, strerror(errno));
655         }
656 #endif
657         if (res > 0) {
658                 memset(&f, 0, sizeof(f));
659                 f.frametype = AST_FRAME_VOICE;
660                 f.subclass = AST_FORMAT_SLINEAR;
661                 f.mallocd = 0;
662                 f.datalen = res;
663                 f.samples = res / 2;
664                 f.data = buf + AST_FRIENDLY_OFFSET / 2;
665                 f.offset = AST_FRIENDLY_OFFSET;
666                 if (ast_write(chan, &f)< 0) {
667                         ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
668                         return -1;
669                 }
670         }
671         return 0;
672 }
673
674 static struct ast_generator mohgen = 
675 {
676         alloc: moh_alloc,
677         release: moh_release,
678         generate: moh_generate,
679 };
680
681 static int moh_scan_files(struct mohclass *class) {
682
683         DIR *files_DIR;
684         struct dirent *files_dirent;
685         char path[512];
686         char filepath[MAX_MOHFILE_LEN];
687         char *ext;
688         struct stat statbuf;
689         int dirnamelen;
690         int i;
691         
692         files_DIR = opendir(class->dir);
693         if (!files_DIR) {
694                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
695                 return -1;
696         }
697
698         class->total_files = 0;
699         dirnamelen = strlen(class->dir) + 2;
700         getcwd(path, 512);
701         chdir(class->dir);
702         memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
703         while ((files_dirent = readdir(files_DIR))) {
704                 if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
705                         continue;
706
707                 snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
708
709                 if (stat(filepath, &statbuf))
710                         continue;
711
712                 if (!S_ISREG(statbuf.st_mode))
713                         continue;
714
715                 if ((ext = strrchr(filepath, '.'))) {
716                         *ext = '\0';
717                         ext++;
718                 }
719
720                 /* check to see if this file's format can be opened */
721                 if (ast_fileexists(filepath, ext, NULL) == -1)
722                         continue;
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                         moh->randomize=1;
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                         moh->custom = 1;
777                 else if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb"))
778                         moh->single = 1;
779                 else if (!strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb"))
780                         moh->quiet = 1;
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         cfg = ast_load("musiconhold.conf");
868         if (cfg) {
869                 var = ast_variable_browse(cfg, "classes");
870                 while(var) {
871                         data = strchr(var->value, ':');
872                         if (data) {
873                                 *data = '\0';
874                                 data++;
875                                 args = strchr(data, ',');
876                                 if (args) {
877                                         *args = '\0';
878                                         args++;
879                                 }
880                                 if(!(get_mohbyname(var->name))) {
881                                         moh_register(var->name, var->value, data, args);
882                                         x++;
883                                 }
884                         }
885                         var = var->next;
886                 }
887                 var = ast_variable_browse(cfg, "moh_files");
888                 while(var) {
889                         if(!(get_mohbyname(var->name))) {
890                                 args = strchr(var->value, ',');
891                                 if (args) {
892                                         *args = '\0';
893                                         args++;
894                                 }
895                                 moh_register(var->name, "files", var->value, args);
896                                 x++;
897                         }
898                         var = var->next;
899                 }
900
901                 ast_destroy(cfg);
902         }
903         return x;
904 }
905
906 static void ast_moh_destroy(void)
907 {
908         struct mohclass *moh,*tmp;
909         char buff[8192];
910         int bytes, tbytes=0, stime = 0, pid = 0;
911         if (option_verbose > 1)
912                 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
913         ast_mutex_lock(&moh_lock);
914         moh = mohclasses;
915
916         while(moh) {
917                 if (moh->pid) {
918                         ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
919                         stime = time(NULL);
920                         pid = moh->pid;
921                         moh->pid = 0;
922                         kill(pid, SIGKILL);
923                         while ((bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime + 5) {
924                                 tbytes = tbytes + bytes;
925                         }
926                         ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
927                         close(moh->srcfd);
928                 }
929                 tmp = moh;
930                 moh = moh->next;
931                 free(tmp);
932         }
933         mohclasses = NULL;
934         ast_mutex_unlock(&moh_lock);
935 }
936
937
938 static void moh_on_off(int on) {
939         struct ast_channel *chan = ast_channel_walk_locked(NULL);
940         while(chan) {
941                 if(ast_test_flag(chan, AST_FLAG_MOH)) {
942                         if(on)
943                                 local_ast_moh_start(chan,NULL);
944                         else
945                                 ast_deactivate_generator(chan);
946                 }
947                 ast_mutex_unlock(&chan->lock);
948                 chan = ast_channel_walk_locked(chan);
949         }
950 }
951
952 static int moh_cli(int fd, int argc, char *argv[]) 
953 {
954         int x = 0;
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 int init_classes(void) {
987     struct mohclass *moh;
988     load_moh_classes();
989         moh = mohclasses;
990     while(moh) {
991         if (moh->total_files)
992             moh_scan_files(moh);
993         moh = moh->next;
994     }
995         return 0;
996 }
997
998 int load_module(void)
999 {
1000         int res;
1001         ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1002         res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1003         ast_register_atexit(ast_moh_destroy);
1004         ast_cli_register(&cli_moh);
1005         ast_cli_register(&cli_moh_files_show);
1006         if (!res)
1007                 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1008         if (!res)
1009                 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1010
1011         return init_classes();
1012 }
1013
1014 int reload(void)
1015 {
1016     return init_classes();
1017 }
1018
1019
1020 int unload_module(void)
1021 {
1022         return -1;
1023 }
1024
1025 char *description(void)
1026 {
1027         return "Music On Hold Resource";
1028 }
1029
1030 int usecount(void)
1031 {
1032         /* Never allow Music On Hold to be unloaded
1033            unresolve needed symbols in the dialer */
1034 #if 0
1035         int res;
1036         STANDARD_USECOUNT(res);
1037         return res;
1038 #else
1039         return 1;
1040 #endif
1041 }
1042
1043 char *key()
1044 {
1045         return ASTERISK_GPL_KEY;
1046 }