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