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