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