00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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) - 1);
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) - 1);
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) - 1);
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
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
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) - 1);
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
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
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) - 1);
00464
00465 while((resp = get_line()) && strlen(resp)) {
00466 if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
00467 strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
00468 if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
00469 strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
00470 if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
00471 strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
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
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
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 fclose(astf);
00540 while(connect_asterisk()) {
00541 sleep(5);
00542 }
00543 if (login_asterisk()) {
00544 fclose(astf);
00545 exit(1);
00546 }
00547 }
00548 }
00549 exit(0);
00550 }