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