Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:54 2009

## Asterisk developer's documentation

- Main Page
- Related Pages
- Modules
- Data Structures
- Files
- Directories

# muted.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2005, Digium, Inc.
    00005  *
    00006  * Mark Spencer <markster@digium.com>
    00007  *
    00008  * Updated for Mac OSX CoreAudio
    00009  * by Josh Roberson <josh@asteriasgi.com>
    00010  *
    00011  * See http://www.asterisk.org for more information about
    00012  * the Asterisk project. Please do not directly contact
    00013  * any of the maintainers of this project for assistance;
    00014  * the project provides a web site, mailing lists and IRC
    00015  * channels for your use.
    00016  *
    00017  * This program is free software, distributed under the terms of
    00018  * the GNU General Public License Version 2. See the LICENSE file
    00019  * at the top of the source tree.
    00020  */
    00021
    00022 /*! \file
    00023  * \brief Mute Daemon
    00024  *
    00025  * \note Specially written for Malcolm Davenport, but I think I'll use it too
    00026  * Connects to the Asterisk Manager Interface, AMI, and listens for events
    00027  * on certain devices. If a phone call is connected to one of the devices (phones)
    00028  * the local sound is muted to a lower volume during the call.
    00029  *
    00030  */
    00031
    00032 #ifndef __Darwin__
    00033 #include <linux/soundcard.h>
    00034 #else
    00035 #include <CoreAudio/AudioHardware.h>
    00036 #endif
    00037 #include <stdio.h>
    00038 #include <errno.h>
    00039 #include <stdlib.h>
    00040 #include <unistd.h>
    00041 #include <fcntl.h>
    00042 #include <string.h>
    00043 #include <netdb.h>
    00044 #include <sys/socket.h>
    00045 #include <sys/ioctl.h>
    00046 #include <netinet/in.h>
    00047 #include <arpa/inet.h>
    00048
    00049 static char *config = "/etc/muted.conf";
    00050
    00051 static char host[256] = "";
    00052 static char user[256] = "";
    00053 static char pass[256] = "";
    00054 static int smoothfade = 0;
    00055 static int mutelevel = 20;
    00056 static int muted = 0;
    00057 static int needfork = 1;
    00058 static int debug = 0;
    00059 static int stepsize = 3;
    00060 #ifndef __Darwin__
    00061 static int mixchan = SOUND_MIXER_VOLUME;
    00062 #endif
    00063
    00064 struct subchannel {
    00065    char *name;
    00066    struct subchannel *next;
    00067 };
    00068
    00069 static struct channel {
    00070    char *tech;
    00071    char *location;
    00072    struct channel *next;
    00073    struct subchannel *subs;
    00074 } *channels;
    00075
    00076 static void add_channel(char *tech, char *location)
    00077 {
    00078    struct channel *chan;
    00079    chan = malloc(sizeof(struct channel));
    00080    if (chan) {
    00081       memset(chan, 0, sizeof(struct channel));
    00082       chan->tech = strdup(tech);
    00083       chan->location = strdup(location);
    00084       chan->next = channels;
    00085       channels = chan;
    00086    }
    00087
    00088 }
    00089
    00090 static int load_config(void)
    00091 {
    00092    FILE *f;
    00093    char buf[1024];
    00094    char *val;
    00095    char *val2;
    00096    int lineno=0;
    00097    int x;
    00098    f = fopen(config, "r");
    00099    if (!f) {
    00100       fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno));
    00101       return -1;
    00102    }
    00103    while(!feof(f)) {
    00104       fgets(buf, sizeof(buf), f);
    00105       if (!feof(f)) {
    00106          lineno++;
    00107          val = strchr(buf, '#');
    00108          if (val) *val = '\0';
    00109          while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
    00110             buf[strlen(buf) - 1] = '\0';
    00111          if (!strlen(buf))
    00112             continue;
    00113          val = buf;
    00114          while(*val) {
    00115             if (*val < 33)
    00116                break;
    00117             val++;
    00118          }
    00119          if (*val) {
    00120             *val = '\0';
    00121             val++;
    00122             while(*val && (*val < 33)) val++;
    00123          }
    00124          if (!strcasecmp(buf, "host")) {
    00125             if (val && strlen(val))
    00126                strncpy(host, val, sizeof(host) - 1);
    00127             else
    00128                fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno);
    00129          } else if (!strcasecmp(buf, "user")) {
    00130             if (val && strlen(val))
    00131                strncpy(user, val, sizeof(user) - 1);
    00132             else
    00133                fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno);
    00134          } else if (!strcasecmp(buf, "pass")) {
    00135             if (val && strlen(val))
    00136                strncpy(pass, val, sizeof(pass) - 1);
    00137             else
    00138                fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno);
    00139          } else if (!strcasecmp(buf, "smoothfade")) {
    00140             smoothfade = 1;
    00141          } else if (!strcasecmp(buf, "mutelevel")) {
    00142             if (val && (sscanf(val, "%30d", &x) == 1) && (x > -1) && (x < 101)) {
    00143                mutelevel = x;
    00144             } else
    00145                fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
    00146          } else if (!strcasecmp(buf, "channel")) {
    00147             if (val && strlen(val)) {
    00148                val2 = strchr(val, '/');
    00149                if (val2) {
    00150                   *val2 = '\0';
    00151                   val2++;
    00152                   add_channel(val, val2);
    00153                } else
    00154                   fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno);
    00155             } else
    00156                fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno);
    00157          } else {
    00158             fprintf(stderr, "ignoring unknown keyword '%s'\n", buf);
    00159          }
    00160       }
    00161    }
    00162    fclose(f);
    00163    if (!strlen(host))
    00164       fprintf(stderr, "no 'host' specification in config file\n");
    00165    else if (!strlen(user))
    00166       fprintf(stderr, "no 'user' specification in config file\n");
    00167    else if (!channels)
    00168       fprintf(stderr, "no 'channel' specifications in config file\n");
    00169    else
    00170       return 0;
    00171    return -1;
    00172 }
    00173
    00174 static FILE *astf;
    00175 #ifndef __Darwin__
    00176 static int mixfd;
    00177
    00178 static int open_mixer(void)
    00179 {
    00180    mixfd = open("/dev/mixer", O_RDWR);
    00181    if (mixfd < 0) {
    00182       fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno));
    00183       return -1;
    00184    }
    00185    return 0;
    00186 }
    00187 #endif /* !__Darwin */
    00188
    00189 /*! Connect to the asterisk manager interface */
    00190 static int connect_asterisk(void)
    00191 {
    00192    int sock;
    00193    struct hostent *hp;
    00194    char *ports;
    00195    int port = 5038;
    00196    struct sockaddr_in sin;
    00197
    00198    ports = strchr(host, ':');
    00199    if (ports) {
    00200       *ports = '\0';
    00201       ports++;
    00202       if ((sscanf(ports, "%30d", &port) != 1) || (port < 1) || (port > 65535)) {
    00203          fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports);
    00204          return -1;
    00205       }
    00206    }
    00207    hp = gethostbyname(host);
    00208    if (!hp) {
    00209       fprintf(stderr, "Can't find host '%s'\n", host);
    00210       return -1;
    00211    }
    00212    sock = socket(AF_INET, SOCK_STREAM, 0);
    00213    if (sock < 0) {
    00214       fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
    00215       return -1;
    00216    }
    00217    sin.sin_family = AF_INET;
    00218    sin.sin_port = htons(port);
    00219    memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
    00220    if (connect(sock, &sin, sizeof(sin))) {
    00221       fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno));
    00222       close(sock);
    00223       return -1;
    00224    }
    00225    astf = fdopen(sock, "r+");
    00226    if (!astf) {
    00227       fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
    00228       close(sock);
    00229       return -1;
    00230    }
    00231    return 0;
    00232 }
    00233
    00234 static char *get_line(void)
    00235 {
    00236    static char buf[1024];
    00237    if (fgets(buf, sizeof(buf), astf)) {
    00238       while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
    00239          buf[strlen(buf) - 1] = '\0';
    00240       return buf;
    00241    } else
    00242       return NULL;
    00243 }
    00244
    00245 /*! Login to the asterisk manager interface */
    00246 static int login_asterisk(void)
    00247 {
    00248    char *welcome;
    00249    char *resp;
    00250    if (!(welcome = get_line())) {
    00251       fprintf(stderr, "disconnected (1)\n");
    00252       return -1;
    00253    }
    00254    fprintf(astf,
    00255       "Action: Login\r\n"
    00256       "Username: %s\r\n"
    00257       "Secret: %s\r\n\r\n", user, pass);
    00258    if (!(welcome = get_line())) {
    00259       fprintf(stderr, "disconnected (2)\n");
    00260       return -1;
    00261    }
    00262    if (strcasecmp(welcome, "Response: Success")) {
    00263       fprintf(stderr, "login failed ('%s')\n", welcome);
    00264       return -1;
    00265    }
    00266    /* Eat the rest of the event */
    00267    while((resp = get_line()) && strlen(resp));
    00268    if (!resp) {
    00269       fprintf(stderr, "disconnected (3)\n");
    00270       return -1;
    00271    }
    00272    fprintf(astf,
    00273       "Action: Status\r\n\r\n");
    00274    if (!(welcome = get_line())) {
    00275       fprintf(stderr, "disconnected (4)\n");
    00276       return -1;
    00277    }
    00278    if (strcasecmp(welcome, "Response: Success")) {
    00279       fprintf(stderr, "status failed ('%s')\n", welcome);
    00280       return -1;
    00281    }
    00282    /* Eat the rest of the event */
    00283    while((resp = get_line()) && strlen(resp));
    00284    if (!resp) {
    00285       fprintf(stderr, "disconnected (5)\n");
    00286       return -1;
    00287    }
    00288    return 0;
    00289 }
    00290
    00291 static struct channel *find_channel(char *channel)
    00292 {
    00293    char tmp[256] = "";
    00294    char *s, *t;
    00295    struct channel *chan;
    00296    strncpy(tmp, channel, sizeof(tmp) - 1);
    00297    s = strchr(tmp, '/');
    00298    if (s) {
    00299       *s = '\0';
    00300       s++;
    00301       t = strrchr(s, '-');
    00302       if (t) {
    00303          *t = '\0';
    00304       }
    00305       if (debug)
    00306          printf("Searching for '%s' tech, '%s' location\n", tmp, s);
    00307       chan = channels;
    00308       while(chan) {
    00309          if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) {
    00310             if (debug)
    00311                printf("Found '%s'/'%s'\n", chan->tech, chan->location);
    00312             break;
    00313          }
    00314          chan = chan->next;
    00315       }
    00316    } else
    00317       chan = NULL;
    00318    return chan;
    00319 }
    00320
    00321 #ifndef __Darwin__
    00322 static int getvol(void)
    00323 {
    00324    int vol;
    00325
    00326    if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
    00327 #else
    00328 static float getvol(void)
    00329 {
    00330    float volumeL, volumeR, vol;
    00331    OSStatus err;
    00332    AudioDeviceID device;
    00333    UInt32 size;
    00334    UInt32 channels[2];
    00335
    00336    size = sizeof(device);
    00337    err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
    00338    size = sizeof(channels);
    00339    if (!err)
    00340       err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
    00341    size = sizeof(vol);
    00342    if (!err)
    00343       err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL);
    00344    if (!err)
    00345       err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR);
    00346    if (!err)
    00347       vol = (volumeL < volumeR) ? volumeR : volumeL;
    00348    else {
    00349 #endif
    00350       fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno));
    00351       return -1;
    00352    }
    00353    return vol;
    00354 }
    00355
    00356 #ifndef __Darwin__
    00357 static int setvol(int vol)
    00358 #else
    00359 static int setvol(float vol)
    00360 #endif
    00361 {
    00362 #ifndef __Darwin__
    00363    if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
    00364 #else
    00365    float volumeL = vol;
    00366    float volumeR = vol;
    00367    OSStatus err;
    00368    AudioDeviceID device;
    00369    UInt32 size;
    00370    UInt32 channels[2];
    00371
    00372    size = sizeof(device);
    00373    err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
    00374    size = sizeof(channels);
    00375    err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
    00376    size = sizeof(vol);
    00377    if (!err)
    00378       err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL);
    00379    if (!err)
    00380       err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR);
    00381    if (err) {
    00382 #endif
    00383
    00384       fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
    00385       return -1;
    00386
    00387    }
    00388    return 0;
    00389 }
    00390
    00391 #ifndef __Darwin__
    00392 static int oldvol = 0;
    00393 static int mutevol = 0;
    00394 #else
    00395 static float oldvol = 0;
    00396 static float mutevol = 0;
    00397 #endif
    00398
    00399 #ifndef __Darwin__
    00400 static int mutedlevel(int orig, int mutelevel)
    00401 {
    00402    int l = orig >> 8;
    00403    int r = orig & 0xff;
    00404    l = (float)(mutelevel) * (float)(l) / 100.0;
    00405    r = (float)(mutelevel) * (float)(r) / 100.0;
    00406
    00407    return (l << 8) | r;
    00408 #else
    00409 static float mutedlevel(float orig, float mutelevel)
    00410 {
    00411    float master = orig;
    00412    master = mutelevel * master / 100.0;
    00413    return master;
    00414 #endif
    00415
    00416 }
    00417
    00418 static void mute(void)
    00419 {
    00420 #ifndef __Darwin__
    00421    int vol;
    00422    int start;
    00423    int x;
    00424 #else
    00425    float vol;
    00426    float start = 1.0;
    00427    float x;
    00428 #endif
    00429    vol = getvol();
    00430    oldvol = vol;
    00431    if (smoothfade)
    00432 #ifdef __Darwin__
    00433       start = mutelevel;
    00434 #else
    00435       start = 100;
    00436    else
    00437       start = mutelevel;
    00438 #endif
    00439    for (x=start;x>=mutelevel;x-=stepsize) {
    00440       mutevol = mutedlevel(vol, x);
    00441       setvol(mutevol);
    00442       /* Wait 0.01 sec */
    00443       usleep(10000);
    00444    }
    00445    mutevol = mutedlevel(vol, mutelevel);
    00446    setvol(mutevol);
    00447    if (debug)
    00448 #ifdef __Darwin__
    00449       printf("Mute from '%f' to '%f'!\n", oldvol, mutevol);
    00450 #else
    00451       printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol);
    00452 #endif
    00453    muted = 1;
    00454 }
    00455
    00456 static void unmute(void)
    00457 {
    00458 #ifdef __Darwin__
    00459    float vol;
    00460    float start;
    00461    float x;
    00462 #else
    00463    int vol;
    00464    int start;
    00465    int x;
    00466 #endif
    00467    vol = getvol();
    00468    if (debug)
    00469 #ifdef __Darwin__
    00470       printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol);
    00471    mutevol = vol;
    00472    if (vol == mutevol) {
    00473 #else
    00474       printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol);
    00475    if ((int)vol == mutevol) {
    00476 #endif
    00477       if (smoothfade)
    00478          start = mutelevel;
    00479       else
    00480 #ifdef __Darwin__
    00481          start = 1.0;
    00482 #else
    00483          start = 100;
    00484 #endif
    00485       for (x=start;x<100;x+=stepsize) {
    00486          mutevol = mutedlevel(oldvol, x);
    00487          setvol(mutevol);
    00488          /* Wait 0.01 sec */
    00489          usleep(10000);
    00490       }
    00491       setvol(oldvol);
    00492    } else
    00493       printf("Whoops, it's already been changed!\n");
    00494    muted = 0;
    00495 }
    00496
    00497 static void check_mute(void)
    00498 {
    00499    int offhook = 0;
    00500    struct channel *chan;
    00501    chan = channels;
    00502    while(chan) {
    00503       if (chan->subs) {
    00504          offhook++;
    00505          break;
    00506       }
    00507       chan = chan->next;
    00508    }
    00509    if (offhook && !muted)
    00510       mute();
    00511    else if (!offhook && muted)
    00512       unmute();
    00513 }
    00514
    00515 static void delete_sub(struct channel *chan, char *name)
    00516 {
    00517    struct subchannel *sub, *prev;
    00518    prev = NULL;
    00519    sub = chan->subs;
    00520    while(sub) {
    00521       if (!strcasecmp(sub->name, name)) {
    00522          if (prev)
    00523             prev->next = sub->next;
    00524          else
    00525             chan->subs = sub->next;
    00526          free(sub->name);
    00527          free(sub);
    00528          return;
    00529       }
    00530       prev = sub;
    00531       sub = sub->next;
    00532    }
    00533 }
    00534
    00535 static void append_sub(struct channel *chan, char *name)
    00536 {
    00537    struct subchannel *sub;
    00538    sub = chan->subs;
    00539    while(sub) {
    00540       if (!strcasecmp(sub->name, name))
    00541          return;
    00542       sub = sub->next;
    00543    }
    00544    sub = malloc(sizeof(struct subchannel));
    00545    if (sub) {
    00546       memset(sub, 0, sizeof(struct subchannel));
    00547       sub->name = strdup(name);
    00548       sub->next = chan->subs;
    00549       chan->subs = sub;
    00550    }
    00551 }
    00552
    00553 static void hangup_chan(char *channel)
    00554 {
    00555    struct channel *chan;
    00556    if (debug)
    00557       printf("Hangup '%s'\n", channel);
    00558    chan = find_channel(channel);
    00559    if (chan)
    00560       delete_sub(chan, channel);
    00561    check_mute();
    00562 }
    00563
    00564 static void offhook_chan(char *channel)
    00565 {
    00566    struct channel *chan;
    00567    if (debug)
    00568       printf("Offhook '%s'\n", channel);
    00569    chan = find_channel(channel);
    00570    if (chan)
    00571       append_sub(chan, channel);
    00572    check_mute();
    00573 }
    00574
    00575 static int wait_event(void)
    00576 {
    00577    char *resp;
    00578    char event[120]="";
    00579    char channel[120]="";
    00580    char oldname[120]="";
    00581    char newname[120]="";
    00582
    00583    resp = get_line();
    00584    if (!resp) {
    00585       fprintf(stderr, "disconnected (6)\n");
    00586       return -1;
    00587    }
    00588    if (!strncasecmp(resp, "Event: ", strlen("Event: "))) {
    00589       strncpy(event, resp + strlen("Event: "), sizeof(event) - 1);
    00590       /* Consume the rest of the non-event */
    00591       while((resp = get_line()) && strlen(resp)) {
    00592          if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
    00593             strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
    00594          if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
    00595             strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
    00596          if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
    00597             strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
    00598       }
    00599       if (strlen(channel)) {
    00600          if (!strcasecmp(event, "Hangup"))
    00601             hangup_chan(channel);
    00602          else
    00603             offhook_chan(channel);
    00604       }
    00605       if (strlen(newname) && strlen(oldname)) {
    00606          if (!strcasecmp(event, "Rename")) {
    00607             hangup_chan(oldname);
    00608             offhook_chan(newname);
    00609          }
    00610       }
    00611    } else {
    00612       /* Consume the rest of the non-event */
    00613       while((resp = get_line()) && strlen(resp));
    00614    }
    00615    if (!resp) {
    00616       fprintf(stderr, "disconnected (7)\n");
    00617       return -1;
    00618    }
    00619    return 0;
    00620 }
    00621
    00622 static void usage(void)
    00623 {
    00624    printf("Usage: muted [-f] [-d]\n"
    00625           "        -f : Do not fork\n"
    00626           "        -d : Debug (implies -f)\n");
    00627 }
    00628
    00629 int main(int argc, char *argv[])
    00630 {
    00631    int x;
    00632    while((x = getopt(argc, argv, "fhd")) > 0) {
    00633       switch(x) {
    00634       case 'd':
    00635          debug = 1;
    00636          needfork = 0;
    00637          break;
    00638       case 'f':
    00639          needfork = 0;
    00640          break;
    00641       case 'h':
    00642          /* Fall through */
    00643       default:
    00644          usage();
    00645          exit(1);
    00646       }
    00647    }
    00648    if (load_config())
    00649       exit(1);
    00650 #ifndef __Darwin__
    00651    if (open_mixer())
    00652       exit(1);
    00653 #endif
    00654    if (connect_asterisk()) {
    00655 #ifndef __Darwin__
    00656       close(mixfd);
    00657 #endif
    00658       exit(1);
    00659    }
    00660    if (login_asterisk()) {
    00661 #ifndef __Darwin__
    00662       close(mixfd);
    00663 #endif
    00664       fclose(astf);
    00665       exit(1);
    00666    }
    00667    if (needfork)
    00668       daemon(0,0);
    00669    for(;;) {
    00670       if (wait_event()) {
    00671          fclose(astf);
    00672          while(connect_asterisk()) {
    00673             sleep(5);
    00674          }
    00675          if (login_asterisk()) {
    00676             fclose(astf);
    00677             exit(1);
    00678          }
    00679       }
    00680    }
    00681    exit(0);
    00682 }