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.
 
 
 
 
 
 

507 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/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(
  101. "set dummy_val $httppostdata(%s); set dummy_val",
  102. name);
  103. retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
  104. free((void *) script);
  105. if (retcode != JIM_OK)
  106. return retcode;
  107. data = Jim_GetString(Jim_GetResult(interp), &actual);
  108. FILE *f = fopen(file, "wb");
  109. if (NULL == f)
  110. {
  111. Jim_SetResultString(interp, "Could not create file", -1);
  112. return JIM_ERR;
  113. }
  114. int result = fwrite(data, 1, actual, f);
  115. fclose(f);
  116. if (result != actual)
  117. {
  118. Jim_SetResultString(interp, "Could not write to file", -1);
  119. return JIM_ERR;
  120. }
  121. return JIM_OK;
  122. }
  123. int
  124. httpd_Jim_Command_formfetch(Jim_Interp *interp,
  125. int argc,
  126. Jim_Obj *const *argv)
  127. {
  128. if (argc != 2)
  129. {
  130. Jim_WrongNumArgs(interp, 1, argv, "method ?CMD_ARGV ...?");
  131. return JIM_ERR;
  132. }
  133. char *name = (char*)Jim_GetString(argv[1], NULL);
  134. const char *script = alloc_printf(
  135. "set dummy_val $httppostdata(%s); set dummy_val",
  136. name);
  137. int retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
  138. free((void *) script);
  139. if (retcode != JIM_OK)
  140. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  141. else
  142. Jim_SetResult(interp, Jim_GetResult(interp));
  143. return JIM_OK;
  144. }
  145. struct httpd_request
  146. {
  147. int post;
  148. Jim_Interp *interp;
  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 dictionary */
  169. static void append_key(Jim_Interp *interp,
  170. struct httpd_request *r, const char *key,
  171. const char *data, size_t off, size_t size)
  172. {
  173. Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1);
  174. Jim_IncrRefCount(keyObj);
  175. Jim_Obj *value = NULL;
  176. Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0);
  177. if (dict != NULL)
  178. {
  179. if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK)
  180. {
  181. value = NULL;
  182. }
  183. else
  184. {
  185. Jim_IncrRefCount(value);
  186. }
  187. }
  188. if (value == NULL)
  189. {
  190. value = Jim_NewStringObj(interp, "", -1);
  191. Jim_IncrRefCount(value);
  192. }
  193. /* create a new object we append to and insert into this location */
  194. Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1);
  195. Jim_IncrRefCount(newObj);
  196. Jim_AppendObj(interp, newObj, value);
  197. Jim_AppendString(interp, newObj, data, size);
  198. /* uhh... use name here of dictionary */
  199. dict = Jim_NewStringObj(interp, "httppostdata", -1);
  200. Jim_IncrRefCount(dict);
  201. Jim_SetDictKeysVector(interp, dict, &keyObj, 1, newObj);
  202. Jim_DecrRefCount(interp, dict);
  203. Jim_DecrRefCount(interp, value);
  204. Jim_DecrRefCount(interp, newObj);
  205. Jim_DecrRefCount(interp, keyObj);
  206. }
  207. /* append data to each key */
  208. static int iterate_post(void *con_cls, enum MHD_ValueKind kind,
  209. const char *key, const char *filename, const char *content_type,
  210. const char *transfer_encoding, const char *data, uint64_t off,
  211. size_t size)
  212. {
  213. struct httpd_request *r = (struct httpd_request*) con_cls;
  214. append_key(r->interp, r, key, data, off, size);
  215. return MHD_YES;
  216. }
  217. static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key,
  218. const char *value)
  219. {
  220. struct httpd_request *r = (struct httpd_request*) cls;
  221. append_key(r->interp, r, key, value, 0, strlen(value));
  222. return MHD_YES;
  223. }
  224. static int handle_request(Jim_Interp *interp,
  225. struct MHD_Connection * connection, const char * url)
  226. {
  227. struct MHD_Response * response;
  228. int ret;
  229. const char *suffix;
  230. suffix = strrchr(url, '.');
  231. if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0))
  232. {
  233. printf("Run tcl %s\n", url);
  234. int retcode;
  235. const char *script = alloc_printf(
  236. "global httpdata; source {%s}; set httpdata", url);
  237. retcode = Jim_Eval_Named(interp, script, __FILE__, __LINE__);
  238. free((void *) script);
  239. if (retcode != JIM_OK)
  240. {
  241. printf("Tcl failed\n");
  242. const char *t = httpd_exec_cgi_tcl_error(interp);
  243. if (t == NULL)
  244. return MHD_NO;
  245. response = MHD_create_response_from_data(strlen(t), (void *) t,
  246. MHD_YES, MHD_NO);
  247. ret = MHD_queue_response(connection,
  248. MHD_HTTP_INTERNAL_SERVER_ERROR, response);
  249. MHD_destroy_response(response);
  250. return ret;
  251. }
  252. else
  253. {
  254. LOG_DEBUG("Tcl OK");
  255. /* FIX!!! how to handle mime types??? */
  256. const char *result;
  257. int reslen;
  258. result = Jim_GetString(Jim_GetResult(interp), &reslen);
  259. response = MHD_create_response_from_data(reslen, (void *) result,
  260. MHD_NO, MHD_YES);
  261. ret = MHD_queue_response(connection,
  262. MHD_HTTP_INTERNAL_SERVER_ERROR, response);
  263. MHD_destroy_response(response);
  264. return ret;
  265. }
  266. }
  267. else
  268. {
  269. void *data;
  270. size_t len;
  271. int retval = loadFile(url, &data, &len);
  272. if (retval != ERROR_OK)
  273. {
  274. printf("Did not find %s\n", url);
  275. response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND),
  276. (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO);
  277. ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
  278. MHD_destroy_response(response);
  279. return ret;
  280. }
  281. LOG_DEBUG("Serving %s length=%zu", url, len);
  282. /* serve file directly */
  283. response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
  284. /* Should we expose mimetype via tcl here or just let the browser
  285. guess?
  286. MHD_add_response_header(response, "Content-Type", "image/png");
  287. */
  288. ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
  289. MHD_destroy_response(response);
  290. //free(data);
  291. return ret;
  292. }
  293. }
  294. static int ahc_echo_inner(void * cls, struct MHD_Connection * connection,
  295. const char * url, const char * method, const char * version,
  296. const char * upload_data, size_t * upload_data_size, void ** ptr)
  297. {
  298. Jim_Interp *interp = (Jim_Interp *)cls;
  299. int post = 0;
  300. if (0 == strcmp(method, "POST"))
  301. {
  302. post = 1;
  303. }
  304. else if (0 == strcmp(method, "GET"))
  305. {
  306. }
  307. else
  308. {
  309. return MHD_NO; /* unexpected method */
  310. }
  311. struct httpd_request *r;
  312. if (*ptr == NULL)
  313. {
  314. /* The first time only the headers are valid,
  315. do not respond in the first round... */
  316. *ptr = malloc(sizeof(struct httpd_request));
  317. if (*ptr == NULL)
  318. return MHD_NO;
  319. memset(*ptr, 0, sizeof(struct httpd_request));
  320. r = (struct httpd_request *) *ptr;
  321. r->interp = interp;
  322. r->post = post;
  323. Jim_SetVariableStr(interp, "httppostdata", Jim_NewDictObj(interp, NULL, 0));
  324. /* fill in url query strings in dictionary */
  325. MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND,
  326. record_arg, r);
  327. if (r->post)
  328. {
  329. r->postprocessor = MHD_create_post_processor(connection, 2048
  330. * 1024, &iterate_post, r);
  331. }
  332. return MHD_YES;
  333. }
  334. r = (struct httpd_request *) *ptr;
  335. if (r->post)
  336. {
  337. /* consume post data */
  338. if (*upload_data_size)
  339. {
  340. MHD_post_process(r->postprocessor, upload_data, *upload_data_size);
  341. *upload_data_size = 0;
  342. return MHD_YES;
  343. }
  344. else
  345. {
  346. }
  347. } else
  348. {
  349. }
  350. /* hand over to request who will be using it. */
  351. // r->dict = NULL;
  352. /* FIX!!!! we need more advanced handling of url's to avoid them
  353. * being subverted to evil purposes
  354. */
  355. const char *httpd_dir = PKGDATADIR "/httpd";
  356. if (*url=='/')
  357. {
  358. url++; /* skip '/' */
  359. }
  360. if (!*url)
  361. url="index.tcl";
  362. const char *file_name = alloc_printf("%s/%s", httpd_dir, url);
  363. int result = handle_request(interp, connection, file_name);
  364. free((void *)file_name);
  365. return result;
  366. }
  367. static int ahc_echo(void * cls, struct MHD_Connection * connection,
  368. const char * url, const char * method, const char * version,
  369. const char * upload_data, size_t * upload_data_size, void ** ptr)
  370. {
  371. int result;
  372. openocd_sleep_postlude();
  373. result = ahc_echo_inner(cls, connection, url, method, version, upload_data, upload_data_size, ptr);
  374. openocd_sleep_prelude();
  375. return result;
  376. }
  377. static struct MHD_Daemon * d;
  378. static const struct command_registration httpd_command_handlers[] = {
  379. {
  380. .name = "formfetch",
  381. .jim_handler = httpd_Jim_Command_formfetch,
  382. .mode = COMMAND_EXEC,
  383. .usage = "parameter_name",
  384. .help = "Reads a posted form value.",
  385. },
  386. {
  387. .name = "writeform",
  388. .jim_handler = httpd_Jim_Command_writeform,
  389. .mode = COMMAND_EXEC,
  390. .usage = "parameter_name filename",
  391. .help = "Writes a form value to a file.",
  392. },
  393. COMMAND_REGISTRATION_DONE
  394. };
  395. int httpd_start(struct command_context *cmd_ctx)
  396. {
  397. pthread_mutexattr_t attr;
  398. pthread_mutexattr_init(&attr);
  399. pthread_mutex_init(&mutex, &attr);
  400. int port = 8888;
  401. LOG_USER("Launching httpd server on port %d", port);
  402. d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
  403. &ahc_echo, cmd_ctx->interp,
  404. MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */
  405. MHD_OPTION_END);
  406. if (d == NULL)
  407. return ERROR_FAIL;
  408. return register_commands(cmd_ctx, NULL, httpd_command_handlers);
  409. }
  410. void httpd_stop(void)
  411. {
  412. MHD_stop_daemon(d);
  413. pthread_mutex_destroy(&mutex);
  414. }