From: oharboe Date: Tue, 16 Dec 2008 22:20:10 +0000 (+0000) Subject: work in progress to hook up libmicrohttpd + tcl integration X-Git-Tag: v0.1.0~91 X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=commitdiff_plain;h=0fc19255c365a59acf4683d177bacf662e84d4f1 work in progress to hook up libmicrohttpd + tcl integration git-svn-id: svn://svn.berlios.de/openocd/trunk@1251 b42882b7-edfa-0310-969c-e2dbd0fdcd60 --- diff --git a/configure.in b/configure.in index 8889db4cac..642be2ede8 100644 --- a/configure.in +++ b/configure.in @@ -71,6 +71,10 @@ AC_ARG_ENABLE(ioutil, AS_HELP_STRING([--enable-ioutil], [Enable ioutil functions - useful for standalone OpenOCD implementations]), [build_ioutil=$enableval], [build_ioutil=no]) +AC_ARG_ENABLE(httpd, + AS_HELP_STRING([--enable-httpd], [Enable builtin httpd server - useful for standalone OpenOCD implementations]), + [build_httpd=$enableval], [build_httpd=no]) + case "${host_cpu}" in arm*) AC_ARG_ENABLE(ep93xx, @@ -189,6 +193,12 @@ else AC_DEFINE(BUILD_IOUTIL, 0, [0 if you don't want ioutils.]) fi +if test $build_httpd = yes; then + AC_DEFINE(BUILD_HTTPD, 1, [1 if you want httpd.]) +else + AC_DEFINE(BUILD_HTTPD, 0, [0 if you don't want httpd.]) +fi + if test $build_at91rm9200 = yes; then build_bitbang=yes AC_DEFINE(BUILD_AT91RM9200, 1, [1 if you want at91rm9200.]) @@ -285,6 +295,7 @@ AM_CONDITIONAL(GIVEIO, test $parport_use_giveio = yes) AM_CONDITIONAL(EP93XX, test $build_ep93xx = yes) AM_CONDITIONAL(ECOSBOARD, test $build_ecosboard = yes) AM_CONDITIONAL(IOUTIL, test $build_ioutil = yes) +AM_CONDITIONAL(HTTPD, test $build_httpd = yes) AM_CONDITIONAL(AT91RM9200, test $build_at91rm9200 = yes) AM_CONDITIONAL(BITBANG, test $build_bitbang = yes) AM_CONDITIONAL(FT2232_LIBFTDI, test $build_ft2232_libftdi = yes) diff --git a/src/Makefile.am b/src/Makefile.am index 669720f9e6..7574e84aa3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,6 +79,13 @@ openocd_LDADD = $(top_builddir)/src/xsvf/libxsvf.a \ $(top_builddir)/src/pld/libpld.a \ $(FTDI2232LIB) $(FTD2XXLIB) $(MINGWLDADD) $(LIBUSB) + +if HTTPD +openocd_LDADD += -lmicrohttpd +endif + + + nobase_dist_pkglib_DATA = \ tcl/bitsbytes.tcl \ tcl/chip/atmel/at91/aic.tcl \ diff --git a/src/main.c b/src/main.c index b7be8970b8..c2f30035f3 100644 --- a/src/main.c +++ b/src/main.c @@ -18,6 +18,12 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if !BUILD_HTTPD /* implementations of OpenOCD that uses multithreading needs to know when * OpenOCD is sleeping. No-op in vanilla OpenOCD */ @@ -28,13 +34,14 @@ void openocd_sleep_prelude(void) void openocd_sleep_postlude(void) { } +#endif /* This is the main entry for developer PC hosted OpenOCD. * * OpenOCD can also be used as a library that is linked with * another application(not mainstream yet, but possible), e.g. * w/as an embedded application. - * + * * Those applications will have their own main() implementation * and use bits and pieces from openocd.c. */ diff --git a/src/openocd.c b/src/openocd.c index 014433c76f..471a097a4b 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -229,6 +229,9 @@ command_context_t *setup_command_handler(void) return cmd_ctx; } +int httpd_start(void); +void httpd_stop(void); + /* normally this is the main() function entry, but if OpenOCD is linked * into application, then this fn will not be invoked, but rather that * application will have it's own implementation of main(). */ @@ -255,6 +258,11 @@ int openocd_main(int argc, char *argv[]) if ( (ret != ERROR_OK) && (ret != ERROR_COMMAND_CLOSE_CONNECTION) ) return EXIT_FAILURE; +#if BUILD_HTTPD + if (httpd_start()!=ERROR_OK) + return EXIT_FAILURE; +#endif + if (ret != ERROR_COMMAND_CLOSE_CONNECTION) { command_context_mode(cmd_ctx, COMMAND_EXEC); @@ -268,10 +276,15 @@ int openocd_main(int argc, char *argv[]) /* shut server down */ server_quit(); +#if BUILD_HTTPD + httpd_stop(); +#endif + unregister_all_commands(cmd_ctx); /* free commandline interface */ command_done(cmd_ctx); + return EXIT_SUCCESS; } diff --git a/src/server/Makefile.am b/src/server/Makefile.am index 63bcc04300..0e3a3a4790 100644 --- a/src/server/Makefile.am +++ b/src/server/Makefile.am @@ -4,6 +4,11 @@ noinst_LIBRARIES = libserver.a noinst_HEADERS = server.h telnet_server.h gdb_server.h libserver_a_SOURCES = server.c telnet_server.c gdb_server.c +if HTTPD +libserver_a_SOURCES += httpd.c +endif + + # tcl server addons noinst_HEADERS += tcl_server.h libserver_a_SOURCES += tcl_server.c diff --git a/src/server/httpd.c b/src/server/httpd.c new file mode 100644 index 0000000000..f24e58ade8 --- /dev/null +++ b/src/server/httpd.c @@ -0,0 +1,455 @@ +/*************************************************************************** + * Copyright (C) 2007,2008 Øyvind Harboe * + * oyvind.harboe@zylin.com * + * * + * Copyright (C) 2008 Free Software Foundation + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* some bits were copied from ahttpd which is under eCos license and + * copyright to FSF + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "replacements.h" + +#include "server.h" + +#include "log.h" +#include "telnet_server.h" +#include "target.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_NOT_FOUND "File not foundFile not found" + +static const char *appendf(const *prev, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *string = alloc_vprintf(format, ap); + va_end(ap); + char *string2 = NULL; + + if (string != NULL) + { + string2 = alloc_printf("%s%s", (prev == NULL) ? "" : prev, string); + } + + if (prev != NULL) + { + free(prev); + } + + if (string == NULL) + free(string); + + return string2; +} + +static const char *httpd_exec_cgi_tcl_error(Jim_Interp *interp) +{ + int len, i; + + const char *t = NULL; + t = appendf(t, "\n"); + + t = appendf(t, "Runtime error, file \"%s\", line %d:
", + interp->errorFileName, interp->errorLine); + t = appendf(t, " %s
", Jim_GetString(interp->result, NULL)); + Jim_ListLength(interp, interp->stackTrace, &len); + for (i = 0; i < len; i += 3) + { + Jim_Obj *objPtr; + const char *proc, *file, *line; + + Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE); + proc = Jim_GetString(objPtr, NULL); + Jim_ListIndex(interp, interp->stackTrace, i + 1, &objPtr, JIM_NONE); + file = Jim_GetString(objPtr, NULL); + Jim_ListIndex(interp, interp->stackTrace, i + 2, &objPtr, JIM_NONE); + line = Jim_GetString(objPtr, NULL); + t = appendf(t, "In procedure '%s' called at file \"%s\", line %s
", + proc, file, line); + } + t = appendf(t, "\n"); + + return t; +} + +static int httpd_Jim_Command_writeform(Jim_Interp *interp, int argc, + Jim_Obj * const *argv) +{ + if (argc != 3) + { + Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?"); + return JIM_ERR; + } + char *name = (char*) Jim_GetString(argv[1], NULL); + char *file = (char*) Jim_GetString(argv[2], NULL); + + // Find length + char *data; + int actual; + + int retcode; + + const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val", + name); + retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ ); + free((void *) script); + if (retcode != JIM_OK) + return retcode; + + data = Jim_GetString(Jim_GetResult(interp), &actual); + + FILE *f; + f = fopen(file, "wb"); + if (f != NULL) + { + int ok; + ok = fwrite(data, 1, actual, f) == actual; + fclose(f); + + if (!ok) + { + Jim_SetResultString(interp, "Could not write to file", -1); + return JIM_ERR; + } + } + else + { + Jim_SetResultString(interp, "Could not create file", -1); + return JIM_ERR; + } + return JIM_OK; +} + + +int +httpd_Jim_Command_formfetch(Jim_Interp *interp, + int argc, + Jim_Obj *const *argv) +{ + if (argc!=2) + { + Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?"); + return JIM_ERR; + } + char *name = (char*)Jim_GetString(argv[1], NULL); + + + const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val", + name); + int retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ ); + free((void *) script); + if (retcode != JIM_OK) + return retcode; + + Jim_SetResult(interp, Jim_GetResult(interp)); + return JIM_OK; +} + +struct httpd_request +{ + int post; + struct MHD_PostProcessor *postprocessor; + + //Jim_Obj *dict; + + int complete; /* did we receive the entire post ? */ + +}; + +static void request_completed(void *cls, struct MHD_Connection *connection, + void **con_cls, enum MHD_RequestTerminationCode toe) +{ + struct httpd_request *r = (struct httpd_request*) *con_cls; + + if (NULL == r) + return; + + if (r->postprocessor) + { + MHD_destroy_post_processor(r->postprocessor); + } + + free(r); + *con_cls = NULL; +} + +/* append to said key in dictonary */ +static void append_key(struct httpd_request *r, const char *key, + const char *data, size_t off, size_t size) +{ + Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1); + Jim_Obj *value = NULL; + + Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0); + + if (dict!=NULL) + { + if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK) + { + value = NULL; + } + } + if (value == NULL) + value = Jim_NewStringObj(interp, "", -1); + + /* create a new object we append to and insert into this location */ + Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1); + Jim_AppendObj(interp, newObj, value); + Jim_AppendString(interp, newObj, data, size); + /* uhh... use name here of dictionary */ + Jim_SetDictKeysVector(interp, Jim_NewStringObj(interp, "httppostdata", -1), &keyObj, 1, newObj); +} + +/* append data to each key */ +static int iterate_post(void *con_cls, enum MHD_ValueKind kind, + const char *key, const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, size_t off, + size_t size) +{ + struct httpd_request *r = (struct httpd_request*) con_cls; + + append_key(r, key, data, off, size); + + return MHD_YES; +} + +static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key, + const char *value) +{ + struct httpd_request *r = (struct httpd_request*) cls; + append_key(r, key, value, 0, strlen(value)); + return MHD_YES; +} + +static int ahc_echo(void * cls, struct MHD_Connection * connection, + const char * url, const char * method, const char * version, + const char * upload_data, unsigned int * upload_data_size, void ** ptr) +{ + struct MHD_Response * response; + int ret; + + int post = 0; + + if (0 == strcmp(method, "POST")) + { + post = 1; + } + else if (0 == strcmp(method, "GET")) + { + } + else + { + return MHD_NO; /* unexpected method */ + } + + struct httpd_request *r; + if (*ptr == NULL) + { + /* The first time only the headers are valid, + do not respond in the first round... */ + + *ptr = malloc(sizeof(struct httpd_request)); + if (*ptr == NULL) + return MHD_NO; + memset(*ptr, 0, sizeof(struct httpd_request)); + + r = (struct httpd_request *) *ptr; + + r->post = post; + +// r->dict = Jim_NewDictObj(interp, NULL, 0); + + /* fill in url query strings in dictonary */ + MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, + record_arg, r); + + if (r->post) + { + r->postprocessor = MHD_create_post_processor(connection, 2048 + * 1024, iterate_post, r); + } + + return MHD_YES; + } + + r = (struct httpd_request *) *ptr; + + if (r->post) + { + /* consume post data */ + if (*upload_data_size) + { + MHD_post_process(r->postprocessor, upload_data, *upload_data_size); + *upload_data_size = 0; + return MHD_YES; + } + else + { + } + } else + { + } + + /* hand over to request who will be using it. */ + //Jim_SetGlobalVariableStr(interp, "httppostdata", Jim_GetVariableStr(interp, "httppostdata", 0)); + // r->dict = NULL; + + + /* FIX!!!! we need more advanced handling of url's to avoid them + * being subverted to evil purposes + */ + + url++; /* skip '/' */ + + const char *suffix; + suffix = strrchr(url, '.'); + if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0)) + { + printf("Run tcl %s\n", url); + + int retcode; + + const char *script = alloc_printf( + "global httpdata; source {%s}; set httpdata", url); + retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__ ); + free((void *) script); + + if (retcode == JIM_ERR) + { + printf("Tcl failed\n"); + const char *t = httpd_exec_cgi_tcl_error(interp); + if (t == NULL) + return MHD_NO; + + response = MHD_create_response_from_data(strlen(t), (void *) t, + MHD_YES, MHD_NO); + ret = MHD_queue_response(connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, response); + MHD_destroy_response(response); + return ret; + } + else + { + printf("Tcl OK\n"); + /* FIX!!! how to handle mime types??? */ + const char *result; + int reslen; + result = Jim_GetString(Jim_GetResult(interp), &reslen); + + response = MHD_create_response_from_data(reslen, (void *) result, + MHD_NO, MHD_YES); + ret = MHD_queue_response(connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, response); + MHD_destroy_response(response); + return ret; + } + } + else + { + void *data; + int len; + + int retval = loadFile(url, &data, &len); + if (retval != ERROR_OK) + { + printf("Did not find %s\n", url); + + response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND), + (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO); + ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); + return ret; + } + + printf("Serving %s length=%d\n", url, len); + /* serve file directly */ + response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO); + MHD_add_response_header(response, "Content-Type", "image/png"); + + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + //free(data); + return ret; + } +} + +static struct MHD_Daemon * d; + +int httpd_start(void) +{ + + int port = 8888; + LOG_USER("Launching httpd server on port %d", port); + d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL, + &ahc_echo, NULL, /* could be data for handler, but we only have a single handler, use global variables instead */ + MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */ + MHD_OPTION_END); + if (d == NULL) + return ERROR_FAIL; + + Jim_CreateCommand(interp, + "formfetch", + httpd_Jim_Command_formfetch, + NULL, + NULL); + + Jim_CreateCommand(interp, + "writeform", + httpd_Jim_Command_writeform, + NULL, + NULL); + + + return ERROR_OK; +} + +void httpd_stop(void) +{ + MHD_stop_daemon(d); +} + +void openocd_sleep_prelude(void) +{ +} + +void openocd_sleep_postlude(void) +{ +} +