httpd smoketest info
[openocd.git] / src / server / httpd.c
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
21 /* some bits were copied from ahttpd which is under eCos license and
22 * copyright to FSF
23 */
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "telnet_server.h"
29 #include "target.h"
30
31 #include <microhttpd.h>
32 #include <pthread.h>
33 #include <signal.h>
34
35 #define PAGE_NOT_FOUND "<html><head><title > File not found</title></head><body > File not found</body></html>"
36
37 int loadFile(const char *name, void **data, size_t *len);
38
39 static const char *appendf(const char *prev, const char *format, ...)
40 {
41 va_list ap;
42 va_start(ap, format);
43 char *string = alloc_vprintf(format, ap);
44 va_end(ap);
45 char *string2 = NULL;
46
47 if (string != NULL)
48 {
49 string2 = alloc_printf("%s%s", (prev == NULL) ? "" : prev, string);
50 }
51
52 if (prev != NULL)
53 {
54 free((void *)prev);
55 }
56
57 if (string == NULL)
58 free(string);
59
60 return string2;
61 }
62
63 static const char *httpd_exec_cgi_tcl_error(Jim_Interp *interp)
64 {
65 int len, i;
66
67 const char *t = NULL;
68 t = appendf(t, "<html><body>\n");
69
70 t = appendf(t, "Runtime error, file \"%s\", line %d:<br>",
71 interp->errorFileName, interp->errorLine);
72 t = appendf(t, " %s < br>", Jim_GetString(interp->result, NULL));
73 Jim_ListLength(interp, interp->stackTrace, &len);
74 for (i = 0; i < len; i += 3)
75 {
76 Jim_Obj *objPtr;
77 const char *proc, *file, *line;
78
79 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
80 proc = Jim_GetString(objPtr, NULL);
81 Jim_ListIndex(interp, interp->stackTrace, i + 1, &objPtr, JIM_NONE);
82 file = Jim_GetString(objPtr, NULL);
83 Jim_ListIndex(interp, interp->stackTrace, i + 2, &objPtr, JIM_NONE);
84 line = Jim_GetString(objPtr, NULL);
85 t = appendf(t, "In procedure '%s' called at file \"%s\", line %s < br>",
86 proc, file, line);
87 }
88 t = appendf(t, "</html></body>\n");
89
90 return t;
91 }
92
93 static int httpd_Jim_Command_writeform(Jim_Interp *interp, int argc,
94 Jim_Obj * const *argv)
95 {
96 if (argc != 3)
97 {
98 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
99 return JIM_ERR;
100 }
101 char *name = (char*) Jim_GetString(argv[1], NULL);
102 char *file = (char*) Jim_GetString(argv[2], NULL);
103
104 // Find length
105 const char *data;
106 int actual;
107
108 int retcode;
109
110 const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
111 name);
112 retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__);
113 free((void *) script);
114 if (retcode != JIM_OK)
115 return retcode;
116
117 data = Jim_GetString(Jim_GetResult(interp), &actual);
118
119 FILE *f = fopen(file, "wb");
120 if (NULL == f)
121 {
122 Jim_SetResultString(interp, "Could not create file", -1);
123 return JIM_ERR;
124 }
125
126 int result = fwrite(data, 1, actual, f);
127 fclose(f);
128
129 if (result != actual)
130 {
131 Jim_SetResultString(interp, "Could not write to file", -1);
132 return JIM_ERR;
133 }
134 return JIM_OK;
135 }
136
137
138 int
139 httpd_Jim_Command_formfetch(Jim_Interp *interp,
140 int argc,
141 Jim_Obj *const *argv)
142 {
143 if (argc != 2)
144 {
145 Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
146 return JIM_ERR;
147 }
148 char *name = (char*)Jim_GetString(argv[1], NULL);
149
150
151 const char *script = alloc_printf("set dummy_val $httppostdata(%s); set dummy_val",
152 name);
153 int retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__);
154 free((void *) script);
155 if (retcode != JIM_OK)
156 {
157 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
158 } else
159 {
160 Jim_SetResult(interp, Jim_GetResult(interp));
161 }
162
163 return JIM_OK;
164 }
165
166 struct httpd_request
167 {
168 int post;
169 struct MHD_PostProcessor *postprocessor;
170
171 //Jim_Obj *dict;
172
173 int complete; /* did we receive the entire post ? */
174
175 };
176
177 static void request_completed(void *cls, struct MHD_Connection *connection,
178 void **con_cls, enum MHD_RequestTerminationCode toe)
179 {
180 struct httpd_request *r = (struct httpd_request*) *con_cls;
181
182 if (NULL == r)
183 return;
184
185 if (r->postprocessor)
186 {
187 MHD_destroy_post_processor(r->postprocessor);
188 }
189
190 free(r);
191 *con_cls = NULL;
192 }
193
194 /* append to said key in dictonary */
195 static void append_key(struct httpd_request *r, const char *key,
196 const char *data, size_t off, size_t size)
197 {
198 Jim_Obj *keyObj = Jim_NewStringObj(interp, key, -1);
199 Jim_IncrRefCount(keyObj);
200 Jim_Obj *value = NULL;
201
202 Jim_Obj *dict = Jim_GetVariableStr(interp, "httppostdata", 0);
203
204 if (dict != NULL)
205 {
206 if (Jim_DictKey(interp, dict, keyObj, &value, 0) != JIM_OK)
207 {
208 value = NULL;
209 }
210 else
211 {
212 Jim_IncrRefCount(value);
213 }
214 }
215
216 if (value == NULL)
217 {
218 value = Jim_NewStringObj(interp, "", -1);
219 Jim_IncrRefCount(value);
220
221 }
222
223 /* create a new object we append to and insert into this location */
224 Jim_Obj *newObj = Jim_NewStringObj(interp, "", -1);
225 Jim_IncrRefCount(newObj);
226 Jim_AppendObj(interp, newObj, value);
227 Jim_AppendString(interp, newObj, data, size);
228 /* uhh... use name here of dictionary */
229 dict = Jim_NewStringObj(interp, "httppostdata", -1);
230 Jim_IncrRefCount(dict);
231 Jim_SetDictKeysVector(interp, dict, &keyObj, 1, newObj);
232 Jim_DecrRefCount(interp, dict);
233 Jim_DecrRefCount(interp, value);
234 Jim_DecrRefCount(interp, newObj);
235 Jim_DecrRefCount(interp, keyObj);
236 }
237
238 /* append data to each key */
239 static int iterate_post(void *con_cls, enum MHD_ValueKind kind,
240 const char *key, const char *filename, const char *content_type,
241 const char *transfer_encoding, const char *data, uint64_t off,
242 size_t size)
243 {
244 struct httpd_request *r = (struct httpd_request*) con_cls;
245
246 append_key(r, key, data, off, size);
247
248 return MHD_YES;
249 }
250
251 static int record_arg(void *cls, enum MHD_ValueKind kind, const char *key,
252 const char *value)
253 {
254 struct httpd_request *r = (struct httpd_request*) cls;
255 append_key(r, key, value, 0, strlen(value));
256 return MHD_YES;
257 }
258
259
260 int handle_request(struct MHD_Connection * connection, const char * url)
261 {
262 struct MHD_Response * response;
263
264 int ret;
265 const char *suffix;
266 suffix = strrchr(url, '.');
267 if ((suffix != NULL) && (strcmp(suffix, ".tcl") == 0))
268 {
269 printf("Run tcl %s\n", url);
270
271 int retcode;
272
273 const char *script = alloc_printf(
274 "global httpdata; source {%s}; set httpdata", url);
275 retcode = Jim_Eval_Named(interp, script, "httpd.c", __LINE__);
276 free((void *) script);
277
278 if (retcode == JIM_ERR)
279 {
280 printf("Tcl failed\n");
281 const char *t = httpd_exec_cgi_tcl_error(interp);
282 if (t == NULL)
283 return MHD_NO;
284
285 response = MHD_create_response_from_data(strlen(t), (void *) t,
286 MHD_YES, MHD_NO);
287 ret = MHD_queue_response(connection,
288 MHD_HTTP_INTERNAL_SERVER_ERROR, response);
289 MHD_destroy_response(response);
290 return ret;
291 }
292 else
293 {
294 LOG_DEBUG("Tcl OK");
295 /* FIX!!! how to handle mime types??? */
296 const char *result;
297 int reslen;
298 result = Jim_GetString(Jim_GetResult(interp), &reslen);
299
300 response = MHD_create_response_from_data(reslen, (void *) result,
301 MHD_NO, MHD_YES);
302 ret = MHD_queue_response(connection,
303 MHD_HTTP_INTERNAL_SERVER_ERROR, response);
304 MHD_destroy_response(response);
305 return ret;
306 }
307 }
308 else
309 {
310 void *data;
311 size_t len;
312
313 int retval = loadFile(url, &data, &len);
314 if (retval != ERROR_OK)
315 {
316 printf("Did not find %s\n", url);
317
318 response = MHD_create_response_from_data(strlen(PAGE_NOT_FOUND),
319 (void *) PAGE_NOT_FOUND, MHD_NO, MHD_NO);
320 ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
321 MHD_destroy_response(response);
322 return ret;
323 }
324
325 LOG_DEBUG("Serving %s length=%zu", url, len);
326 /* serve file directly */
327 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
328 MHD_add_response_header(response, "Content-Type", "image/png");
329
330 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
331 MHD_destroy_response(response);
332
333 //free(data);
334 return ret;
335 }
336 }
337
338 static int ahc_echo(void * cls, struct MHD_Connection * connection,
339 const char * url, const char * method, const char * version,
340 const char * upload_data, unsigned int * upload_data_size, void ** ptr)
341 {
342 int post = 0;
343
344 if (0 == strcmp(method, "POST"))
345 {
346 post = 1;
347 }
348 else if (0 == strcmp(method, "GET"))
349 {
350 }
351 else
352 {
353 return MHD_NO; /* unexpected method */
354 }
355
356 struct httpd_request *r;
357 if (*ptr == NULL)
358 {
359 /* The first time only the headers are valid,
360 do not respond in the first round... */
361
362 *ptr = malloc(sizeof(struct httpd_request));
363 if (*ptr == NULL)
364 return MHD_NO;
365 memset(*ptr, 0, sizeof(struct httpd_request));
366
367 r = (struct httpd_request *) *ptr;
368
369 r->post = post;
370 Jim_SetVariableStr(interp, "httppostdata", Jim_NewDictObj(interp, NULL, 0));
371
372 /* fill in url query strings in dictonary */
373 MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND,
374 record_arg, r);
375
376 if (r->post)
377 {
378 r->postprocessor = MHD_create_post_processor(connection, 2048
379 * 1024, &iterate_post, r);
380 }
381
382 return MHD_YES;
383 }
384
385 r = (struct httpd_request *) *ptr;
386
387 if (r->post)
388 {
389 /* consume post data */
390 if (*upload_data_size)
391 {
392 MHD_post_process(r->postprocessor, upload_data, *upload_data_size);
393 *upload_data_size = 0;
394 return MHD_YES;
395 }
396 else
397 {
398 }
399 } else
400 {
401 }
402
403 /* hand over to request who will be using it. */
404 // r->dict = NULL;
405
406
407 /* FIX!!!! we need more advanced handling of url's to avoid them
408 * being subverted to evil purposes
409 */
410
411 const char *httpd_dir = PKGDATADIR "/httpd";
412
413 if (*url=='/')
414 {
415 url++; /* skip '/' */
416 }
417 if (!*url)
418 url="index.tcl";
419
420 const char *file_name = alloc_printf("%s/%s", httpd_dir, url);
421 int result = handle_request(connection, file_name);
422 free((void *)file_name);
423 return result;
424 }
425
426 static struct MHD_Daemon * d;
427 static pthread_mutex_t mutex;
428
429
430 int httpd_start(void)
431 {
432 pthread_mutexattr_t attr;
433 pthread_mutexattr_init(&attr);
434 pthread_mutex_init(&mutex, &attr);
435
436 int port = 8888;
437 LOG_USER("Launching httpd server on port %d", port);
438 d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
439 &ahc_echo, NULL, /* could be data for handler, but we only have a single handler, use global variables instead */
440 MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL, /* Closure... what's that??? */
441 MHD_OPTION_END);
442 if (d == NULL)
443 return ERROR_FAIL;
444
445 Jim_CreateCommand(interp,
446 "formfetch",
447 httpd_Jim_Command_formfetch,
448 NULL,
449 NULL);
450
451 Jim_CreateCommand(interp,
452 "writeform",
453 httpd_Jim_Command_writeform,
454 NULL,
455 NULL);
456
457
458 return ERROR_OK;
459 }
460
461 void httpd_stop(void)
462 {
463 MHD_stop_daemon(d);
464 pthread_mutex_destroy(&mutex);
465 }
466
467 void openocd_sleep_prelude(void)
468 {
469 pthread_mutex_unlock(&mutex);
470 }
471
472 void openocd_sleep_postlude(void)
473 {
474 pthread_mutex_lock(&mutex);
475 }
476

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)