Asterisk - The Open Source Telephony Project GIT-master-a358458
firmware.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief IAX Firmware Support
22 *
23 * \author Mark Spencer <markster@digium.com>
24 */
25
26/*** MODULEINFO
27 <support_level>core</support_level>
28 ***/
29
30#include "asterisk.h"
31
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <dirent.h>
37#include <sys/mman.h>
38#include <arpa/inet.h>
39
41#include "asterisk/md5.h"
42#include "asterisk/paths.h"
43#include "asterisk/utils.h"
44
45#include "include/firmware.h"
46
47#define IAX_FIRMWARE_SUBDIR "/firmware/iax"
48
51 int fd;
53 int dead;
55 unsigned char *buf;
56};
57
59
60static int try_firmware(char *s)
61{
62 struct stat stbuf;
63 struct iax_firmware *cur = NULL;
64 int ifd, fd, res, len, chunk;
65 struct ast_iax2_firmware_header *fwh, fwh2;
66 struct MD5Context md5;
67 unsigned char sum[16], buf[1024];
68 char *s2, *last;
69
70 s2 = ast_alloca(strlen(s) + 100);
71
72 last = strrchr(s, '/');
73 if (last)
74 last++;
75 else
76 last = s;
77
78 snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, ast_random());
79
80 if (stat(s, &stbuf) < 0) {
81 ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
82 return -1;
83 }
84
85 /* Make sure it's not a directory */
86 if (S_ISDIR(stbuf.st_mode))
87 return -1;
88 ifd = open(s, O_RDONLY);
89 if (ifd < 0) {
90 ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
91 return -1;
92 }
93 fd = open(s2, O_RDWR | O_CREAT | O_EXCL, AST_FILE_MODE);
94 if (fd < 0) {
95 ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
96 close(ifd);
97 return -1;
98 }
99 /* Unlink our newly created file */
100 unlink(s2);
101
102 /* Now copy the firmware into it */
103 len = stbuf.st_size;
104 while(len) {
105 chunk = len;
106 if (chunk > sizeof(buf))
107 chunk = sizeof(buf);
108 res = read(ifd, buf, chunk);
109 if (res != chunk) {
110 ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
111 close(ifd);
112 close(fd);
113 return -1;
114 }
115 res = write(fd, buf, chunk);
116 if (res != chunk) {
117 ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
118 close(ifd);
119 close(fd);
120 return -1;
121 }
122 len -= chunk;
123 }
124 close(ifd);
125 /* Return to the beginning */
126 lseek(fd, 0, SEEK_SET);
127 if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
128 ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
129 close(fd);
130 return -1;
131 }
132 if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
133 ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
134 close(fd);
135 return -1;
136 }
137 if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
138 ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
139 close(fd);
140 return -1;
141 }
142 if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
143 ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
144 close(fd);
145 return -1;
146 }
147 fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
148 if (fwh == MAP_FAILED) {
149 ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
150 close(fd);
151 return -1;
152 }
153 MD5Init(&md5);
154 MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
155 MD5Final(sum, &md5);
156 if (memcmp(sum, fwh->chksum, sizeof(sum))) {
157 ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
158 munmap((void*)fwh, stbuf.st_size);
159 close(fd);
160 return -1;
161 }
162
163 AST_LIST_TRAVERSE(&firmwares, cur, list) {
164 if (!strcmp((const char *) cur->fwh->devname, (const char *) fwh->devname)) {
165 /* Found a candidate */
166 if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
167 /* The version we have on loaded is older, load this one instead */
168 break;
169 /* This version is no newer than what we have. Don't worry about it.
170 We'll consider it a proper load anyhow though */
171 munmap((void*)fwh, stbuf.st_size);
172 close(fd);
173 return 0;
174 }
175 }
176
177 if (!cur && ((cur = ast_calloc(1, sizeof(*cur))))) {
178 cur->fd = -1;
179 AST_LIST_INSERT_TAIL(&firmwares, cur, list);
180 }
181
182 if (cur) {
183 if (cur->fwh)
184 munmap((void*)cur->fwh, cur->mmaplen);
185 if (cur->fd > -1)
186 close(cur->fd);
187 cur->fwh = fwh;
188 cur->fd = fd;
189 cur->mmaplen = stbuf.st_size;
190 cur->dead = 0;
191 }
192
193 return 0;
194}
195
196static void destroy_firmware(struct iax_firmware *cur)
197{
198 /* Close firmware */
199 if (cur->fwh) {
200 munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
201 }
202 close(cur->fd);
203 ast_free(cur);
204}
205
207{
208 struct iax_firmware *cur = NULL;
209 DIR *fwd;
210 struct dirent *de;
211 char fn[PATH_MAX + sizeof(IAX_FIRMWARE_SUBDIR) + sizeof(de->d_name)];
212
214
215 /* Mark all as dead */
216 AST_LIST_TRAVERSE(&firmwares, cur, list) {
217 cur->dead = 1;
218 }
219
220 /* Now that we have marked them dead... load new ones */
221 snprintf(fn, sizeof(fn), "%s%s", ast_config_AST_DATA_DIR, IAX_FIRMWARE_SUBDIR);
222 fwd = opendir(fn);
223 if (fwd) {
224 while((de = readdir(fwd))) {
225 if (de->d_name[0] != '.') {
226 snprintf(fn, sizeof(fn), "%s%s/%s",
228 if (!try_firmware(fn)) {
229 ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
230 }
231 }
232 }
233 closedir(fwd);
234 } else {
235 ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", fn, strerror(errno));
236 }
237
238 /* Clean up leftovers */
240 if (!cur->dead)
241 continue;
243 destroy_firmware(cur);
244 }
246
248}
249
251{
252 struct iax_firmware *cur = NULL;
253
257 destroy_firmware(cur);
258 }
261}
262
263int iax_firmware_get_version(const char *dev, uint16_t *version)
264{
265 struct iax_firmware *cur = NULL;
266
267 if (ast_strlen_zero(dev))
268 return 0;
269
272 if (!strcmp(dev, (const char *) cur->fwh->devname)) {
273 *version = ntohs(cur->fwh->version);
275 return 1;
276 }
277 }
279
280 return 0;
281}
282
283int iax_firmware_append(struct iax_ie_data *ied, const char *dev, unsigned int desc)
284{
285 int res = -1;
286 unsigned int bs = desc & 0xff;
287 unsigned int start = (desc >> 8) & 0xffffff;
288 unsigned int bytes;
289 struct iax_firmware *cur;
290
291 if (ast_strlen_zero((char *)dev) || !bs)
292 return -1;
293
294 start *= bs;
295
298 if (strcmp(dev, (const char *) cur->fwh->devname))
299 continue;
301 if (start < ntohl(cur->fwh->datalen)) {
302 bytes = ntohl(cur->fwh->datalen) - start;
303 if (bytes > bs)
304 bytes = bs;
305 iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
306 } else {
307 bytes = 0;
309 }
310 if (bytes == bs)
311 res = 0;
312 else
313 res = 1;
314 break;
315 }
317
318 return res;
319}
320
322 const char *filter,
323 int (*callback)(struct ast_iax2_firmware_header *header, void *data),
324 void *data)
325{
326 struct iax_firmware *cur = NULL;
327
328 if (!callback) {
329 return;
330 }
331
334 if (!filter || !strcasecmp(filter, (const char *) cur->fwh->devname)) {
335 if (callback(cur->fwh, data)) {
336 break;
337 }
338 }
339 }
341}
struct sla_ringing_trunk * last
Definition: app_sla.c:332
Asterisk main include file. File version handling, generic pbx functions.
#define AST_FILE_MODE
Definition: asterisk.h:32
#define PATH_MAX
Definition: asterisk.h:40
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
static const char desc[]
Definition: cdr_radius.c:84
static char version[AST_MAX_EXTENSION]
Definition: chan_ooh323.c:391
char * bs
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int try_firmware(char *s)
Definition: firmware.c:60
#define IAX_FIRMWARE_SUBDIR
Definition: firmware.c:47
int iax_firmware_get_version(const char *dev, uint16_t *version)
Definition: firmware.c:263
void iax_firmware_reload(void)
Definition: firmware.c:206
void iax_firmware_unload(void)
Definition: firmware.c:250
static void destroy_firmware(struct iax_firmware *cur)
Definition: firmware.c:196
int iax_firmware_append(struct iax_ie_data *ied, const char *dev, unsigned int desc)
Definition: firmware.c:283
void iax_firmware_traverse(const char *filter, int(*callback)(struct ast_iax2_firmware_header *header, void *data), void *data)
Definition: firmware.c:321
IAX Firmware Support header file.
static int md5(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_md5.c:52
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:807
#define IAX_FIRMWARE_MAGIC
Definition: iax2.h:293
#define IAX_IE_FWBLOCKDATA
Definition: iax2.h:166
#define IAX_IE_FWBLOCKDESC
Definition: iax2.h:165
#define ast_verb(level,...)
#define LOG_WARNING
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:291
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
int errno
MD5 digest functions.
void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len)
Definition: md5.c:72
void MD5Init(struct MD5Context *context)
Definition: md5.c:57
void MD5Final(unsigned char digest[16], struct MD5Context *context)
Definition: md5.c:120
int iax_ie_append(struct iax_ie_data *ied, unsigned char ie)
Definition: parser.c:780
int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen)
Definition: parser.c:726
int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value)
Definition: parser.c:756
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Definition: md5.h:26
unsigned short version
Definition: iax2.h:297
unsigned char devname[16]
Definition: iax2.h:298
unsigned int magic
Definition: iax2.h:296
unsigned int datalen
Definition: iax2.h:299
unsigned char chksum[16]
Definition: iax2.h:300
unsigned char data[0]
Definition: iax2.h:301
struct ast_iax2_firmware_header * fwh
Definition: firmware.c:54
struct iax_firmware::@138 list
unsigned char * buf
Definition: firmware.c:55
int mmaplen
Definition: firmware.c:52
Utility functions.
long int ast_random(void)
Definition: utils.c:2312