firmware.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2013, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief IAX Firmware Support
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
00033 
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <unistd.h>
00037 #include <fcntl.h>
00038 #include <dirent.h>
00039 #include <sys/mman.h>
00040 #include <arpa/inet.h>
00041 
00042 #include "asterisk/linkedlists.h"
00043 #include "asterisk/md5.h"
00044 #include "asterisk/paths.h"
00045 #include "asterisk/utils.h"
00046 
00047 #include "include/firmware.h"
00048 
00049 struct iax_firmware {
00050    AST_LIST_ENTRY(iax_firmware) list;
00051    int fd;
00052    int mmaplen;
00053    int dead;
00054    struct ast_iax2_firmware_header *fwh;
00055    unsigned char *buf;
00056 };
00057 
00058 static AST_LIST_HEAD_STATIC(firmwares, iax_firmware);
00059 
00060 static int try_firmware(char *s)
00061 {
00062    struct stat stbuf;
00063    struct iax_firmware *cur = NULL;
00064    int ifd, fd, res, len, chunk;
00065    struct ast_iax2_firmware_header *fwh, fwh2;
00066    struct MD5Context md5;
00067    unsigned char sum[16], buf[1024];
00068    char *s2, *last;
00069 
00070    s2 = ast_alloca(strlen(s) + 100);
00071 
00072    last = strrchr(s, '/');
00073    if (last)
00074       last++;
00075    else
00076       last = s;
00077 
00078    snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, ast_random());
00079 
00080    if (stat(s, &stbuf) < 0) {
00081       ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
00082       return -1;
00083    }
00084 
00085    /* Make sure it's not a directory */
00086    if (S_ISDIR(stbuf.st_mode))
00087       return -1;
00088    ifd = open(s, O_RDONLY);
00089    if (ifd < 0) {
00090       ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
00091       return -1;
00092    }
00093    fd = open(s2, O_RDWR | O_CREAT | O_EXCL, AST_FILE_MODE);
00094    if (fd < 0) {
00095       ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
00096       close(ifd);
00097       return -1;
00098    }
00099    /* Unlink our newly created file */
00100    unlink(s2);
00101 
00102    /* Now copy the firmware into it */
00103    len = stbuf.st_size;
00104    while(len) {
00105       chunk = len;
00106       if (chunk > sizeof(buf))
00107          chunk = sizeof(buf);
00108       res = read(ifd, buf, chunk);
00109       if (res != chunk) {
00110          ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
00111          close(ifd);
00112          close(fd);
00113          return -1;
00114       }
00115       res = write(fd, buf, chunk);
00116       if (res != chunk) {
00117          ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
00118          close(ifd);
00119          close(fd);
00120          return -1;
00121       }
00122       len -= chunk;
00123    }
00124    close(ifd);
00125    /* Return to the beginning */
00126    lseek(fd, 0, SEEK_SET);
00127    if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
00128       ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
00129       close(fd);
00130       return -1;
00131    }
00132    if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
00133       ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
00134       close(fd);
00135       return -1;
00136    }
00137    if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
00138       ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
00139       close(fd);
00140       return -1;
00141    }
00142    if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
00143       ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
00144       close(fd);
00145       return -1;
00146    }
00147    fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
00148    if (fwh == MAP_FAILED) {
00149       ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
00150       close(fd);
00151       return -1;
00152    }
00153    MD5Init(&md5);
00154    MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
00155    MD5Final(sum, &md5);
00156    if (memcmp(sum, fwh->chksum, sizeof(sum))) {
00157       ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
00158       munmap((void*)fwh, stbuf.st_size);
00159       close(fd);
00160       return -1;
00161    }
00162 
00163    AST_LIST_TRAVERSE(&firmwares, cur, list) {
00164       if (!strcmp((const char *) cur->fwh->devname, (const char *) fwh->devname)) {
00165          /* Found a candidate */
00166          if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
00167             /* The version we have on loaded is older, load this one instead */
00168             break;
00169          /* This version is no newer than what we have.  Don't worry about it.
00170             We'll consider it a proper load anyhow though */
00171          munmap((void*)fwh, stbuf.st_size);
00172          close(fd);
00173          return 0;
00174       }
00175    }
00176 
00177    if (!cur && ((cur = ast_calloc(1, sizeof(*cur))))) {
00178       cur->fd = -1;
00179       AST_LIST_INSERT_TAIL(&firmwares, cur, list);
00180    }
00181 
00182    if (cur) {
00183       if (cur->fwh)
00184          munmap((void*)cur->fwh, cur->mmaplen);
00185       if (cur->fd > -1)
00186          close(cur->fd);
00187       cur->fwh = fwh;
00188       cur->fd = fd;
00189       cur->mmaplen = stbuf.st_size;
00190       cur->dead = 0;
00191    }
00192 
00193    return 0;
00194 }
00195 
00196 static void destroy_firmware(struct iax_firmware *cur)
00197 {
00198    /* Close firmware */
00199    if (cur->fwh) {
00200       munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
00201    }
00202    close(cur->fd);
00203    ast_free(cur);
00204 }
00205 
00206 void iax_firmware_reload(void)
00207 {
00208    struct iax_firmware *cur = NULL;
00209    DIR *fwd;
00210    struct dirent *de;
00211    char dir[256], fn[256];
00212 
00213    AST_LIST_LOCK(&firmwares);
00214 
00215    /* Mark all as dead */
00216    AST_LIST_TRAVERSE(&firmwares, cur, list) {
00217       cur->dead = 1;
00218    }
00219 
00220    /* Now that we have marked them dead... load new ones */
00221    snprintf(dir, sizeof(dir), "%s/firmware/iax", ast_config_AST_DATA_DIR);
00222    fwd = opendir(dir);
00223    if (fwd) {
00224       while((de = readdir(fwd))) {
00225          if (de->d_name[0] != '.') {
00226             snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name);
00227             if (!try_firmware(fn)) {
00228                ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
00229             }
00230          }
00231       }
00232       closedir(fwd);
00233    } else {
00234       ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", dir, strerror(errno));
00235    }
00236 
00237    /* Clean up leftovers */
00238    AST_LIST_TRAVERSE_SAFE_BEGIN(&firmwares, cur, list) {
00239       if (!cur->dead)
00240          continue;
00241       AST_LIST_REMOVE_CURRENT(list);
00242       destroy_firmware(cur);
00243    }
00244    AST_LIST_TRAVERSE_SAFE_END;
00245 
00246    AST_LIST_UNLOCK(&firmwares);
00247 }
00248 
00249 void iax_firmware_unload(void)
00250 {
00251    struct iax_firmware *cur = NULL;
00252 
00253    AST_LIST_LOCK(&firmwares);
00254    AST_LIST_TRAVERSE_SAFE_BEGIN(&firmwares, cur, list) {
00255       AST_LIST_REMOVE_CURRENT(list);
00256       destroy_firmware(cur);
00257    }
00258    AST_LIST_TRAVERSE_SAFE_END;
00259    AST_LIST_UNLOCK(&firmwares);
00260 }
00261 
00262 int iax_firmware_get_version(const char *dev, uint16_t *version)
00263 {
00264    struct iax_firmware *cur = NULL;
00265 
00266    if (ast_strlen_zero(dev))
00267       return 0;
00268 
00269    AST_LIST_LOCK(&firmwares);
00270    AST_LIST_TRAVERSE(&firmwares, cur, list) {
00271       if (!strcmp(dev, (const char *) cur->fwh->devname)) {
00272          *version = ntohs(cur->fwh->version);
00273          AST_LIST_UNLOCK(&firmwares);
00274          return 1;
00275       }
00276    }
00277    AST_LIST_UNLOCK(&firmwares);
00278 
00279    return 0;
00280 }
00281 
00282 int iax_firmware_append(struct iax_ie_data *ied, const char *dev, unsigned int desc)
00283 {
00284    int res = -1;
00285    unsigned int bs = desc & 0xff;
00286    unsigned int start = (desc >> 8) & 0xffffff;
00287    unsigned int bytes;
00288    struct iax_firmware *cur;
00289 
00290    if (ast_strlen_zero((char *)dev) || !bs)
00291       return -1;
00292 
00293    start *= bs;
00294 
00295    AST_LIST_LOCK(&firmwares);
00296    AST_LIST_TRAVERSE(&firmwares, cur, list) {
00297       if (strcmp(dev, (const char *) cur->fwh->devname))
00298          continue;
00299       iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc);
00300       if (start < ntohl(cur->fwh->datalen)) {
00301          bytes = ntohl(cur->fwh->datalen) - start;
00302          if (bytes > bs)
00303             bytes = bs;
00304          iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
00305       } else {
00306          bytes = 0;
00307          iax_ie_append(ied, IAX_IE_FWBLOCKDATA);
00308       }
00309       if (bytes == bs)
00310          res = 0;
00311       else
00312          res = 1;
00313       break;
00314    }
00315    AST_LIST_UNLOCK(&firmwares);
00316 
00317    return res;
00318 }
00319 
00320 void iax_firmware_traverse(
00321    const char *filter,
00322    int (*callback)(struct ast_iax2_firmware_header *header, void *data),
00323    void *data)
00324 {
00325    struct iax_firmware *cur = NULL;
00326 
00327    if (!callback) {
00328       return;
00329    }
00330 
00331    AST_LIST_LOCK(&firmwares);
00332    AST_LIST_TRAVERSE(&firmwares, cur, list) {
00333       if (!filter || !strcasecmp(filter, (const char *) cur->fwh->devname)) {
00334          if (callback(cur->fwh, data)) {
00335             break;
00336          }
00337       }
00338    }
00339    AST_LIST_UNLOCK(&firmwares);
00340 }

Generated on Thu Apr 16 06:27:34 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6