firmware.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. /*! \file
  19. *
  20. * \brief IAX Firmware Support
  21. *
  22. * \author Mark Spencer <markster@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <support_level>core</support_level>
  26. ***/
  27. #include "asterisk.h"
  28. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  29. #include <sys/types.h>
  30. #include <sys/stat.h>
  31. #include <unistd.h>
  32. #include <fcntl.h>
  33. #include <dirent.h>
  34. #include <sys/mman.h>
  35. #include <arpa/inet.h>
  36. #include "asterisk/linkedlists.h"
  37. #include "asterisk/md5.h"
  38. #include "asterisk/paths.h"
  39. #include "asterisk/utils.h"
  40. #include "include/firmware.h"
  41. #define IAX_FIRMWARE_SUBDIR "/firmware/iax"
  42. struct iax_firmware {
  43. AST_LIST_ENTRY(iax_firmware) list;
  44. int fd;
  45. int mmaplen;
  46. int dead;
  47. struct ast_iax2_firmware_header *fwh;
  48. unsigned char *buf;
  49. };
  50. static AST_LIST_HEAD_STATIC(firmwares, iax_firmware);
  51. static int try_firmware(char *s)
  52. {
  53. struct stat stbuf;
  54. struct iax_firmware *cur = NULL;
  55. int ifd, fd, res, len, chunk;
  56. struct ast_iax2_firmware_header *fwh, fwh2;
  57. struct MD5Context md5;
  58. unsigned char sum[16], buf[1024];
  59. char *s2, *last;
  60. s2 = ast_alloca(strlen(s) + 100);
  61. last = strrchr(s, '/');
  62. if (last)
  63. last++;
  64. else
  65. last = s;
  66. snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, ast_random());
  67. if (stat(s, &stbuf) < 0) {
  68. ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
  69. return -1;
  70. }
  71. /* Make sure it's not a directory */
  72. if (S_ISDIR(stbuf.st_mode))
  73. return -1;
  74. ifd = open(s, O_RDONLY);
  75. if (ifd < 0) {
  76. ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
  77. return -1;
  78. }
  79. fd = open(s2, O_RDWR | O_CREAT | O_EXCL, AST_FILE_MODE);
  80. if (fd < 0) {
  81. ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
  82. close(ifd);
  83. return -1;
  84. }
  85. /* Unlink our newly created file */
  86. unlink(s2);
  87. /* Now copy the firmware into it */
  88. len = stbuf.st_size;
  89. while(len) {
  90. chunk = len;
  91. if (chunk > sizeof(buf))
  92. chunk = sizeof(buf);
  93. res = read(ifd, buf, chunk);
  94. if (res != chunk) {
  95. ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
  96. close(ifd);
  97. close(fd);
  98. return -1;
  99. }
  100. res = write(fd, buf, chunk);
  101. if (res != chunk) {
  102. ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
  103. close(ifd);
  104. close(fd);
  105. return -1;
  106. }
  107. len -= chunk;
  108. }
  109. close(ifd);
  110. /* Return to the beginning */
  111. lseek(fd, 0, SEEK_SET);
  112. if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
  113. ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
  114. close(fd);
  115. return -1;
  116. }
  117. if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
  118. ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
  119. close(fd);
  120. return -1;
  121. }
  122. if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
  123. ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
  124. close(fd);
  125. return -1;
  126. }
  127. if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
  128. ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
  129. close(fd);
  130. return -1;
  131. }
  132. fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
  133. if (fwh == MAP_FAILED) {
  134. ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
  135. close(fd);
  136. return -1;
  137. }
  138. MD5Init(&md5);
  139. MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
  140. MD5Final(sum, &md5);
  141. if (memcmp(sum, fwh->chksum, sizeof(sum))) {
  142. ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
  143. munmap((void*)fwh, stbuf.st_size);
  144. close(fd);
  145. return -1;
  146. }
  147. AST_LIST_TRAVERSE(&firmwares, cur, list) {
  148. if (!strcmp((const char *) cur->fwh->devname, (const char *) fwh->devname)) {
  149. /* Found a candidate */
  150. if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
  151. /* The version we have on loaded is older, load this one instead */
  152. break;
  153. /* This version is no newer than what we have. Don't worry about it.
  154. We'll consider it a proper load anyhow though */
  155. munmap((void*)fwh, stbuf.st_size);
  156. close(fd);
  157. return 0;
  158. }
  159. }
  160. if (!cur && ((cur = ast_calloc(1, sizeof(*cur))))) {
  161. cur->fd = -1;
  162. AST_LIST_INSERT_TAIL(&firmwares, cur, list);
  163. }
  164. if (cur) {
  165. if (cur->fwh)
  166. munmap((void*)cur->fwh, cur->mmaplen);
  167. if (cur->fd > -1)
  168. close(cur->fd);
  169. cur->fwh = fwh;
  170. cur->fd = fd;
  171. cur->mmaplen = stbuf.st_size;
  172. cur->dead = 0;
  173. }
  174. return 0;
  175. }
  176. static void destroy_firmware(struct iax_firmware *cur)
  177. {
  178. /* Close firmware */
  179. if (cur->fwh) {
  180. munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
  181. }
  182. close(cur->fd);
  183. ast_free(cur);
  184. }
  185. void iax_firmware_reload(void)
  186. {
  187. struct iax_firmware *cur = NULL;
  188. DIR *fwd;
  189. struct dirent *de;
  190. char fn[PATH_MAX + sizeof(IAX_FIRMWARE_SUBDIR) + sizeof(de->d_name)];
  191. AST_LIST_LOCK(&firmwares);
  192. /* Mark all as dead */
  193. AST_LIST_TRAVERSE(&firmwares, cur, list) {
  194. cur->dead = 1;
  195. }
  196. /* Now that we have marked them dead... load new ones */
  197. snprintf(fn, sizeof(fn), "%s%s", ast_config_AST_DATA_DIR, IAX_FIRMWARE_SUBDIR);
  198. fwd = opendir(fn);
  199. if (fwd) {
  200. while((de = readdir(fwd))) {
  201. if (de->d_name[0] != '.') {
  202. snprintf(fn, sizeof(fn), "%s%s/%s",
  203. ast_config_AST_DATA_DIR, IAX_FIRMWARE_SUBDIR, de->d_name);
  204. if (!try_firmware(fn)) {
  205. ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
  206. }
  207. }
  208. }
  209. closedir(fwd);
  210. } else {
  211. ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", fn, strerror(errno));
  212. }
  213. /* Clean up leftovers */
  214. AST_LIST_TRAVERSE_SAFE_BEGIN(&firmwares, cur, list) {
  215. if (!cur->dead)
  216. continue;
  217. AST_LIST_REMOVE_CURRENT(list);
  218. destroy_firmware(cur);
  219. }
  220. AST_LIST_TRAVERSE_SAFE_END;
  221. AST_LIST_UNLOCK(&firmwares);
  222. }
  223. void iax_firmware_unload(void)
  224. {
  225. struct iax_firmware *cur = NULL;
  226. AST_LIST_LOCK(&firmwares);
  227. AST_LIST_TRAVERSE_SAFE_BEGIN(&firmwares, cur, list) {
  228. AST_LIST_REMOVE_CURRENT(list);
  229. destroy_firmware(cur);
  230. }
  231. AST_LIST_TRAVERSE_SAFE_END;
  232. AST_LIST_UNLOCK(&firmwares);
  233. }
  234. int iax_firmware_get_version(const char *dev, uint16_t *version)
  235. {
  236. struct iax_firmware *cur = NULL;
  237. if (ast_strlen_zero(dev))
  238. return 0;
  239. AST_LIST_LOCK(&firmwares);
  240. AST_LIST_TRAVERSE(&firmwares, cur, list) {
  241. if (!strcmp(dev, (const char *) cur->fwh->devname)) {
  242. *version = ntohs(cur->fwh->version);
  243. AST_LIST_UNLOCK(&firmwares);
  244. return 1;
  245. }
  246. }
  247. AST_LIST_UNLOCK(&firmwares);
  248. return 0;
  249. }
  250. int iax_firmware_append(struct iax_ie_data *ied, const char *dev, unsigned int desc)
  251. {
  252. int res = -1;
  253. unsigned int bs = desc & 0xff;
  254. unsigned int start = (desc >> 8) & 0xffffff;
  255. unsigned int bytes;
  256. struct iax_firmware *cur;
  257. if (ast_strlen_zero((char *)dev) || !bs)
  258. return -1;
  259. start *= bs;
  260. AST_LIST_LOCK(&firmwares);
  261. AST_LIST_TRAVERSE(&firmwares, cur, list) {
  262. if (strcmp(dev, (const char *) cur->fwh->devname))
  263. continue;
  264. iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc);
  265. if (start < ntohl(cur->fwh->datalen)) {
  266. bytes = ntohl(cur->fwh->datalen) - start;
  267. if (bytes > bs)
  268. bytes = bs;
  269. iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
  270. } else {
  271. bytes = 0;
  272. iax_ie_append(ied, IAX_IE_FWBLOCKDATA);
  273. }
  274. if (bytes == bs)
  275. res = 0;
  276. else
  277. res = 1;
  278. break;
  279. }
  280. AST_LIST_UNLOCK(&firmwares);
  281. return res;
  282. }
  283. void iax_firmware_traverse(
  284. const char *filter,
  285. int (*callback)(struct ast_iax2_firmware_header *header, void *data),
  286. void *data)
  287. {
  288. struct iax_firmware *cur = NULL;
  289. if (!callback) {
  290. return;
  291. }
  292. AST_LIST_LOCK(&firmwares);
  293. AST_LIST_TRAVERSE(&firmwares, cur, list) {
  294. if (!filter || !strcasecmp(filter, (const char *) cur->fwh->devname)) {
  295. if (callback(cur->fwh, data)) {
  296. break;
  297. }
  298. }
  299. }
  300. AST_LIST_UNLOCK(&firmwares);
  301. }