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

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)