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