Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
emutools_umon.c
Go to the documentation of this file.
1 /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
2  Copyright (C)2017-2021 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
3 
4 !! NOTE: I AM NOT a windows programmer, not even a user ...
5 !! These are my best tries with winsock to be usable also on the win32/64 platform ...
6 
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20 
21 #ifdef HAVE_XEMU_UMON
22 
23 #include "xemu/emutools.h"
24 #include "xemu/emutools_umon.h"
26 #include <string.h>
27 #include <setjmp.h>
28 
29 
30 //#define UNCONNECTED XS_INVALID_SOCKET
31 static xemusock_socket_t sock_server;
32 
33 static SDL_atomic_t thread_counter;
34 static SDL_atomic_t thread_stop_trigger;
35 static jmp_buf jmp_finish_client_thread;
36 
37 #define END_CLIENT_THREAD(n) do { longjmp(jmp_finish_client_thread, n); XEMU_UNREACHABLE(); } while(0)
38 #define CHECK_STOP_TRIGGER() do { \
39  if (XEMU_UNLIKELY(SDL_AtomicGet(&thread_stop_trigger))) \
40  END_CLIENT_THREAD(1); \
41  } while(0)
42 
43 #if 0
44 
45 
46 #include <string.h>
47 #include <setjmp.h>
48 
49 static char _xemunet_strerror_buffer[1024];
50 
51 #ifndef XEMU_ARCH_WIN
52 
53 #include <unistd.h>
54 #include <sys/socket.h>
55 #include <sys/types.h>
56 #include <netdb.h>
57 #include <errno.h>
58 #define XEMUNET_INVALID_SOCKET -1
59 #define XEMUNET_SOCKET_ERROR -1
60 typedef int xemunet_socket_t;
61 #define xemunet_close_socket close
62 #define xemunet_errno errno
63 #define xemunet_perror perror
64 static const char *xemunet_strneterror ( void )
65 {
66  strcpy(_xemunet_strerror_buffer, strerror(errno));
67  return _xemunet_strerror_buffer;
68 }
69 
70 #else
71 
72 #include <winsock2.h>
73 #define XEMUNET_INVALID_SOCKET INVALID_SOCKET
74 #define XEMUNET_SOCKET_ERROR SOCKET_ERROR
75 typedef SOCKET xemunet_socket_t;
76 #define xemunet_close_socket closesocket
77 typedef int socklen_t;
78 #define SHUT_RD 0
79 #define SHUT_WR 1
80 #define SHUT_RDWR 2
81 #define xemunet_errno WSAGetLastError()
82 static void xemunet_perror ( const char *s )
83 {
84  fprintf(stderr, "%s: (WSA_CODE=%d)\n", s, WSAGetLastError());
85 }
86 static const char *xemunet_strneterror ( void )
87 {
88  sprintf(_xemunet_strerror_buffer, "(WSA_CODE=%d)", WSAGetLastError());
89  return _xemunet_strerror_buffer;
90 }
91 
92 #endif
93 
94 
95 volatile int xumon_is_running = 0;
96 static volatile int thread_stop_trigger;
97 static SDL_Thread *xumon_thread_id = NULL;
98 
99 static xemunet_socket_t server_sock = XEMUNET_INVALID_SOCKET;
100 static const char content_type_text[] = "text/plain; charset=utf-8";
101 static jmp_buf jmp_finish_thread;
102 
103 struct xumon_message_queue {
104  volatile int provider_pos;
105  volatile int consumer_pos;
106  volatile char buffer[MESSAGE_QUEUE_SIZE];
107  SDL_mutex *mutex;
108 };
109 
110 
111 #define XEMUNET_SELECT_R 1
112 #define XEMUNET_SELECT_W 2
113 #define XEMUNET_SELECT_E 4
114 
115 
116 static int xemunet_select_1 ( xemunet_socket_t sock, int msec, int what )
117 {
118  msec *= 1000;
119  for (;;) {
120  int ret;
121  struct timeval timeout;
122  fd_set fds_r, fds_w, fds_e;
123  FD_ZERO(&fds_r);
124  FD_ZERO(&fds_w);
125  FD_ZERO(&fds_e);
126  if (what & XEMUNET_SELECT_R)
127  FD_SET(sock, &fds_r);
128  if (what & XEMUNET_SELECT_W)
129  FD_SET(sock, &fds_w);
130  if (what & XEMUNET_SELECT_E)
131  FD_SET(sock, &fds_e);
132  timeout.tv_sec = 0;
133  timeout.tv_usec = msec;
134  ret = select(sock + 1, &fds_r, &fds_w, &fds_e, msec >= 0 ? &timeout : NULL);
135  if (ret == XEMUNET_SOCKET_ERROR) {
136  if (xemunet_errno == EINTR)
137  continue;
138  return -1;
139  }
140  if (ret == 0)
141  return 0;
142  return (FD_ISSET(sock, &fds_r) ? XEMUNET_SELECT_R : 0) | (FD_ISSET(sock, &fds_w) ? XEMUNET_SELECT_W : 0) | (FD_ISSET(sock, &fds_e) ? XEMUNET_SELECT_E : 0);
143  }
144 }
145 
146 
147 
148 
149 /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! *
150  * BEGIN CRITICAL PART: these part (BELOW) of the code runs in a *THREAD*. *
151  * That is, it's important what can do and what you MUST NOT do here ... *
152  * Even not so OK to call Xemu API related stuffs in general, only *
153  * if you considered to be safe (most of the Xemu API is not thread-safe, *
154  * this is even true for dialog boxes, file operations, etc ...) *
155  * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
156 
157 #define FINISH_THREAD(n) longjmp(jmp_finish_thread, n)
158 
159 
160 static void queue_push ( struct xumon_message_queue *queue, const void *data, int size )
161 {
162  int pos = queue->push_pos;
163  while (size > 0) {
164  while (pos == queue->pull_pos)
165  ; // burn CPU ...
166  queue->buffer[pos] = *data++;
167  pos = (pos + 1) & MESSAGE_QUEUE_SIZE_MASK;
168  }
169  queue->push_pos = pos;
170 }
171 
172 
173 
174 static int socket_send_raw ( xemunet_socket_t sock, const void *data, int size )
175 {
176  while (size > 0) {
177  int w;
178  if (thread_stop_trigger)
179  FINISH_THREAD(1);
180  w = send(sock, data, size, 0);
181  if (w < 0) {
182  if (xemunet_errno == EINTR)
183  continue;
184  return -1;
185  }
186  if (w == 0)
187  return -1;
188  data += w;
189  size -= w;
190  }
191  return 0;
192 }
193 
194 
195 static int socket_send_string ( xemunet_socket_t sock, const char *s )
196 {
197  return socket_send_raw(sock, s, strlen(s));
198 }
199 
200 
201 static int hexstr2num ( const char *s, int len )
202 {
203  int out = 0;
204  while (len) {
205  out <<= 4;
206  if (*s >= '0' && *s <= '9')
207  out |= *s - '0';
208  else if (*s >= 'A' && *s <= 'F')
209  out |= *s - 'A' + 10;
210  else if (*s >= 'a' && *s <= 'f')
211  out |= *s - 'a' + 10;
212  else
213  return -1;
214  s++;
215  len--;
216  }
217  return out;
218 }
219 
220 
221 
222 static void submit_monitor_command_from_client ( int sock, char *cmd, int is_uri_encoded )
223 {
224  if (is_uri_encoded) {
225  char *s, *t;
226  s = strstr(cmd, by_uri_part);
227  if (!s)
228  return;
229  s += strlen(by_uri_part);
230  t = s;
231  cmd = s;
232  for (;;) {
233  if (*s == 0 || *s == '?' || *s == '&' || *s == '#')
234  break;
235  else if (*s == '+') {
236  *t++ = ' ';
237  s++;
238  } else if (*cmd == '%') {
239  int h = hexstr2num(s + 1, 2);
240  if (h < 0)
241  return; // fatal error, unterminated % encoding ...
242  if (h < 32)
243  return; // control character in URI??
244  *t++ = h;
245  s += 3;
246  } else
247  *t++ = *s++;
248  }
249  *t = 0;
250  }
251  /* queue data for the main thread */
252  queue_push(&queue_from_client, cmd);
253  SDL_AtomicLock(&queue->lock);
254  SDL_AtomicUnlock(&queue->lock);
255 
256  int pos = queue_from_client.push_pos;
257  while (*cmd) {
258  while (pos == queue_from_client.pull_pos)
259  ;
260  queue_from_client.buffer[pos] = *cmd++;
261  pos = (pos + 1) & (MSG_QUEUE_SIZE - 1);
262  }
263  queue_from_client.push_pos = pos;
264 
265  queue_pos(&our_queue, cmd, strlen(cmd));
266  // LOCK command queue!
267  SDL_LockMutex(queue_input.mutex);
268 
269  if (strlen(cmd) + queue_input
270  strcpy(queue_input.provider_pos, cmd);
271  queue_input.provider_pos += strlen(cmd);
272  SDL_UnlockMutex(queue_input.mutex);
273 
274  socket_send_string(sock, "test-reply: you've entered this: \"");
275  socket_send_string(sock, cmd);
276  socket_send_string(sock, "\"\r\n");
277 }
278 
279 
280 #if 0
281 static int http_send_chunk ( xemunet_socket_t sock, const void *data, int size )
282 {
283  char buffer[32];
284  if (size) {
285  sprintf(buffer, "\r\n%X\r\n", size);
286  if (socket_send_raw(sock, buffer, strlen(buffer)))
287  return -1;
288  return socket_send_raw(sock, data, size);
289  } else {
290  return socket_send_raw(sock, "\r\n0\r\n\r\n", 7);
291  }
292 }
293 #endif
294 
295 
296 static int http_answer ( xemunet_socket_t sock, int error_code, const char *error_msg, int keep_alive, const char *ctype, const char *simple_answer )
297 {
298  char buffer[4096];
299  sprintf(buffer,
300  "HTTP/1.1 %d %s\r\n"
301  "Cache-Control: no-store, no-cache, must-revalidate, max-age=0\r\n"
302  "Cache-Control: post-check=0, pre-check=0\r\n"
303  "Pragma: no-cache\r\n"
304  "Expires: Tue, 19 Sep 2017 19:08:16 GMT\r\n"
305  "Content-Type: %s\r\n"
306  "Connection: %s\r\n"
307  "X-UA-Compatible: IE=edge\r\n"
308  "X-Powered-By: The Powerpuff Girls\r\n"
309  "X-Content-Type-Options: nosniff\r\n"
310  "Access-Control-Allow-Origin: *\r\n"
311  "Server: %s/%s\r\n",
312  error_code, error_msg,
313  ctype,
314  keep_alive ? "keep-alive" : "close",
315  "Xemu", "Version"
316  );
317  if (error_code == 405)
318  strcat(buffer, "Allow: GET\r\n");
319  if (simple_answer) {
320  sprintf(buffer + strlen(buffer), "Content-Length: %d\r\n\r\n", (int)strlen(simple_answer));
321  } else
322  strcat(buffer, "Transfer-Encoding: chunked\r\n");
323  if (socket_send_raw(sock, buffer, strlen(buffer)))
324  return -1;
325  if (simple_answer) {
326  if (socket_send_raw(sock, simple_answer, strlen(simple_answer)))
327  return -1;
328  }
329  return 0;
330 }
331 
332 
333 
334 
335 static void fancy_print ( const char *s )
336 {
337  printf("[");
338  while (*s) {
339  if (*s >= 32 && *s <= 127)
340  printf("%c", *s);
341  else
342  printf("<%d>", *s);
343  s++;
344  }
345  printf("]\n");
346 }
347 
348 
349 static int http_client_handler ( xemunet_socket_t sock, const char *method, const char *uri, const char *http_headers)
350 {
351  if (strcasecmp(method, "GET"))
352  return http_answer(sock, 405, "Method Not Allowed", 0, content_type_text, "This service only supports GET method!");
353  else {
354  char buffer[0x4000];
355  char *p = buffer + sprintf(buffer, "%s", "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=utf-8>\n<title>Xemu</title>\n</head>\n<body>\n<H1>XEMU</H1>\n<p>Wannabe XEMU web-debug ...</p>\n<pre>\n");
356  printf("http_headers = %s\n", http_headers);
357  if (http_headers)
358  while (*http_headers != 1) {
359  char c = *http_headers++;
360  if (c == '<')
361  p += sprintf(p, "%s", "&lt;");
362  else if (c == '>')
363  p += sprintf(p, "%s", "&gt;");
364  else if (c == 0) {
365  *p++ = '\n';
366  while (!*http_headers)
367  http_headers++;
368  } else
369  *p++ = c;
370  }
371  sprintf(p, "%s", "</pre></body></html>");
372  return http_answer(sock, 200, "Ok", 0, "text/html; charset=utf-8", buffer);
373  }
374  return 0;
375 }
376 
377 
378 
379 static int generic_client_handler ( xemunet_socket_t sock )
380 {
381  char buffer[8192];
382  int buffer_fill = 0; // current used buffer space (including the already not used space before buffer_start)
383  int buffer_start = 0; // acutal offset in buffer have the new data from
384  char line_ending = 0; // used in line-ending auto detection (last \r or \n at the next char, or zero if not the situation)
385  //int link_mode = 0; // 0=to-be-detected, 1=umon-std, 2=http
386  enum { DETECT_LINK, TEXT_LINK, HTTP_LINK } link_mode = DETECT_LINK;
387  char *http_uri, *http_method, *http_headers = NULL;
388  for (;;) {
389  int a;
390  if (buffer_start && link_mode == TEXT_LINK) {
391  // buffer space "normalization" is not allowed in http mode, as we need all the request in once, unlike in text mode
392  printf("memmove(buffer, buffer + %d, %d - %d);\n", buffer_start, buffer_fill, buffer_start);
393  memmove(buffer, buffer + buffer_start, buffer_fill - buffer_start);
394  buffer_fill -= buffer_start;
395  buffer_start = 0;
396  }
397  if (buffer_fill >= sizeof(buffer)) {
398  printf("Too long request ... aborting\n");
399  if (link_mode == HTTP_LINK)
400  http_answer(sock, 400, "Bad Request", 0, content_type_text, "Request is too large!");
401  else
402  socket_send_string(sock, "?TOO LONG LINE OR TOO MANY QUEUED REQUESTS\r\n");
403  return -1;
404  }
405  for (;;) {
406  // TODO: at this point, we may need to introduce non-blocking I/O or something (pre-write) to allow send back queued answer (especially in link_mode=TEXT)
407  // since this runs as a thread, the emulator can queue an answer in an async way but this read() will block ...
408  // Also: it's possible that we need to introduce timeout and fall back into TEXT_LINK mode if no info got, thus we can produce a greeter message on the
409  // monitor. If really needed ...
410  if (thread_stop_trigger)
411  FINISH_THREAD(1);
412  a = recv(sock, buffer + buffer_fill, sizeof(buffer) - buffer_fill, 0);
413  if (!a) {
414  printf("Connection closed by remote peer?\n");
415  return -1;
416  }
417  if (a > 0)
418  break;
419  if (xemunet_errno != EINTR) {
420  xemunet_perror("Connection failed at read()");
421  return -1;
422  }
423  }
424  buffer_fill += a;
425  a = buffer_start;
426  printf("BEFORE: buffer_start=%d buffer_fill=%d\n", buffer_start, buffer_fill);
427  while (a < buffer_fill) {
428  if (buffer[a] == '\r' || buffer[a] == '\n') {
429  if (line_ending && buffer[a] != line_ending) {
430  // the line_ending logic tries to catch all possible line endings, \n and \r and \n\r and \r\n too
431  printf("Ignoring extra byte: %d\n", buffer[a]);
432  buffer_start++;
433  line_ending = 0;
434  buffer[a] = 0;
435  a++;
436  } else {
437  char *line = buffer + buffer_start;
438  line_ending = buffer[a]; // set-up marker for line ending detection
439  buffer[a] = 0; // terminate the new line found
440  buffer_start = ++a; // prepare for the next line to parse
441  fancy_print(line);
442  if (link_mode == DETECT_LINK) {
443  char *p;
444  // No link mode is detected yet, try to detect HTTP first-line
445  http_uri = strchr(line, ' ');
446  p = http_uri ? strchr(http_uri + 1, ' ') : NULL;
447  if (http_uri && p && p - http_uri > 1 && http_uri[1] == '/' && !strncasecmp(p, " http/1.", 8)) {
448  *http_uri++ = 0;
449  *p = 0;
450  http_method = line;
451  http_headers = NULL;
452  link_mode = HTTP_LINK;
453  printf("HTTP mode detected! Method=\"%s\" URI=\"%s\"\n", http_method, http_uri);
454  } else {
455  link_mode = TEXT_LINK;
456  submit_monitor_command_from_client(sock, line, 0);
457  }
458  } else if (link_mode == TEXT_LINK) {
459  submit_monitor_command_from_client(sock, line, 0);
460  } else { // so link_mode is HTTP_LINK ...
461  if (!*line) {
462  // empty line terminates the HTTP request
463  *line = 1; // this odd stuff marks end of the http header for header parser functions
464  return http_client_handler(sock, http_method, http_uri, http_headers); // currently we don't support HTTP keep-alive, hence the return ...
465  } else if (!http_headers) {
466  http_headers = line;
467  printf("http_headers set to %s\n", http_headers);
468  }
469  }
470  }
471  } else {
472  line_ending = 0;
473  a++;
474  }
475  }
476  }
477  return 0;
478 }
479 
480 
481 
482 
483 static int xumon_main ( void *user_data_unused )
484 {
485  int ret_on;
486  xemunet_socket_t client_sock = XEMUNET_INVALID_SOCKET;
487  printf("UMON: linking thread is running\n");
488  ret_on = setjmp(jmp_finish_thread);
489  if (ret_on) {
490  printf("UMON: finish jump\n");
491  if (client_sock != XEMUNET_INVALID_SOCKET) {
492  printf("UMON: close client socket on stopping\n");
493  shutdown(client_sock, SHUT_RDWR);
494  xemunet_close_socket(client_sock);
495  }
496  } else {
497  printf("UMON: entering into the main accept() loop ...\n");
498  thread_stop_trigger = 0;
499  xumon_is_running = 1;
500  while (xumon_is_running) {
501  int ret;
502  struct sockaddr_in client_addr;
503  socklen_t addrlen = sizeof client_addr;
504  do {
505  int ret = xemunet_select_1(server_sock, 50, XEMUNET_SELECT_R);
506  if (thread_stop_trigger)
507  FINISH_THREAD(1);
508  } while (ret != 1);
509  client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &addrlen);
510  if (client_sock == XEMUNET_INVALID_SOCKET) {
511  if (xemunet_errno == EINTR)
512  continue;
513  xemunet_perror("Server: accept");
514  ret_on = -1;
515  break;
516  }
517  printf("Connection: connected\n");
518  generic_client_handler(client_sock);
519  printf("Connection: closing ...\n");
520  shutdown(client_sock, SHUT_RDWR);
521  if (xemunet_close_socket(client_sock) < 0) {
522  xemunet_perror("Server: close client connection");
523  }
524  client_sock = XEMUNET_INVALID_SOCKET;
525  }
526  printf("UMON: leaving main accept() loop ...\n");
527  }
528  printf("UMON: close server socket\n");
529  xemunet_close_socket(server_sock);
530  server_sock = XEMUNET_INVALID_SOCKET;
531  xumon_is_running = 0;
532  printf("UMON: good bye!\n");
533  return ret_on;
534 }
535 
536 
537 
538 #undef FINISH_THREAD
539 
540 #endif
541 
542 
543 static int recv_raw ( xemusock_socket_t sock, void *buffer, int min_size, int max_size )
544 {
545  int size = 0;
546  while (size < min_size && max_size > 0) {
547  CHECK_STOP_TRIGGER();
548  int xerr;
549  int ret = xemusock_select_1(sock, 100000, XEMUSOCK_SELECT_R, &xerr);
550  if (ret < 0) {
551  if (xerr == XSEINTR) {
552  DEBUGPRINT("UMON: client: recv_raw: select() got EINTR, restarting" NL);
553  continue;
554  }
555  DEBUGPRINT("UMON: client: recv_raw: select() returned with error: %s" NL, xemusock_strerror(xerr));
556  END_CLIENT_THREAD(1);
557  }
558  if (!ret)
559  continue;
560  ret = xemusock_recv(sock, buffer, max_size, &xerr);
561  if (ret == 0) {
562  // successfull receiving of zero bytes usually (?? FIXME ??) means socket has been closed
563  DEBUGPRINT("UMON: client: recv_raw: recv() returned with zero" NL);
564  END_CLIENT_THREAD(1);
565  }
566  if (ret < 0) {
567  if (xemusock_should_repeat_from_error(xerr)) {
568  DEBUGPRINT("UMON: client: recv_raw: recv() non-fatal error, restarting: %s" NL, xemusock_strerror(xerr));
569  continue;
570  }
571  DEBUGPRINT("UMON: client: recv_raw: recv() returned with error: %s" NL, xemusock_strerror(xerr));
572  END_CLIENT_THREAD(1);
573  }
574  buffer += ret;
575  size += ret;
576  max_size -= ret;
577  }
578  return size;
579 }
580 
581 
582 static void send_raw ( xemusock_socket_t sock, const void *buffer, int size )
583 {
584  while (size > 0) {
585  CHECK_STOP_TRIGGER();
586  int xerr;
587  int ret = xemusock_select_1(sock, 100000, XEMUSOCK_SELECT_W, &xerr);
588  if (ret < 0) {
589  if (xerr == XSEINTR) {
590  DEBUGPRINT("UMON: client: send_raw: select() got EINTR, restarting" NL);
591  continue;
592  }
593  DEBUGPRINT("UMON: client: send_raw: select() returned with error: %s" NL, xemusock_strerror(xerr));
594  END_CLIENT_THREAD(1);
595  }
596  if (!ret)
597  continue;
598  ret = xemusock_send(sock, buffer, size, &xerr);
599  if (ret == 0) {
600  // successfull sending of zero bytes usually (?? FIXME ??) means socket has been closed
601  DEBUGPRINT("UMON: client: send_raw: send() returned with zero" NL);
602  END_CLIENT_THREAD(1);
603  }
604  if (ret < 0) {
605  if (xemusock_should_repeat_from_error(xerr)) {
606  DEBUGPRINT("UMON: client: send_raw: send() non-fatal error, restarting: %s" NL, xemusock_strerror(xerr));
607  continue;
608  }
609  DEBUGPRINT("UMON: client: send_raw: send() returned with error: %s" NL, xemusock_strerror(xerr));
610  END_CLIENT_THREAD(1);
611  }
612  buffer += ret;
613  size -= ret;
614  }
615 }
616 
617 
618 static inline void send_string ( xemusock_socket_t sock, const char *p )
619 {
620  send_raw(sock, p, strlen(p));
621 }
622 
623 
624 static void client_run ( xemusock_socket_t sock )
625 {
626  char buffer[8192];
627  int read_size = 0;
628  int xerr;
629  for (;;) {
630  CHECK_STOP_TRIGGER();
631  if (read_size >= sizeof(buffer) - 1)
632  break;
633  int ret = xemusock_recv(sock, buffer + read_size, sizeof(buffer) - read_size - 1, &xerr);
634  DEBUGPRINT("UMON: client: result of recv() = %d, error = %s" NL, ret, ret == -1 ? xemusock_strerror(xerr) : "OK");
635  if (ret == 0)
636  break;
637  if (ret > 0) {
638  read_size += ret;
639  buffer[read_size] = 0;
640  const char *p = strstr(buffer, "\r\n\r\n");
641  if (p) {
642 
643  char outbuffer[8192];
644  sprintf(outbuffer,
645  "HTTP/1.1 200 OK\r\n"
646  "Host: 127.0.0.1\r\n"
647  "Content-Type: text/plain; charset=UTF-8\r\n"
648  "Connection: close\r\n"
649  "Cache-Control: no-store, no-cache, must-revalidate, max-age=0\r\n"
650  "Cache-Control: post-check=0, pre-check=0\r\n"
651  "Pragma: no-cache\r\n"
652  "Expires: Tue, 19 Sep 2017 19:08:16 GMT\r\n"
653  "X-UA-Compatible: IE=edge\r\n"
654  "X-Powered-By: The Powerpuff Girls\r\n"
655  "X-Content-Type-Options: nosniff\r\n"
656  "Access-Control-Allow-Origin: *\r\n"
657  "Server: Xemu/0.1\r\n"
658  "\r\n"
659  "Hello, world ;)\r\n"
660  );
661  send_string(sock, outbuffer);
662  //p = outbuffer;
663  //while (*p) {
664  // ret = xemusock_send(sock, p, strlen(p), &xerr);
665  // DEBUGPRINT("UMON: client: result of send() = %d, error = %s" NL, ret, ret == -1 ? xemusock_strerror(xerr) : "OK");
666  // if (ret == 0)
667  // break;
668  // if (ret > 0)
669  // p += ret;
670  // SDL_Delay(100);
671  //}
672  return;
673  }
674  }
675  SDL_Delay(100);
676  }
677 }
678 
679 
680 #undef END_CLIENT_THREAD
681 #undef CHECK_STOP_TRIGGER
682 
683 
684 // Client handling thread, for the life-time of a given connection only.
685 // It calls function client_run() to actually handle the connection.
686 // It's a very trivial function, just "extracts" the client socket from the thread parameter,
687 // and sets client socket to non-blocking mode, besides calling the mentioned real handler function.
688 // Also, this function sets a jump point for longjmp() thus, it's possible to return and finish thread
689 // without "walking backwards" in the call chain to be able to return from the thread handler itself.
690 static int client_thread_initiate ( void *user_param )
691 {
692  int num_of_threads = SDL_AtomicAdd(&thread_counter, 1) + 1; // increment thread counter (and remember)
693  xemusock_socket_t sock = (xemusock_socket_t)(uintptr_t)user_param;
694  DEBUGPRINT("UMON: client: new connection on socket %d" NL, (int)sock);
695  if (num_of_threads > XUMON_MAX_THREADS) {
696  DEBUGPRINT("UMON: client: too many threads (%d > %d), aborting connection." NL, num_of_threads, XUMON_MAX_THREADS);
697  } else {
698  int xerr;
699  if (xemusock_set_nonblocking(sock, XEMUSOCK_NONBLOCKING, &xerr)) {
700  DEBUGPRINT("UMON: client: Cannot set socket %d into non-blocking mode:\n%s" NL, (int)sock, xemusock_strerror(xerr));
701  } else {
702  if (!setjmp(jmp_finish_client_thread))
703  client_run(sock);
704  xemusock_set_nonblocking(sock, XEMUSOCK_BLOCKING, NULL);
705  }
706  }
707  xemusock_shutdown(sock, NULL);
708  xemusock_close(sock, NULL);
709  (void)SDL_AtomicAdd(&thread_counter, -1); // decrement thread counter
710  return 0;
711 }
712 
713 
714 // Main server thread, running during the full life-time of UMON subsystem.
715 // It accepts incoming connections and creating new threads to handle the given connection then.
716 static int main_thread ( void *user_param )
717 {
718  int client_seq = 0;
719  SDL_AtomicSet(&thread_counter, 1); // the main thread counts as the first one already
720  while (!SDL_AtomicGet(&thread_stop_trigger)) {
721  struct sockaddr_in sock_st;
722  int xerr;
723  // Wait for socket event with select, with 0.1sec timeout
724  // We need timeout, to check thread_stop_trigger condition
725  int select_result = xemusock_select_1(sock_server, 100000, XEMUSOCK_SELECT_R | XEMUSOCK_SELECT_E, &xerr);
726  if (!select_result)
727  continue;
728  if (select_result < 0) {
729  if (xerr == XSEINTR)
730  continue;
731  DEBUGPRINT("UMON: client: select() error: %s" NL, xemusock_strerror(xerr));
732  SDL_Delay(100);
733  continue;
734  }
735  xemusock_socklen_t len = sizeof(struct sockaddr_in);
736  xemusock_socket_t sock = xemusock_accept(sock_server, (struct sockaddr *)&sock_st, &len, &xerr);
737  if (sock != XS_INVALID_SOCKET && sock != XS_SOCKET_ERROR) { // FIXME: both conditions needed? maybe others as well?
738  char thread_name[64];
739  sprintf(thread_name, "Xemu-Umon-%d-%d", SDL_AtomicGet(&thread_counter), client_seq);
740  SDL_Thread *thread = SDL_CreateThread(client_thread_initiate, thread_name, (void*)(uintptr_t)sock);
741  if (thread) {
742  client_seq++;
743  SDL_DetachThread(thread);
744  } else {
745  DEBUGPRINT("UMON: client: cannot create thread for incomming connection" NL);
746  xemusock_shutdown(sock, NULL);
747  xemusock_close(sock, NULL);
748  }
749  } else {
750  DEBUGPRINT("UMON: client: accept() error: %s" NL, xemusock_strerror(xerr));
751  SDL_Delay(10);
752  }
753  }
754  (void)SDL_AtomicAdd(&thread_counter, -1); // for the main thread itself
755  return 0;
756 }
757 
758 
759 
760 /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! *
761  * END CRITICAL PART: these part ABOVE of the code runs in a *THREAD*. *
762  * The rest of this file is about creating the thread and it's enivornment first, *
763  * and it will run in the main context of the execution. *
764  * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
765 
766 
767 int xumon_init ( int port )
768 {
769  SDL_AtomicSet(&thread_counter, 0);
770  sock_server = XS_INVALID_SOCKET;
771  if (!port) {
772  DEBUGPRINT("UMON: not enabled" NL);
773  return 0;
774  }
775  static const char err_msg[] = "UMON initialization problem, UMON won't be available:\n";
776  if (port < 1024 || port > 0xFFFF) {
777  ERROR_WINDOW("%sInvalid port (must be between 1024 and 65535): %d", err_msg, port);
778  goto error;
779  }
780  const char *sock_init_status = xemusock_init();
781  if (sock_init_status) {
782  ERROR_WINDOW("%sCannot initialize network library:\n%s", err_msg, sock_init_status);
783  goto error;
784  }
785  int xerr;
786  sock_server = xemusock_create_for_inet(XEMUSOCK_TCP, XEMUSOCK_BLOCKING, &xerr);
787  if (sock_server == XS_INVALID_SOCKET) {
788  ERROR_WINDOW("%sCannot create TCP socket:\n%s", err_msg, xemusock_strerror(xerr));
789  goto error;
790  }
791  if (xemusock_setsockopt_reuseaddr(sock_server, &xerr)) {
792  ERROR_WINDOW("UMON setsockopt for SO_REUSEADDR failed:\n%s", xemusock_strerror(xerr));
793  goto error;
794  }
795  struct sockaddr_in sock_st;
796  xemusock_fill_servaddr_for_inet_ip_native(&sock_st, 0, port);
797  xemusock_socklen_t sock_len = sizeof(struct sockaddr_in);
798  if (xemusock_bind(sock_server, (struct sockaddr*)&sock_st, sock_len, &xerr)) {
799  ERROR_WINDOW("%sCannot bind TCP socket %d:\n%s", err_msg, port, xemusock_strerror(xerr));
800  goto error;
801  }
802  if (xemusock_listen(sock_server, 5, &xerr)) {
803  ERROR_WINDOW("%sCannot listen socket %d:\n%s", err_msg, (int)sock_server, xemusock_strerror(xerr));
804  goto error;
805  }
806  if (xemusock_set_nonblocking(sock_server, XEMUSOCK_NONBLOCKING, &xerr)) {
807  ERROR_WINDOW("%sCannot set socket %d into non-blocking mode:\n%s", err_msg, (int)sock_server, xemusock_strerror(xerr));
808  goto error;
809  }
810  // Create thread to handle incoming connections on our brand new server socket we've just created for this purpose
811  SDL_AtomicSet(&thread_stop_trigger, 0);
812  Uint32 passed_time = 0, start_time = SDL_GetTicks();
813  SDL_Thread *thread = SDL_CreateThread(main_thread, "Xemu-Umon-Main", NULL);
814  if (!thread) {
815  ERROR_WINDOW("%sCannot create monitor thread:\n%s", err_msg, SDL_GetError());
816  goto error;
817  }
818  SDL_DetachThread(thread);
819  while (!SDL_AtomicGet(&thread_counter)) {
820  SDL_Delay(1);
821  passed_time = SDL_GetTicks() - start_time;
822  if (passed_time > 500) {
823  DEBUGPRINT("UMON: timeout while waiting for thread to start! UMON won't be available!" NL);
824  goto error;
825  }
826  }
827  // Everything is OK, return with success.
828  DEBUGPRINT("UMON: has been initialized for TCP/IP port %d, on-line within %d msecs." NL, port, passed_time);
829  return 0;
830 error:
831  SDL_AtomicSet(&thread_stop_trigger, 1);
832  if (sock_server != XS_INVALID_SOCKET) {
833  int xerr;
834  if (xemusock_close(sock_server, &xerr))
835  DEBUGPRINT("UMON: warning, could not close server socket after error: %s" NL, xemusock_strerror(xerr));
836  sock_server = XS_INVALID_SOCKET;
837  }
838  return 1;
839 }
840 
841 
842 int xumon_stop ( void )
843 {
844  int count = SDL_AtomicGet(&thread_counter);
845  if (!count)
846  return 0;
847  Uint32 passed_time = 0, start_time = SDL_GetTicks();
848  SDL_AtomicSet(&thread_stop_trigger, 1);
849  while (SDL_AtomicGet(&thread_counter) > 0) {
850  SDL_Delay(1);
851  passed_time = SDL_GetTicks() - start_time;
852  if (passed_time > 500) {
853  DEBUGPRINT("UMON: timeout while waiting for threads to stop!" NL);
854  break;
855  }
856  }
857  xemusock_set_nonblocking(sock_server, XEMUSOCK_BLOCKING, NULL);
858  xemusock_shutdown(sock_server, NULL);
859  xemusock_close(sock_server, NULL);
860  sock_server = XS_INVALID_SOCKET;
861  int count2 = SDL_AtomicGet(&thread_counter);
862  DEBUGPRINT("UMON: shutdown, %d thread(s) (%d client) exited, %d thread(s) has timeout condition, %d msecs." NL, count - count2, count - count2 - 1, count2, passed_time);
863  return 0;
864 }
865 
866 #endif
emutools.h
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
Uint32
uint32_t Uint32
Definition: fat32.c:49
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
emutools_socketapi.h
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
error_code
int error_code
Definition: hdos.c:52
NL
#define NL
Definition: fat32.c:37
size
int size
Definition: inject.c:37
emutools_umon.h