Add some other files to ignore
[asterisk-bristuff.git] / utils / muted.c
blobc406258e18e3f889ed0ff90a160e96fde50254da
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * Updated for Mac OSX CoreAudio
9 * by Josh Roberson <josh@asteriasgi.com>
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.
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.
22 /*! \file
24 * \brief Mute Daemon
26 * \author Mark Spencer <markster@digium.com>
28 * Updated for Mac OSX CoreAudio
29 * \arg Josh Roberson <josh@asteriasgi.com>
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.
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>
55 static char *config = "/etc/asterisk/muted.conf";
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
70 struct subchannel {
71 char *name;
72 struct subchannel *next;
75 static struct channel {
76 char *tech;
77 char *location;
78 struct channel *next;
79 struct subchannel *subs;
80 } *channels;
82 static void add_channel(char *tech, char *location)
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;
92 if (!(chan->location = strdup(location))) {
93 free(chan->tech);
94 free(chan);
95 return;
97 chan->next = channels;
98 channels = chan;
103 static int load_config(void)
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;
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++;
132 if (*val) {
133 *val = '\0';
134 val++;
135 while(*val && (*val < 33)) val++;
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);
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;
187 static FILE *astf;
188 #ifndef __Darwin__
189 static int mixfd;
191 static int open_mixer(void)
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;
198 return 0;
200 #endif /* !__Darwin */
202 /*! Connect to the asterisk manager interface */
203 static int connect_asterisk(void)
205 int sock;
206 struct hostent *hp;
207 char *ports;
208 int port = 5038;
209 struct sockaddr_in sin;
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;
220 hp = gethostbyname(host);
221 if (!hp) {
222 fprintf(stderr, "Can't find host '%s'\n", host);
223 return -1;
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;
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;
238 astf = fdopen(sock, "r+");
239 if (!astf) {
240 fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
241 close(sock);
242 return -1;
244 return 0;
247 static char *get_line(void)
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;
258 /*! Login to the asterisk manager interface */
259 static int login_asterisk(void)
261 char *welcome;
262 char *resp;
263 if (!(welcome = get_line())) {
264 fprintf(stderr, "disconnected (1)\n");
265 return -1;
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;
275 if (strcasecmp(welcome, "Response: Success")) {
276 fprintf(stderr, "login failed ('%s')\n", welcome);
277 return -1;
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;
285 fprintf(astf,
286 "Action: Status\r\n\r\n");
287 if (!(welcome = get_line())) {
288 fprintf(stderr, "disconnected (4)\n");
289 return -1;
291 if (strcasecmp(welcome, "Response: Success")) {
292 fprintf(stderr, "status failed ('%s')\n", welcome);
293 return -1;
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;
301 return 0;
304 static struct channel *find_channel(char *channel)
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';
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;
327 chan = chan->next;
329 } else
330 chan = NULL;
331 return chan;
334 #ifndef __Darwin__
335 static int getvol(void)
337 int vol;
339 if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
340 #else
341 static float getvol(void)
343 float volumeL, volumeR, vol;
344 OSStatus err;
345 AudioDeviceID device;
346 UInt32 size;
347 UInt32 channels[2];
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;
366 return vol;
369 #ifndef __Darwin__
370 static int setvol(int vol)
371 #else
372 static int setvol(float vol)
373 #endif
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];
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
397 fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
398 return -1;
401 return 0;
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
412 #ifndef __Darwin__
413 static int mutedlevel(int orig, int mutelevel)
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;
420 return (l << 8) | r;
421 #else
422 static float mutedlevel(float orig, float mutelevel)
424 float master = orig;
425 master = mutelevel * master / 100.0;
426 return master;
427 #endif
431 static void mute(void)
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);
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;
469 static void unmute(void)
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);
504 setvol(oldvol);
505 } else
506 printf("Whoops, it's already been changed!\n");
507 muted = 0;
510 static void check_mute(void)
512 int offhook = 0;
513 struct channel *chan;
514 chan = channels;
515 while(chan) {
516 if (chan->subs) {
517 offhook++;
518 break;
520 chan = chan->next;
522 if (offhook && !muted)
523 mute();
524 else if (!offhook && muted)
525 unmute();
528 static void delete_sub(struct channel *chan, char *name)
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;
543 prev = sub;
544 sub = sub->next;
548 static void append_sub(struct channel *chan, char *name)
550 struct subchannel *sub;
551 sub = chan->subs;
552 while(sub) {
553 if (!strcasecmp(sub->name, name))
554 return;
555 sub = sub->next;
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;
564 sub->next = chan->subs;
565 chan->subs = sub;
569 static void hangup_chan(char *channel)
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();
580 static void offhook_chan(char *channel)
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();
591 static int wait_event(void)
593 char *resp;
594 char event[120]="";
595 char channel[120]="";
596 char oldname[120]="";
597 char newname[120]="";
599 resp = get_line();
600 if (!resp) {
601 fprintf(stderr, "disconnected (6)\n");
602 return -1;
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);
615 if (strlen(channel)) {
616 if (!strcasecmp(event, "Hangup"))
617 hangup_chan(channel);
618 else
619 offhook_chan(channel);
621 if (strlen(newname) && strlen(oldname)) {
622 if (!strcasecmp(event, "Rename")) {
623 hangup_chan(oldname);
624 offhook_chan(newname);
627 } else {
628 /* Consume the rest of the non-event */
629 while((resp = get_line()) && strlen(resp));
631 if (!resp) {
632 fprintf(stderr, "disconnected (7)\n");
633 return -1;
635 return 0;
638 static void usage(void)
640 printf("Usage: muted [-f] [-d]\n"
641 " -f : Do not fork\n"
642 " -d : Debug (implies -f)\n");
645 int main(int argc, char *argv[])
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);
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);
676 if (login_asterisk()) {
677 #ifndef __Darwin__
678 close(mixfd);
679 #endif
680 fclose(astf);
681 exit(1);
683 if (needfork) {
684 #ifndef HAVE_SBIN_LAUNCHD
685 daemon(0,0);
686 #else
687 fprintf(stderr, "Mac OS X detected. Use 'launchd -d muted -f' to launch.\n");
688 exit(1);
689 #endif
691 for(;;) {
692 if (wait_event()) {
693 fclose(astf);
694 while(connect_asterisk()) {
695 sleep(5);
697 if (login_asterisk()) {
698 fclose(astf);
699 exit(1);
703 exit(0);