Main Page | Alphabetical List | Data Structures | Directories | 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) - 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    /* 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) - 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       /* 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) - 1);
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) - 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       /* 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          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 }

Generated on Wed Mar 16 20:08:35 2005 for Asterisk by  doxygen 1.4.0