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