firmware.c File Reference

IAX Firmware Support. More...

#include "asterisk.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include "asterisk/linkedlists.h"
#include "asterisk/md5.h"
#include "asterisk/paths.h"
#include "asterisk/utils.h"
#include "include/firmware.h"

Include dependency graph for firmware.c:

Go to the source code of this file.

Data Structures

struct  firmwares
struct  iax_firmware

Functions

static void destroy_firmware (struct iax_firmware *cur)
int iax_firmware_append (struct iax_ie_data *ied, const char *dev, unsigned int desc)
int iax_firmware_get_version (const char *dev, uint16_t *version)
void iax_firmware_reload (void)
void iax_firmware_traverse (const char *filter, int(*callback)(struct ast_iax2_firmware_header *header, void *data), void *data)
void iax_firmware_unload (void)
static int try_firmware (char *s)


Detailed Description

IAX Firmware Support.

Author:
Mark Spencer <markster@digium.com>

Definition in file firmware.c.


Function Documentation

static void destroy_firmware ( struct iax_firmware cur  )  [static]

Definition at line 196 of file firmware.c.

References ast_free, ast_iax2_firmware_header::datalen, iax_firmware::fd, and iax_firmware::fwh.

Referenced by iax_firmware_reload(), and iax_firmware_unload().

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 }

int iax_firmware_append ( struct iax_ie_data ied,
const char *  dev,
unsigned int  desc 
)

Definition at line 282 of file firmware.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero, bs, ast_iax2_firmware_header::data, ast_iax2_firmware_header::datalen, ast_iax2_firmware_header::devname, iax_firmware::fwh, iax_ie_append(), iax_ie_append_int(), iax_ie_append_raw(), IAX_IE_FWBLOCKDATA, IAX_IE_FWBLOCKDESC, and iax_firmware::list.

Referenced by socket_process_helper().

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 }

int iax_firmware_get_version ( const char *  dev,
uint16_t *  version 
)

Definition at line 262 of file firmware.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero, ast_iax2_firmware_header::devname, iax_firmware::fwh, iax_firmware::list, NULL, and ast_iax2_firmware_header::version.

Referenced by update_registry().

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 }

void iax_firmware_reload ( void   ) 

Definition at line 206 of file firmware.c.

References ast_config_AST_DATA_DIR, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log, ast_verb, iax_firmware::dead, destroy_firmware(), errno, iax_firmware::list, LOG_WARNING, NULL, and try_firmware().

Referenced by load_module().

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 }

void iax_firmware_traverse ( const char *  filter,
int(*)(struct ast_iax2_firmware_header *header, void *data)  callback,
void *  data 
)

Definition at line 320 of file firmware.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_iax2_firmware_header::devname, iax_firmware::fwh, iax_firmware::list, and NULL.

Referenced by handle_cli_iax2_show_firmware().

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 }

void iax_firmware_unload ( void   ) 

static int try_firmware ( char *  s  )  [static]

Definition at line 60 of file firmware.c.

References ast_alloca, ast_calloc, AST_FILE_MODE, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log, ast_random(), ast_strlen_zero, ast_iax2_firmware_header::chksum, ast_iax2_firmware_header::data, ast_iax2_firmware_header::datalen, iax_firmware::dead, ast_iax2_firmware_header::devname, errno, iax_firmware::fd, iax_firmware::fwh, IAX_FIRMWARE_MAGIC, last, len(), LOG_WARNING, ast_iax2_firmware_header::magic, MD5Final(), MD5Init(), MD5Update(), iax_firmware::mmaplen, NULL, and ast_iax2_firmware_header::version.

Referenced by iax_firmware_reload().

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 }


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