You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

505 lines
12 KiB

  1. /***************************************************************************
  2. * Copyright (C) 2007,2008,2009 √ėyvind Harboe *
  3. * oyvind.harboe@zylin.com *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 2 of the License, or *
  8. * (at your option) any later version. *
  9. * *
  10. * This program is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU General Public License *
  16. * along with this program; if not, write to the *
  17. * Free Software Foundation, Inc., *
  18. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  19. ***************************************************************************/
  20. /* some bits were copied from ahttpd which is under eCos license and
  21. * copyright to FSF
  22. */
  23. #ifdef HAVE_CONFIG_H
  24. #include "config.h"
  25. #endif
  26. #include "telnet_server.h"
  27. #include "target.h"
  28. #include <microhttpd.h>
  29. #include <pthread.h>
  30. #include <signal.h>
  31. #define PAGE_NOT_FOUND "<html><head><title > File not found</title></head><body > File not found</body></html>"
  32. static pthread_mutex_t mutex;
  33. void openocd_sleep_prelude(void)
  34. {
  35. pthread_mutex_unlock(&mutex);
  36. }
  37. void openocd_sleep_postlude(void)
  38. {
  39. pthread_mutex_lock(&mutex);
  40. }
  41. int loadFile(const char *name, void **data, size_t *len);
  42. static const char *appendf(const char *prev, const char *format, ...)
  43. {
  44. va_list ap;
  45. va_start(ap, format);
  46. char *string = alloc_vprintf(format, ap);
  47. va_end(ap);
  48. char *string2 = NULL;
  49. if (string != NULL)
  50. {
  51. string2 = alloc_printf("%s%s", (prev == NULL) ? "" : prev, string);
  52. }
  53. if (prev != NULL)
  54. {
  55. free((void *)prev);
  56. }
  57. if (string == NULL)
  58. free(string);
  59. return string2;
  60. }
  61. static const char *httpd_exec_cgi_tcl_error(Jim_Interp *interp)
  62. {
  63. int len, i;
  64. const char *t = NULL;
  65. t = appendf(t, "<html><body>\n");
  66. t = appendf(t, "Runtime error, file \"%s\", line %d:<br>",
  67. interp->errorFileName, interp->errorLine);
  68. t = appendf(t, " %s < br>", Jim_GetString(interp->result, NULL));
  69. Jim_ListLength(interp, interp->stackTrace, &len);
  70. for (i = 0; i < len; i += 3)
  71. {
  72. Jim_Obj *objPtr;
  73. const char *proc, *file, *line;
  74. Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
  75. proc = Jim_GetString(objPtr, NULL);
  76. Jim_ListIndex(interp, interp->stackTrace, i + 1, &objPtr, JIM_NONE);
  77. file = Jim_GetString(objPtr, NULL);
  78. Jim_ListIndex(interp, interp->stackTrace, i + 2, &objPtr, JIM_NONE);
  79. line = Jim_GetString(objPtr, NULL);
  80. t = appendf(t, "In procedure '%s' called at file \"%s\", line %s < br>",
  81. proc, file, line);
  82. }
  83. t = appendf(t, "</html></body>\n");
  84. return t;
  85. }
  86. static int httpd_Jim_Command_writeform(Jim_Interp *interp, int argc,
  87. Jim_Obj * const *argv)
  88. {
  89. if (argc != 3)
  90. {
  91. Jim_WrongNumArgs(interp, 1, argv, "method ?CMD_ARGV ...?");
  92. return JIM_ERR;
  93. }
  94. char *name = (char*) Jim_GetString(argv[1], NULL);
  95. char *file = (char*) Jim_GetString(argv[2], NULL);
  96. // Find length
  97. const char *data;
  98. int actual;
  99. int retcode;
  100. const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
  101. name);
  102. retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
  103. free((void *) script);
  104. if (retcode != JIM_OK)
  105. return retcode;
  106. data = Jim_GetString(Jim_GetResult(interp), &actual);
  107. FILE *f = fopen(file, "wb");
  108. if (NULL == f)
  109. {
  110. Jim_SetResultString(interp, "Could not create file", -1);
  111. return JIM_ERR;
  112. }
  113. int result = fwrite(data, 1, actual, f);
  114. fclose(f);
  115. if (result != actual)
  116. {
  117. Jim_SetResultString(interp, "Could not write to file", -1);
  118. return JIM_ERR;
  119. }
  120. return JIM_OK;
  121. }
  122. int
  123. httpd_Jim_Command_formfetch(Jim_Interp *interp,
  124. int argc,
  125. Jim_Obj *const *argv)
  126. {
  127. if (argc != 2)
  128. {
  129. Jim_WrongNumArgs(interp, 1, argv, "method ?CMD_ARGV ...?");
  130. return JIM_ERR;
  131. }
  132. char *name = (char*)Jim_GetString(argv[1], NULL);
  133. const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
  134. name);
  135. int retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
  136. free((void *) script);
  137. if (retcode != JIM_OK)
  138. {
  139. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  140. } else
  141. {
  142. Jim_SetResult(interp, Jim_GetResult(interp));
  143. }
  144. return JIM_OK;
  145. }
  146. struct httpd_request
  147. {
  148. int post;
  149. struct MHD_PostProcessor *postprocessor;
  150. //Jim_Obj *dict;
  151. int complete; /* did we receive the entire post ? */
  152. };
  153. static void request_completed(void *cls, struct MHD_Connection *connection,
  154. void **con_cls, enum MHD_RequestTerminationCode toe)
  155. {
  156. struct httpd_request *r = (struct httpd_request*) *con_cls;
  157. if (NULL == r)
  158. return;
  159. if (r->postprocessor)
  160. {
  161. openocd_sleep_postlude();
  162. MHD_destroy_post_processor(r->postprocessor);
  163. openocd_sleep_prelude();
  164. }
  165. free(r);
  166. *con_cls = NULL;
  167. }
  168. /* append to said key in dictonary */
  169. static void append_key(struct httpd_request *r, const char *key,
  170. const char *data, size_t off, size_t size)
  171. {
  172. Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1);
  173. Jim_IncrRefCount(keyObj);
  174. Jim_Obj *value = NULL;
  175. Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0);
  176. if (dict != NULL)
  177. {
  178. if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK)
  179. {
  180. value = NULL;
  181. }
  182. else
  183. {
  184. Jim_IncrRefCount(value);
  185. }
  186. }
  187. if (value == NULL)
  188. {
  189. value = Jim_NewStringObj(interp, "", -1);
  190. Jim_IncrRefCount(value);
  191. }
  192. /* create a new object we append to and insert into this location */
  193. Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1);
  194. Jim_IncrRefCount(newObj);
  195. Jim_AppendObj(interp, newObj, value);
  196. Jim_AppendString(interp, newObj, data, size);
  197. /* uhh... use name here of dictionary */
  198. dict = Jim_NewStringObj(interp, "httppostdata", -1);
  199. Jim_IncrRefCount(dict);
  200. Jim_SetDictKeysVector(interp, dict, &keyObj, 1, newObj);
  201. Jim_DecrRefCount(interp, dict);
  202. Jim_DecrRefCount(interp, value);
  203. Jim_DecrRefCount(interp, newObj);
  204. Jim_DecrRefCount(interp, keyObj);
  205. }
  206. /* append data to each key */
  207. static int iterate_post(void *con_cls, enum MHD_ValueKind kind,
  208. const char *key, const char *filename, const char *content_type,
  209. const char *transfer_encoding, const char *data, uint64_t off,
  210. size_t size)
  211. {
  212. struct httpd_request *r = (struct httpd_request*) con_cls;
  213. append_key(r, key, data, off, size);
  214. return MHD_YES;
  215. }
  216. static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key,
  217. const char *value)
  218. {
  219. struct httpd_request *r = (struct httpd_request*) cls;
  220. append_key(r, key, value, 0, strlen(value));
  221. return MHD_YES;
  222. }
  223. static int handle_request(struct MHD_Connection * connection, const char * url)
  224. {
  225. struct MHD_Response * response;
  226. int ret;
  227. const char *suffix;
  228. suffix = strrchr(url, '.');
  229. if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0))
  230. {
  231. printf("Run tcl %s\n", url);
  232. int retcode;
  233. const char *script = alloc_printf(
  234. "global httpdata; source {%s}; set httpdata", url);
  235. retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
  236. free((void *) script);
  237. if (retcode != JIM_OK)
  238. {
  239. printf("Tcl failed\n");
  240. const char *t = httpd_exec_cgi_tcl_error(interp);
  241. if (t == NULL)
  242. return MHD_NO;
  243. response = MHD_create_response_from_data(strlen(t), (void *) t,
  244. MHD_YES, MHD_NO);
  245. ret = MHD_queue_response(connection,
  246. MHD_HTTP_INTERNAL_SERVER_ERROR, response);
  247. MHD_destroy_response(response);
  248. return ret;
  249. }
  250. else
  251. {
  252. LOG_DEBUG("Tcl OK");
  253. /* FIX!!! how to handle mime types??? */
  254. const char *result;
  255. int reslen;
  256. result = Jim_GetString(Jim_GetResult(interp), &reslen);
  257. response = MHD_create_response_from_data(reslen, (void *) result,
  258. MHD_NO, MHD_YES);
  259. ret = MHD_queue_response(connection,
  260. MHD_HTTP_INTERNAL_SERVER_ERROR, response);
  261. MHD_destroy_response(response);
  262. return ret;
  263. }
  264. }
  265. else
  266. {
  267. void *data;
  268. size_t len;
  269. int retval = loadFile(url, &data, &len);
  270. if (retval != ERROR_OK)
  271. {
  272. printf("Did not find %s\n", url);
  273. response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND),
  274. (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO);
  275. ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
  276. MHD_destroy_response(response);
  277. return ret;
  278. }
  279. LOG_DEBUG("Serving %s length=%zu", url, len);
  280. /* serve file directly */
  281. response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
  282. /* Should we expose mimetype via tcl here or just let the browser
  283. guess?
  284. MHD_add_response_header(response, "Content-Type", "image/png");
  285. */
  286. ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
  287. MHD_destroy_response(response);
  288. //free(data);
  289. return ret;
  290. }
  291. }
  292. static int ahc_echo_inner(void * cls, struct MHD_Connection * connection,
  293. const char * url, const char * method, const char * version,
  294. const char * upload_data, size_t * upload_data_size, void ** ptr)
  295. {
  296. int post = 0;
  297. if (0 == strcmp(method, "POST"))
  298. {
  299. post = 1;
  300. }
  301. else if (0 == strcmp(method, "GET"))
  302. {
  303. }
  304. else
  305. {
  306. return MHD_NO; /* unexpected method */
  307. }
  308. struct httpd_request *r;
  309. if (*ptr == NULL)
  310. {
  311. /* The first time only the headers are valid,
  312. do not respond in the first round... */
  313. *ptr = malloc(sizeof(struct httpd_request));
  314. if (*ptr == NULL)
  315. return MHD_NO;
  316. memset(*ptr, 0, sizeof(struct httpd_request));
  317. r = (struct httpd_request *) *ptr;
  318. r->post = post;
  319. Jim_SetVariableStr(interp, "httppostdata", Jim_NewDictObj(interp, NULL, 0));
  320. /* fill in url query strings in dictonary */
  321. MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND,
  322. record_arg, r);
  323. if (r->post)
  324. {
  325. r->postprocessor = MHD_create_post_processor(connection, 2048
  326. * 1024, &iterate_post, r);
  327. }
  328. return MHD_YES;
  329. }
  330. r = (struct httpd_request *) *ptr;
  331. if (r->post)
  332. {
  333. /* consume post data */
  334. if (*upload_data_size)
  335. {
  336. MHD_post_process(r->postprocessor, upload_data, *upload_data_size);
  337. *upload_data_size = 0;
  338. return MHD_YES;
  339. }
  340. else
  341. {
  342. }
  343. } else
  344. {
  345. }
  346. /* hand over to request who will be using it. */
  347. // r->dict = NULL;
  348. /* FIX!!!! we need more advanced handling of url's to avoid them
  349. * being subverted to evil purposes
  350. */
  351. const char *httpd_dir = PKGDATADIR "/httpd";
  352. if (*url=='/')
  353. {
  354. url++; /* skip '/' */
  355. }
  356. if (!*url)
  357. url="index.tcl";
  358. const char *file_name = alloc_printf("%s/%s", httpd_dir, url);
  359. int result = handle_request(connection, file_name);
  360. free((void *)file_name);
  361. return result;
  362. }
  363. static int ahc_echo(void * cls, struct MHD_Connection * connection,
  364. const char * url, const char * method, const char * version,
  365. const char * upload_data, size_t * upload_data_size, void ** ptr)
  366. {
  367. int result;
  368. openocd_sleep_postlude();
  369. result = ahc_echo_inner(cls, connection, url, method, version, upload_data, upload_data_size, ptr);
  370. openocd_sleep_prelude();
  371. return result;
  372. }
  373. static struct MHD_Daemon * d;
  374. static const struct command_registration httpd_command_handlers[] = {
  375. {
  376. .name = "formfetch",
  377. .jim_handler = &httpd_Jim_Command_formfetch,
  378. .mode = COMMAND_EXEC,
  379. .usage = "<parameter_name>",
  380. .help = "Reads a posted form value.",
  381. },
  382. {
  383. .name = "writeform",
  384. .jim_handler = &httpd_Jim_Command_writeform,
  385. .mode = COMMAND_EXEC,
  386. .usage = "<parameter_name> <file>",
  387. .help = "Writes a form value to a file.",
  388. },
  389. COMMAND_REGISTRATION_DONE
  390. };
  391. int httpd_start(struct command_context *cmd_ctx)
  392. {
  393. pthread_mutexattr_t attr;
  394. pthread_mutexattr_init(&attr);
  395. pthread_mutex_init(&mutex, &attr);
  396. int port = 8888;
  397. LOG_USER("Launching httpd server on port %d", port);
  398. d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
  399. &ahc_echo, NULL, /* could be data for handler, but we only have a single handler, use global variables instead */
  400. MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */
  401. MHD_OPTION_END);
  402. if (d == NULL)
  403. return ERROR_FAIL;
  404. return register_commands(cmd_ctx, NULL, httpd_command_handlers);
  405. }
  406. void httpd_stop(void)
  407. {
  408. MHD_stop_daemon(d);
  409. pthread_mutex_destroy(&mutex);
  410. }