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