officially deprecate the 'roundrobin' queue strategy in favor of 'rrmemory'
[asterisk-bristuff.git] / muted.c
blob73eadfa3d5171169af2bb7d222aa7c9c6e40b603
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 #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>
55 static char *config = "/etc/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 chan->tech = strdup(tech);
89 chan->location = strdup(location);
90 chan->next = channels;
91 channels = chan;
96 static int load_config(void)
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;
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++;
125 if (*val) {
126 *val = '\0';
127 val++;
128 while(*val && (*val < 33)) val++;
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);
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;
180 static FILE *astf;
181 #ifndef __Darwin__
182 static int mixfd;
184 static int open_mixer(void)
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;
191 return 0;
193 #endif /* !__Darwin */
195 /*! Connect to the asterisk manager interface */
196 static int connect_asterisk(void)
198 int sock;
199 struct hostent *hp;
200 char *ports;
201 int port = 5038;
202 struct sockaddr_in sin;
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;
213 hp = gethostbyname(host);
214 if (!hp) {
215 fprintf(stderr, "Can't find host '%s'\n", host);
216 return -1;
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;
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;
231 astf = fdopen(sock, "r+");
232 if (!astf) {
233 fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
234 close(sock);
235 return -1;
237 return 0;
240 static char *get_line(void)
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;
251 /*! Login to the asterisk manager interface */
252 static int login_asterisk(void)
254 char *welcome;
255 char *resp;
256 if (!(welcome = get_line())) {
257 fprintf(stderr, "disconnected (1)\n");
258 return -1;
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;
268 if (strcasecmp(welcome, "Response: Success")) {
269 fprintf(stderr, "login failed ('%s')\n", welcome);
270 return -1;
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;
278 fprintf(astf,
279 "Action: Status\r\n\r\n");
280 if (!(welcome = get_line())) {
281 fprintf(stderr, "disconnected (4)\n");
282 return -1;
284 if (strcasecmp(welcome, "Response: Success")) {
285 fprintf(stderr, "status failed ('%s')\n", welcome);
286 return -1;
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;
294 return 0;
297 static struct channel *find_channel(char *channel)
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';
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;
320 chan = chan->next;
322 } else
323 chan = NULL;
324 return chan;
327 #ifndef __Darwin__
328 static int getvol(void)
330 int vol;
332 if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
333 #else
334 static float getvol(void)
336 float volumeL, volumeR, vol;
337 OSStatus err;
338 AudioDeviceID device;
339 UInt32 size;
340 UInt32 channels[2];
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;
359 return vol;
362 #ifndef __Darwin__
363 static int setvol(int vol)
364 #else
365 static int setvol(float vol)
366 #endif
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];
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
390 fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
391 return -1;
394 return 0;
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
405 #ifndef __Darwin__
406 static int mutedlevel(int orig, int mutelevel)
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;
413 return (l << 8) | r;
414 #else
415 static float mutedlevel(float orig, float mutelevel)
417 float master = orig;
418 master = mutelevel * master / 100.0;
419 return master;
420 #endif
424 static void mute(void)
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);
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;
462 static void unmute(void)
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);
497 setvol(oldvol);
498 } else
499 printf("Whoops, it's already been changed!\n");
500 muted = 0;
503 static void check_mute(void)
505 int offhook = 0;
506 struct channel *chan;
507 chan = channels;
508 while(chan) {
509 if (chan->subs) {
510 offhook++;
511 break;
513 chan = chan->next;
515 if (offhook && !muted)
516 mute();
517 else if (!offhook && muted)
518 unmute();
521 static void delete_sub(struct channel *chan, char *name)
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;
536 prev = sub;
537 sub = sub->next;
541 static void append_sub(struct channel *chan, char *name)
543 struct subchannel *sub;
544 sub = chan->subs;
545 while(sub) {
546 if (!strcasecmp(sub->name, name))
547 return;
548 sub = sub->next;
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;
559 static void hangup_chan(char *channel)
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();
570 static void offhook_chan(char *channel)
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();
581 static int wait_event(void)
583 char *resp;
584 char event[120]="";
585 char channel[120]="";
586 char oldname[120]="";
587 char newname[120]="";
589 resp = get_line();
590 if (!resp) {
591 fprintf(stderr, "disconnected (6)\n");
592 return -1;
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);
605 if (strlen(channel)) {
606 if (!strcasecmp(event, "Hangup"))
607 hangup_chan(channel);
608 else
609 offhook_chan(channel);
611 if (strlen(newname) && strlen(oldname)) {
612 if (!strcasecmp(event, "Rename")) {
613 hangup_chan(oldname);
614 offhook_chan(newname);
617 } else {
618 /* Consume the rest of the non-event */
619 while((resp = get_line()) && strlen(resp));
621 if (!resp) {
622 fprintf(stderr, "disconnected (7)\n");
623 return -1;
625 return 0;
628 static void usage(void)
630 printf("Usage: muted [-f] [-d]\n"
631 " -f : Do not fork\n"
632 " -d : Debug (implies -f)\n");
635 int main(int argc, char *argv[])
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);
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);
666 if (login_asterisk()) {
667 #ifndef __Darwin__
668 close(mixfd);
669 #endif
670 fclose(astf);
671 exit(1);
673 if (needfork)
674 daemon(0,0);
675 for(;;) {
676 if (wait_event()) {
677 fclose(astf);
678 while(connect_asterisk()) {
679 sleep(5);
681 if (login_asterisk()) {
682 fclose(astf);
683 exit(1);
687 exit(0);