make app_queue 1.2 jump compliant (issue #5580)
[asterisk/asterisk.git] / muted.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Updated for Mac OSX CoreAudio 
9  * by Josh Roberson <josh@asteriasgi.com>
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  * \brief Mute Daemon
24  *
25  * Specially written for Malcolm Davenport, but I think I'll use it too
26  *
27  */
28
29 #ifndef __Darwin__
30 #include <linux/soundcard.h>
31 #else
32 #include <CoreAudio/AudioHardware.h> 
33 #endif
34 #include <stdio.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <netdb.h>
41 #include <sys/socket.h>
42 #include <sys/ioctl.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45
46 static char *config = "/etc/muted.conf";
47
48 static char host[256] = "";
49 static char user[256] = "";
50 static char pass[256] = "";
51 static int smoothfade = 0;
52 static int mutelevel = 20;
53 static int muted = 0;
54 static int needfork = 1;
55 static int debug = 0;
56 static int stepsize = 3;
57 #ifndef __Darwin__
58 static int mixchan = SOUND_MIXER_VOLUME;
59 #endif
60
61 struct subchannel {
62         char *name;
63         struct subchannel *next;
64 };
65
66 static struct channel {
67         char *tech;
68         char *location;
69         struct channel *next;
70         struct subchannel *subs;
71 } *channels;
72
73 static void add_channel(char *tech, char *location)
74 {
75         struct channel *chan;
76         chan = malloc(sizeof(struct channel));
77         if (chan) {
78                 memset(chan, 0, sizeof(struct channel));
79                 chan->tech = strdup(tech);
80                 chan->location = strdup(location);
81                 chan->next = channels;
82                 channels = chan;
83         }
84         
85 }
86
87 static int load_config(void)
88 {
89         FILE *f;
90         char buf[1024];
91         char *val;
92         char *val2;
93         int lineno=0;
94         int x;
95         f = fopen(config, "r");
96         if (!f) {
97                 fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno));
98                 return -1;
99         }
100         while(!feof(f)) {
101                 fgets(buf, sizeof(buf), f);
102                 if (!feof(f)) {
103                         lineno++;
104                         val = strchr(buf, '#');
105                         if (val) *val = '\0';
106                         while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
107                                 buf[strlen(buf) - 1] = '\0';
108                         if (!strlen(buf))
109                                 continue;
110                         val = buf;
111                         while(*val) {
112                                 if (*val < 33)
113                                         break;
114                                 val++;
115                         }
116                         if (*val) {
117                                 *val = '\0';
118                                 val++;
119                                 while(*val && (*val < 33)) val++;
120                         }
121                         if (!strcasecmp(buf, "host")) {
122                                 if (val && strlen(val))
123                                         strncpy(host, val, sizeof(host) - 1);
124                                 else
125                                         fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno);
126                         } else if (!strcasecmp(buf, "user")) {
127                                 if (val && strlen(val))
128                                         strncpy(user, val, sizeof(user) - 1);
129                                 else
130                                         fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno);
131                         } else if (!strcasecmp(buf, "pass")) {
132                                 if (val && strlen(val))
133                                         strncpy(pass, val, sizeof(pass) - 1);
134                                 else
135                                         fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno);
136                         } else if (!strcasecmp(buf, "smoothfade")) {
137                                 smoothfade = 1;
138                         } else if (!strcasecmp(buf, "mutelevel")) {
139                                 if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) {
140                                         mutelevel = x;
141                                 } else 
142                                         fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
143                         } else if (!strcasecmp(buf, "channel")) {
144                                 if (val && strlen(val)) {
145                                         val2 = strchr(val, '/');
146                                         if (val2) {
147                                                 *val2 = '\0';
148                                                 val2++;
149                                                 add_channel(val, val2);
150                                         } else
151                                                 fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno);
152                                 } else
153                                         fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno);
154                         } else {
155                                 fprintf(stderr, "ignoring unknown keyword '%s'\n", buf);
156                         }
157                 }
158         }
159         fclose(f);
160         if (!strlen(host))
161                 fprintf(stderr, "no 'host' specification in config file\n");
162         else if (!strlen(user))
163                 fprintf(stderr, "no 'user' specification in config file\n");
164         else if (!channels) 
165                 fprintf(stderr, "no 'channel' specifications in config file\n");
166         else
167                 return 0;
168         return -1;
169 }
170
171 static FILE *astf;
172 #ifndef __Darwin__
173 static int mixfd;
174
175 static int open_mixer(void)
176 {
177         mixfd = open("/dev/mixer", O_RDWR);
178         if (mixfd < 0) {
179                 fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno));
180                 return -1;
181         }
182         return 0;
183 }
184 #endif /* !__Darwin */
185
186 static int connect_asterisk(void)
187 {
188         int sock;
189         struct hostent *hp;
190         char *ports;
191         int port = 5038;
192         struct sockaddr_in sin;
193         ports = strchr(host, ':');
194         if (ports) {
195                 *ports = '\0';
196                 ports++;
197                 if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) {
198                         fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports);
199                         return -1;
200                 }
201         }
202         hp = gethostbyname(host);
203         if (!hp) {
204                 fprintf(stderr, "Can't find host '%s'\n", host);
205                 return -1;
206         }
207         sock = socket(AF_INET, SOCK_STREAM, 0);
208         if (sock < 0) {
209                 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
210                 return -1;
211         }
212         sin.sin_family = AF_INET;
213         sin.sin_port = htons(port);
214         memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
215         if (connect(sock, &sin, sizeof(sin))) {
216                 fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno));
217                 close(sock);
218                 return -1;
219         }
220         astf = fdopen(sock, "r+");
221         if (!astf) {
222                 fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
223                 close(sock);
224                 return -1;
225         }
226         return 0;
227 }
228
229 static char *get_line(void)
230 {
231         static char buf[1024];
232         if (fgets(buf, sizeof(buf), astf)) {
233                 while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
234                         buf[strlen(buf) - 1] = '\0';
235                 return buf;
236         } else
237                 return NULL;
238 }
239
240 static int login_asterisk(void)
241 {
242         char *welcome;
243         char *resp;
244         if (!(welcome = get_line())) {
245                 fprintf(stderr, "disconnected (1)\n");
246                 return -1;
247         }
248         fprintf(astf, 
249                 "Action: Login\r\n"
250                 "Username: %s\r\n"
251                 "Secret: %s\r\n\r\n", user, pass);
252         if (!(welcome = get_line())) {
253                 fprintf(stderr, "disconnected (2)\n");
254                 return -1;
255         }
256         if (strcasecmp(welcome, "Response: Success")) {
257                 fprintf(stderr, "login failed ('%s')\n", welcome);
258                 return -1;
259         }
260         /* Eat the rest of the event */
261         while((resp = get_line()) && strlen(resp));
262         if (!resp) {
263                 fprintf(stderr, "disconnected (3)\n");
264                 return -1;
265         }
266         fprintf(astf, 
267                 "Action: Status\r\n\r\n");
268         if (!(welcome = get_line())) {
269                 fprintf(stderr, "disconnected (4)\n");
270                 return -1;
271         }
272         if (strcasecmp(welcome, "Response: Success")) {
273                 fprintf(stderr, "status failed ('%s')\n", welcome);
274                 return -1;
275         }
276         /* Eat the rest of the event */
277         while((resp = get_line()) && strlen(resp));
278         if (!resp) {
279                 fprintf(stderr, "disconnected (5)\n");
280                 return -1;
281         }
282         return 0;
283 }
284
285 static struct channel *find_channel(char *channel)
286 {
287         char tmp[256] = "";
288         char *s, *t;
289         struct channel *chan;
290         strncpy(tmp, channel, sizeof(tmp) - 1);
291         s = strchr(tmp, '/');
292         if (s) {
293                 *s = '\0';
294                 s++;
295                 t = strrchr(s, '-');
296                 if (t) {
297                         *t = '\0';
298                 }
299                 if (debug)
300                         printf("Searching for '%s' tech, '%s' location\n", tmp, s);
301                 chan = channels;
302                 while(chan) {
303                         if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) {
304                                 if (debug)
305                                         printf("Found '%s'/'%s'\n", chan->tech, chan->location);
306                                 break;
307                         }
308                         chan = chan->next;
309                 }
310         } else
311                 chan = NULL;
312         return chan;
313 }
314
315 #ifndef __Darwin__
316 static int getvol(void)
317 {
318         int vol;
319
320         if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
321 #else
322 static float getvol(void)
323 {
324         float volumeL, volumeR, vol;
325         OSStatus err;
326         AudioDeviceID device;
327         UInt32 size;
328         UInt32 channels[2];
329
330         size = sizeof(device);
331         err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
332         size = sizeof(channels);
333         if (!err) 
334                 err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
335         size = sizeof(vol);
336         if (!err)
337                 err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL);
338         if (!err)
339                 err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR);
340         if (!err)
341                 vol = (volumeL < volumeR) ? volumeR : volumeL;
342         else {
343 #endif
344                 fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno));
345                 return -1;
346         }
347         return vol;
348 }
349
350 #ifndef __Darwin__
351 static int setvol(int vol)
352 #else
353 static int setvol(float vol)
354 #endif
355 {
356 #ifndef __Darwin__
357         if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
358 #else   
359         float volumeL = vol;
360         float volumeR = vol;
361         OSStatus err;
362         AudioDeviceID device;
363         UInt32 size;
364         UInt32 channels[2];
365
366         size = sizeof(device);
367         err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
368         size = sizeof(channels);
369         err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
370         size = sizeof(vol);
371         if (!err)
372                 err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL);
373         if (!err)
374                 err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR); 
375         if (err) {
376 #endif
377
378                 fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
379                 return -1;
380
381         }
382         return 0;
383 }
384
385 #ifndef __Darwin__
386 static int oldvol = 0;
387 static int mutevol = 0;
388 #else
389 static float oldvol = 0;
390 static float mutevol = 0;
391 #endif
392
393 #ifndef __Darwin__
394 static int mutedlevel(int orig, int mutelevel)
395 {
396         int l = orig >> 8;
397         int r = orig & 0xff;
398         l = (float)(mutelevel) * (float)(l) / 100.0;
399         r = (float)(mutelevel) * (float)(r) / 100.0;
400
401         return (l << 8) | r;
402 #else
403 static float mutedlevel(float orig, float mutelevel)
404 {
405         float master = orig;
406         master = mutelevel * master / 100.0;
407         return master;
408 #endif
409         
410 }
411
412 static void mute(void)
413 {
414 #ifndef __Darwin__
415         int vol;
416         int start;
417         int x;
418 #else
419         float vol;
420         float start = 1.0;
421         float x;
422 #endif
423         vol = getvol();
424         oldvol = vol;
425         if (smoothfade)
426 #ifdef __Darwin__ 
427                 start = mutelevel;
428 #else
429                 start = 100;
430         else
431                 start = mutelevel;
432 #endif
433         for (x=start;x>=mutelevel;x-=stepsize) {
434                 mutevol = mutedlevel(vol, x);
435                 setvol(mutevol);
436                 /* Wait 0.01 sec */
437                 usleep(10000);
438         }
439         mutevol = mutedlevel(vol, mutelevel);
440         setvol(mutevol);
441         if (debug)
442 #ifdef __Darwin__
443                 printf("Mute from '%f' to '%f'!\n", oldvol, mutevol);
444 #else
445                 printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol);
446 #endif
447         muted = 1;
448 }
449
450 static void unmute(void)
451 {
452 #ifdef __Darwin__
453         float vol;
454         float start;
455         float x;
456 #else
457         int vol;
458         int start;
459         int x;
460 #endif
461         vol = getvol();
462         if (debug)
463 #ifdef __Darwin__
464                 printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol);
465         mutevol = vol;
466         if (vol == mutevol) {
467 #else
468                 printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol);
469         if ((int)vol == mutevol) {
470 #endif
471                 if (smoothfade)
472                         start = mutelevel;
473                 else
474 #ifdef __Darwin__
475                         start = 1.0;
476 #else
477                         start = 100;
478 #endif
479                 for (x=start;x<100;x+=stepsize) {
480                         mutevol = mutedlevel(oldvol, x);
481                         setvol(mutevol);
482                         /* Wait 0.01 sec */
483                         usleep(10000);
484                 }
485                 setvol(oldvol);
486         } else
487                 printf("Whoops, it's already been changed!\n");
488         muted = 0;
489 }
490
491 static void check_mute(void)
492 {
493         int offhook = 0;
494         struct channel *chan;
495         chan = channels;
496         while(chan) {
497                 if (chan->subs) {
498                         offhook++;
499                         break;
500                 }
501                 chan = chan->next;
502         }
503         if (offhook && !muted)
504                 mute();
505         else if (!offhook && muted)
506                 unmute();
507 }
508
509 static void delete_sub(struct channel *chan, char *name)
510 {
511         struct subchannel *sub, *prev;
512         prev = NULL;
513         sub = chan->subs;
514         while(sub) {
515                 if (!strcasecmp(sub->name, name)) {
516                         if (prev)
517                                 prev->next = sub->next;
518                         else
519                                 chan->subs = sub->next;
520                         free(sub->name);
521                         free(sub);
522                         return;
523                 }
524                 prev = sub;
525                 sub = sub->next;
526         }
527 }
528
529 static void append_sub(struct channel *chan, char *name)
530 {
531         struct subchannel *sub;
532         sub = chan->subs;
533         while(sub) {
534                 if (!strcasecmp(sub->name, name)) 
535                         return;
536                 sub = sub->next;
537         }
538         sub = malloc(sizeof(struct subchannel));
539         if (sub) {
540                 memset(sub, 0, sizeof(struct subchannel));
541                 sub->name = strdup(name);
542                 sub->next = chan->subs;
543                 chan->subs = sub;
544         }
545 }
546
547 static void hangup_chan(char *channel)
548 {
549         struct channel *chan;
550         if (debug)
551                 printf("Hangup '%s'\n", channel);
552         chan = find_channel(channel);
553         if (chan)
554                 delete_sub(chan, channel);
555         check_mute();
556 }
557
558 static void offhook_chan(char *channel)
559 {
560         struct channel *chan;
561         if (debug)
562                 printf("Offhook '%s'\n", channel);
563         chan = find_channel(channel);
564         if (chan)
565                 append_sub(chan, channel);
566         check_mute();
567 }
568
569 static int wait_event(void)
570 {
571         char *resp;
572         char event[120]="";
573         char channel[120]="";
574         char oldname[120]="";
575         char newname[120]="";
576         resp = get_line();
577         if (!resp) {
578                 fprintf(stderr, "disconnected (6)\n");
579                 return -1;
580         }
581         if (!strncasecmp(resp, "Event: ", strlen("Event: "))) {
582                 strncpy(event, resp + strlen("Event: "), sizeof(event) - 1);
583                 /* Consume the rest of the non-event */
584                 while((resp = get_line()) && strlen(resp)) {
585                         if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
586                                 strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
587                         if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
588                                 strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
589                         if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
590                                 strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
591                 }
592                 if (strlen(channel)) {
593                         if (!strcasecmp(event, "Hangup")) 
594                                 hangup_chan(channel);
595                         else
596                                 offhook_chan(channel);
597                 }
598                 if (strlen(newname) && strlen(oldname)) {
599                         if (!strcasecmp(event, "Rename")) {
600                                 hangup_chan(oldname);
601                                 offhook_chan(newname);
602                         }
603                 }
604         } else {
605                 /* Consume the rest of the non-event */
606                 while((resp = get_line()) && strlen(resp));
607         }
608         if (!resp) {
609                 fprintf(stderr, "disconnected (7)\n");
610                 return -1;
611         }
612         return 0;
613 }
614
615 static void usage(void)
616 {
617         printf("Usage: muted [-f] [-d]\n"
618                "        -f : Do not fork\n"
619                    "        -d : Debug (implies -f)\n");
620 }
621
622 int main(int argc, char *argv[])
623 {
624         int x;
625         while((x = getopt(argc, argv, "fhd")) > 0) {
626                 switch(x) {
627                 case 'd':
628                         debug = 1;
629                         needfork = 0;
630                         break;
631                 case 'f':
632                         needfork = 0;
633                         break;
634                 case 'h':
635                         /* Fall through */
636                 default:
637                         usage();
638                         exit(1);
639                 }
640         }
641         if (load_config())
642                 exit(1);
643 #ifndef __Darwin__
644         if (open_mixer())
645                 exit(1);
646 #endif
647         if (connect_asterisk()) {
648 #ifndef __Darwin__
649                 close(mixfd);
650 #endif
651                 exit(1);
652         }
653         if (login_asterisk()) {
654 #ifndef __Darwin__              
655                 close(mixfd);
656 #endif
657                 fclose(astf);
658                 exit(1);
659         }
660         if (needfork)
661                 daemon(0,0);
662         for(;;) {
663                 if (wait_event()) {
664                         fclose(astf);
665                         while(connect_asterisk()) {
666                                 sleep(5);
667                         }
668                         if (login_asterisk()) {
669                                 fclose(astf);
670                                 exit(1);
671                         }
672                 }
673         }
674         exit(0);
675 }