Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

muted.c

Go to the documentation of this file.
00001 /* 00002 * Mute Daemon 00003 * 00004 * Specially written for Malcolm Davenport, but I think I'll use it too 00005 * 00006 * Copyright (C) 2004, Digium Inc. 00007 * 00008 * Mark Spencer <markster@digium.com> 00009 * 00010 * Distributed under the terms of the GNU General Public License version 2.0 00011 * 00012 */ 00013 #include <linux/soundcard.h> 00014 #include <stdio.h> 00015 #include <errno.h> 00016 #include <stdlib.h> 00017 #include <unistd.h> 00018 #include <fcntl.h> 00019 #include <string.h> 00020 #include <netdb.h> 00021 #include <sys/socket.h> 00022 #include <sys/ioctl.h> 00023 #include <netinet/in.h> 00024 #include <arpa/inet.h> 00025 00026 static char *config = "/etc/muted.conf"; 00027 00028 static char host[256]; 00029 static char user[256]; 00030 static char pass[256]; 00031 static int smoothfade = 0; 00032 static int mutelevel = 20; 00033 static int muted = 0; 00034 static int needfork = 1; 00035 static int debug = 0; 00036 static int stepsize = 3; 00037 static int mixchan = SOUND_MIXER_VOLUME; 00038 00039 struct subchannel { 00040 char *name; 00041 struct subchannel *next; 00042 }; 00043 00044 static struct channel { 00045 char *tech; 00046 char *location; 00047 struct channel *next; 00048 struct subchannel *subs; 00049 } *channels; 00050 00051 static void add_channel(char *tech, char *location) 00052 { 00053 struct channel *chan; 00054 chan = malloc(sizeof(struct channel)); 00055 if (chan) { 00056 memset(chan, 0, sizeof(struct channel)); 00057 chan->tech = strdup(tech); 00058 chan->location = strdup(location); 00059 chan->next = channels; 00060 channels = chan; 00061 } 00062 00063 } 00064 00065 static int load_config(void) 00066 { 00067 FILE *f; 00068 char buf[1024]; 00069 char *val; 00070 char *val2; 00071 int lineno=0; 00072 int x; 00073 f = fopen(config, "r"); 00074 if (!f) { 00075 fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno)); 00076 return -1; 00077 } 00078 while(!feof(f)) { 00079 fgets(buf, sizeof(buf), f); 00080 if (!feof(f)) { 00081 lineno++; 00082 val = strchr(buf, '#'); 00083 if (val) *val = '\0'; 00084 while(strlen(buf) && (buf[strlen(buf) - 1] < 33)) 00085 buf[strlen(buf) - 1] = '\0'; 00086 if (!strlen(buf)) 00087 continue; 00088 val = buf; 00089 while(*val) { 00090 if (*val < 33) 00091 break; 00092 val++; 00093 } 00094 if (*val) { 00095 *val = '\0'; 00096 val++; 00097 while(*val && (*val < 33)) val++; 00098 } 00099 if (!strcasecmp(buf, "host")) { 00100 if (val && strlen(val)) 00101 strncpy(host, val, sizeof(host)); 00102 else 00103 fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno); 00104 } else if (!strcasecmp(buf, "user")) { 00105 if (val && strlen(val)) 00106 strncpy(user, val, sizeof(user)); 00107 else 00108 fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno); 00109 } else if (!strcasecmp(buf, "pass")) { 00110 if (val && strlen(val)) 00111 strncpy(pass, val, sizeof(pass)); 00112 else 00113 fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno); 00114 } else if (!strcasecmp(buf, "smoothfade")) { 00115 smoothfade = 1; 00116 } else if (!strcasecmp(buf, "mutelevel")) { 00117 if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) { 00118 mutelevel = x; 00119 } else 00120 fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno); 00121 } else if (!strcasecmp(buf, "channel")) { 00122 if (val && strlen(val)) { 00123 val2 = strchr(val, '/'); 00124 if (val2) { 00125 *val2 = '\0'; 00126 val2++; 00127 add_channel(val, val2); 00128 } else 00129 fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno); 00130 } else 00131 fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno); 00132 } else { 00133 fprintf(stderr, "ignoring unknown keyword '%s'\n", buf); 00134 } 00135 } 00136 } 00137 fclose(f); 00138 if (!strlen(host)) 00139 fprintf(stderr, "no 'host' specification in config file\n"); 00140 else if (!strlen(user)) 00141 fprintf(stderr, "no 'user' specification in config file\n"); 00142 else if (!channels) 00143 fprintf(stderr, "no 'channel' specifications in config file\n"); 00144 else 00145 return 0; 00146 return -1; 00147 } 00148 00149 static FILE *astf; 00150 00151 static int mixfd; 00152 00153 static int open_mixer(void) 00154 { 00155 mixfd = open("/dev/mixer", O_RDWR); 00156 if (mixfd < 0) { 00157 fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno)); 00158 return -1; 00159 } 00160 return 0; 00161 } 00162 00163 static int connect_asterisk(void) 00164 { 00165 int sock; 00166 struct hostent *hp; 00167 char *ports; 00168 int port = 5038; 00169 struct sockaddr_in sin; 00170 ports = strchr(host, ':'); 00171 if (ports) { 00172 *ports = '\0'; 00173 ports++; 00174 if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) { 00175 fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports); 00176 return -1; 00177 } 00178 } 00179 hp = gethostbyname(host); 00180 if (!hp) { 00181 fprintf(stderr, "Can't find host '%s'\n", host); 00182 return -1; 00183 } 00184 sock = socket(AF_INET, SOCK_STREAM, 0); 00185 if (sock < 0) { 00186 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno)); 00187 return -1; 00188 } 00189 sin.sin_family = AF_INET; 00190 sin.sin_port = htons(port); 00191 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); 00192 if (connect(sock, &sin, sizeof(sin))) { 00193 fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno)); 00194 close(sock); 00195 return -1; 00196 } 00197 astf = fdopen(sock, "r+"); 00198 if (!astf) { 00199 fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); 00200 close(sock); 00201 return -1; 00202 } 00203 return 0; 00204 } 00205 00206 static char *get_line(void) 00207 { 00208 static char buf[1024]; 00209 if (fgets(buf, sizeof(buf), astf)) { 00210 while(strlen(buf) && (buf[strlen(buf) - 1] < 33)) 00211 buf[strlen(buf) - 1] = '\0'; 00212 return buf; 00213 } else 00214 return NULL; 00215 } 00216 00217 static int login_asterisk(void) 00218 { 00219 char *welcome; 00220 char *resp; 00221 if (!(welcome = get_line())) { 00222 fprintf(stderr, "disconnected (1)\n"); 00223 return -1; 00224 } 00225 fprintf(astf, 00226 "Action: Login\r\n" 00227 "Username: %s\r\n" 00228 "Secret: %s\r\n\r\n", user, pass); 00229 if (!(welcome = get_line())) { 00230 fprintf(stderr, "disconnected (2)\n"); 00231 return -1; 00232 } 00233 if (strcasecmp(welcome, "Response: Success")) { 00234 fprintf(stderr, "login failed ('%s')\n", welcome); 00235 return -1; 00236 } 00237 /* Eat the rest of the event */ 00238 while((resp = get_line()) && strlen(resp)); 00239 if (!resp) { 00240 fprintf(stderr, "disconnected (3)\n"); 00241 return -1; 00242 } 00243 fprintf(astf, 00244 "Action: Status\r\n\r\n"); 00245 if (!(welcome = get_line())) { 00246 fprintf(stderr, "disconnected (4)\n"); 00247 return -1; 00248 } 00249 if (strcasecmp(welcome, "Response: Success")) { 00250 fprintf(stderr, "status failed ('%s')\n", welcome); 00251 return -1; 00252 } 00253 /* Eat the rest of the event */ 00254 while((resp = get_line()) && strlen(resp)); 00255 if (!resp) { 00256 fprintf(stderr, "disconnected (5)\n"); 00257 return -1; 00258 } 00259 return 0; 00260 } 00261 00262 static struct channel *find_channel(char *channel) 00263 { 00264 char tmp[256] = ""; 00265 char *s, *t; 00266 struct channel *chan; 00267 strncpy(tmp, channel, sizeof(tmp)); 00268 s = strchr(tmp, '/'); 00269 if (s) { 00270 *s = '\0'; 00271 s++; 00272 t = strrchr(s, '-'); 00273 if (t) { 00274 *t = '\0'; 00275 } 00276 if (debug) 00277 printf("Searching for '%s' tech, '%s' location\n", tmp, s); 00278 chan = channels; 00279 while(chan) { 00280 if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) { 00281 if (debug) 00282 printf("Found '%s'/'%s'\n", chan->tech, chan->location); 00283 break; 00284 } 00285 chan = chan->next; 00286 } 00287 } else 00288 chan = NULL; 00289 return chan; 00290 } 00291 00292 static int getvol(void) 00293 { 00294 int vol; 00295 if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) { 00296 fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno)); 00297 return -1; 00298 } 00299 return vol; 00300 } 00301 00302 static int setvol(int vol) 00303 { 00304 if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) { 00305 fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno)); 00306 return -1; 00307 } 00308 return 0; 00309 } 00310 00311 static int oldvol = 0; 00312 static int mutevol = 0; 00313 00314 static int mutedlevel(int orig, int mutelevel) 00315 { 00316 int l = orig >> 8; 00317 int r = orig & 0xff; 00318 l = (float)(mutelevel) * (float)(l) / 100.0; 00319 r = (float)(mutelevel) * (float)(r) / 100.0; 00320 return (l << 8) | r; 00321 } 00322 00323 static void mute(void) 00324 { 00325 int vol; 00326 int start; 00327 int x; 00328 vol = getvol(); 00329 oldvol = vol; 00330 if (smoothfade) 00331 start = 100; 00332 else 00333 start = mutelevel; 00334 for (x=start;x>=mutelevel;x-=stepsize) { 00335 mutevol = mutedlevel(vol, x); 00336 setvol(mutevol); 00337 /* Wait 0.01 sec */ 00338 usleep(10000); 00339 } 00340 mutevol = mutedlevel(vol, mutelevel); 00341 setvol(mutevol); 00342 if (debug) 00343 printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol); 00344 muted = 1; 00345 } 00346 00347 static void unmute(void) 00348 { 00349 int vol; 00350 int start; 00351 int x; 00352 vol = getvol(); 00353 if (debug) 00354 printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol); 00355 if (vol == mutevol) { 00356 if (smoothfade) 00357 start = mutelevel; 00358 else 00359 start = 100; 00360 for (x=start;x<100;x+=stepsize) { 00361 mutevol = mutedlevel(oldvol, x); 00362 setvol(mutevol); 00363 /* Wait 0.01 sec */ 00364 usleep(10000); 00365 } 00366 setvol(oldvol); 00367 } else 00368 printf("Whoops, it's already been changed!\n"); 00369 muted = 0; 00370 } 00371 00372 static void check_mute(void) 00373 { 00374 int offhook = 0; 00375 struct channel *chan; 00376 chan = channels; 00377 while(chan) { 00378 if (chan->subs) { 00379 offhook++; 00380 break; 00381 } 00382 chan = chan->next; 00383 } 00384 if (offhook && !muted) 00385 mute(); 00386 else if (!offhook && muted) 00387 unmute(); 00388 } 00389 00390 static void delete_sub(struct channel *chan, char *name) 00391 { 00392 struct subchannel *sub, *prev; 00393 prev = NULL; 00394 sub = chan->subs; 00395 while(sub) { 00396 if (!strcasecmp(sub->name, name)) { 00397 if (prev) 00398 prev->next = sub->next; 00399 else 00400 chan->subs = sub->next; 00401 free(sub->name); 00402 free(sub); 00403 return; 00404 } 00405 prev = sub; 00406 sub = sub->next; 00407 } 00408 } 00409 00410 static void append_sub(struct channel *chan, char *name) 00411 { 00412 struct subchannel *sub; 00413 sub = chan->subs; 00414 while(sub) { 00415 if (!strcasecmp(sub->name, name)) 00416 return; 00417 sub = sub->next; 00418 } 00419 sub = malloc(sizeof(struct subchannel)); 00420 if (sub) { 00421 memset(sub, 0, sizeof(struct subchannel)); 00422 sub->name = strdup(name); 00423 sub->next = chan->subs; 00424 chan->subs = sub; 00425 } 00426 } 00427 00428 static void hangup_chan(char *channel) 00429 { 00430 struct channel *chan; 00431 if (debug) 00432 printf("Hangup '%s'\n", channel); 00433 chan = find_channel(channel); 00434 if (chan) 00435 delete_sub(chan, channel); 00436 check_mute(); 00437 } 00438 00439 static void offhook_chan(char *channel) 00440 { 00441 struct channel *chan; 00442 if (debug) 00443 printf("Offhook '%s'\n", channel); 00444 chan = find_channel(channel); 00445 if (chan) 00446 append_sub(chan, channel); 00447 check_mute(); 00448 } 00449 00450 static int wait_event(void) 00451 { 00452 char *resp; 00453 char event[120]=""; 00454 char channel[120]=""; 00455 char oldname[120]=""; 00456 char newname[120]=""; 00457 resp = get_line(); 00458 if (!resp) { 00459 fprintf(stderr, "disconnected (6)\n"); 00460 return -1; 00461 } 00462 if (!strncasecmp(resp, "Event: ", strlen("Event: "))) { 00463 strncpy(event, resp + strlen("Event: "), sizeof(event)); 00464 /* Consume the rest of the non-event */ 00465 while((resp = get_line()) && strlen(resp)) { 00466 if (!strncasecmp(resp, "Channel: ", strlen("Channel: "))) 00467 strncpy(channel, resp + strlen("Channel: "), sizeof(channel)); 00468 if (!strncasecmp(resp, "Newname: ", strlen("Newname: "))) 00469 strncpy(newname, resp + strlen("Newname: "), sizeof(newname)); 00470 if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: "))) 00471 strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname)); 00472 } 00473 if (strlen(channel)) { 00474 if (!strcasecmp(event, "Hangup")) 00475 hangup_chan(channel); 00476 else 00477 offhook_chan(channel); 00478 } 00479 if (strlen(newname) && strlen(oldname)) { 00480 if (!strcasecmp(event, "Rename")) { 00481 hangup_chan(oldname); 00482 offhook_chan(newname); 00483 } 00484 } 00485 } else { 00486 /* Consume the rest of the non-event */ 00487 while((resp = get_line()) && strlen(resp)); 00488 } 00489 if (!resp) { 00490 fprintf(stderr, "disconnected (7)\n"); 00491 return -1; 00492 } 00493 return 0; 00494 } 00495 00496 static void usage(void) 00497 { 00498 printf("Usage: muted [-f] [-d]\n" 00499 " -f : Do not fork\n" 00500 " -d : Debug (implies -f)\n"); 00501 } 00502 00503 int main(int argc, char *argv[]) 00504 { 00505 int x; 00506 while((x = getopt(argc, argv, "fhd")) > 0) { 00507 switch(x) { 00508 case 'd': 00509 debug = 1; 00510 needfork = 0; 00511 break; 00512 case 'f': 00513 needfork = 0; 00514 break; 00515 case 'h': 00516 /* Fall through */ 00517 default: 00518 usage(); 00519 exit(1); 00520 } 00521 } 00522 if (load_config()) 00523 exit(1); 00524 if (open_mixer()) 00525 exit(1); 00526 if (connect_asterisk()) { 00527 close(mixfd); 00528 exit(1); 00529 } 00530 if (login_asterisk()) { 00531 close(mixfd); 00532 fclose(astf); 00533 exit(1); 00534 } 00535 if (needfork) 00536 daemon(0,0); 00537 for(;;) { 00538 if (wait_event()) 00539 exit(1); 00540 } 00541 exit(0); 00542 }

Generated on Sat Jun 12 16:40:58 2004 for Asterisk by doxygen 1.3.7