31 static xemusock_socket_t sock_server;
33 static SDL_atomic_t thread_counter;
34 static SDL_atomic_t thread_stop_trigger;
35 static jmp_buf jmp_finish_client_thread;
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); \
49 static char _xemunet_strerror_buffer[1024];
54 #include <sys/socket.h>
55 #include <sys/types.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 )
66 strcpy(_xemunet_strerror_buffer, strerror(errno));
67 return _xemunet_strerror_buffer;
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;
81 #define xemunet_errno WSAGetLastError()
82 static void xemunet_perror (
const char *s )
84 fprintf(stderr,
"%s: (WSA_CODE=%d)\n", s, WSAGetLastError());
86 static const char *xemunet_strneterror (
void )
88 sprintf(_xemunet_strerror_buffer,
"(WSA_CODE=%d)", WSAGetLastError());
89 return _xemunet_strerror_buffer;
95 volatile int xumon_is_running = 0;
96 static volatile int thread_stop_trigger;
97 static SDL_Thread *xumon_thread_id = NULL;
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;
103 struct xumon_message_queue {
104 volatile int provider_pos;
105 volatile int consumer_pos;
106 volatile char buffer[MESSAGE_QUEUE_SIZE];
111 #define XEMUNET_SELECT_R 1
112 #define XEMUNET_SELECT_W 2
113 #define XEMUNET_SELECT_E 4
116 static int xemunet_select_1 ( xemunet_socket_t sock,
int msec,
int what )
121 struct timeval timeout;
122 fd_set fds_r, fds_w, 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);
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)
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);
157 #define FINISH_THREAD(n) longjmp(jmp_finish_thread, n)
160 static void queue_push (
struct xumon_message_queue *queue,
const void *
data,
int size )
162 int pos = queue->push_pos;
164 while (pos == queue->pull_pos)
166 queue->buffer[pos] = *
data++;
167 pos = (pos + 1) & MESSAGE_QUEUE_SIZE_MASK;
169 queue->push_pos = pos;
174 static int socket_send_raw ( xemunet_socket_t sock,
const void *
data,
int size )
178 if (thread_stop_trigger)
182 if (xemunet_errno == EINTR)
195 static int socket_send_string ( xemunet_socket_t sock,
const char *s )
197 return socket_send_raw(sock, s, strlen(s));
201 static int hexstr2num (
const char *s,
int len )
206 if (*s >=
'0' && *s <=
'9')
208 else if (*s >=
'A' && *s <=
'F')
209 out |= *s -
'A' + 10;
210 else if (*s >=
'a' && *s <=
'f')
211 out |= *s -
'a' + 10;
222 static void submit_monitor_command_from_client (
int sock,
char *cmd,
int is_uri_encoded )
224 if (is_uri_encoded) {
226 s = strstr(cmd, by_uri_part);
229 s += strlen(by_uri_part);
233 if (*s == 0 || *s ==
'?' || *s ==
'&' || *s ==
'#')
235 else if (*s ==
'+') {
238 }
else if (*cmd ==
'%') {
239 int h = hexstr2num(s + 1, 2);
252 queue_push(&queue_from_client, cmd);
253 SDL_AtomicLock(&queue->lock);
254 SDL_AtomicUnlock(&queue->lock);
256 int pos = queue_from_client.push_pos;
258 while (pos == queue_from_client.pull_pos)
260 queue_from_client.buffer[pos] = *cmd++;
261 pos = (pos + 1) & (MSG_QUEUE_SIZE - 1);
263 queue_from_client.push_pos = pos;
265 queue_pos(&our_queue, cmd, strlen(cmd));
267 SDL_LockMutex(queue_input.mutex);
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);
274 socket_send_string(sock,
"test-reply: you've entered this: \"");
275 socket_send_string(sock, cmd);
276 socket_send_string(sock,
"\"\r\n");
281 static int http_send_chunk ( xemunet_socket_t sock,
const void *
data,
int size )
285 sprintf(buffer,
"\r\n%X\r\n",
size);
286 if (socket_send_raw(sock, buffer, strlen(buffer)))
288 return socket_send_raw(sock,
data,
size);
290 return socket_send_raw(sock,
"\r\n0\r\n\r\n", 7);
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 )
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"
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"
314 keep_alive ?
"keep-alive" :
"close",
318 strcat(buffer,
"Allow: GET\r\n");
320 sprintf(buffer + strlen(buffer),
"Content-Length: %d\r\n\r\n", (
int)strlen(simple_answer));
322 strcat(buffer,
"Transfer-Encoding: chunked\r\n");
323 if (socket_send_raw(sock, buffer, strlen(buffer)))
326 if (socket_send_raw(sock, simple_answer, strlen(simple_answer)))
335 static void fancy_print (
const char *s )
339 if (*s >= 32 && *s <= 127)
349 static int http_client_handler ( xemunet_socket_t sock,
const char *method,
const char *uri,
const char *http_headers)
351 if (strcasecmp(method,
"GET"))
352 return http_answer(sock, 405,
"Method Not Allowed", 0, content_type_text,
"This service only supports GET method!");
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);
358 while (*http_headers != 1) {
359 char c = *http_headers++;
361 p += sprintf(p,
"%s",
"<");
363 p += sprintf(p,
"%s",
">");
366 while (!*http_headers)
371 sprintf(p,
"%s",
"</pre></body></html>");
372 return http_answer(sock, 200,
"Ok", 0,
"text/html; charset=utf-8", buffer);
379 static int generic_client_handler ( xemunet_socket_t sock )
383 int buffer_start = 0;
384 char line_ending = 0;
386 enum { DETECT_LINK, TEXT_LINK, HTTP_LINK } link_mode = DETECT_LINK;
387 char *http_uri, *http_method, *http_headers = NULL;
390 if (buffer_start && link_mode == TEXT_LINK) {
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;
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!");
402 socket_send_string(sock,
"?TOO LONG LINE OR TOO MANY QUEUED REQUESTS\r\n");
410 if (thread_stop_trigger)
412 a = recv(sock, buffer + buffer_fill,
sizeof(buffer) - buffer_fill, 0);
414 printf(
"Connection closed by remote peer?\n");
419 if (xemunet_errno != EINTR) {
420 xemunet_perror(
"Connection failed at read()");
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) {
431 printf(
"Ignoring extra byte: %d\n", buffer[a]);
437 char *line = buffer + buffer_start;
438 line_ending = buffer[a];
442 if (link_mode == DETECT_LINK) {
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)) {
452 link_mode = HTTP_LINK;
453 printf(
"HTTP mode detected! Method=\"%s\" URI=\"%s\"\n", http_method, http_uri);
455 link_mode = TEXT_LINK;
456 submit_monitor_command_from_client(sock, line, 0);
458 }
else if (link_mode == TEXT_LINK) {
459 submit_monitor_command_from_client(sock, line, 0);
464 return http_client_handler(sock, http_method, http_uri, http_headers);
465 }
else if (!http_headers) {
467 printf(
"http_headers set to %s\n", http_headers);
483 static int xumon_main (
void *user_data_unused )
486 xemunet_socket_t client_sock = XEMUNET_INVALID_SOCKET;
487 printf(
"UMON: linking thread is running\n");
488 ret_on = setjmp(jmp_finish_thread);
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);
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) {
502 struct sockaddr_in client_addr;
503 socklen_t addrlen =
sizeof client_addr;
505 int ret = xemunet_select_1(server_sock, 50, XEMUNET_SELECT_R);
506 if (thread_stop_trigger)
509 client_sock = accept(server_sock, (
struct sockaddr *)&client_addr, &addrlen);
510 if (client_sock == XEMUNET_INVALID_SOCKET) {
511 if (xemunet_errno == EINTR)
513 xemunet_perror(
"Server: accept");
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");
524 client_sock = XEMUNET_INVALID_SOCKET;
526 printf(
"UMON: leaving main accept() loop ...\n");
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");
543 static int recv_raw ( xemusock_socket_t sock,
void *buffer,
int min_size,
int max_size )
546 while (size < min_size && max_size > 0) {
547 CHECK_STOP_TRIGGER();
549 int ret = xemusock_select_1(sock, 100000, XEMUSOCK_SELECT_R, &xerr);
551 if (xerr == XSEINTR) {
552 DEBUGPRINT(
"UMON: client: recv_raw: select() got EINTR, restarting" NL);
555 DEBUGPRINT(
"UMON: client: recv_raw: select() returned with error: %s" NL, xemusock_strerror(xerr));
556 END_CLIENT_THREAD(1);
560 ret = xemusock_recv(sock, buffer, max_size, &xerr);
563 DEBUGPRINT(
"UMON: client: recv_raw: recv() returned with zero" NL);
564 END_CLIENT_THREAD(1);
567 if (xemusock_should_repeat_from_error(xerr)) {
568 DEBUGPRINT(
"UMON: client: recv_raw: recv() non-fatal error, restarting: %s" NL, xemusock_strerror(xerr));
571 DEBUGPRINT(
"UMON: client: recv_raw: recv() returned with error: %s" NL, xemusock_strerror(xerr));
572 END_CLIENT_THREAD(1);
582 static void send_raw ( xemusock_socket_t sock,
const void *buffer,
int size )
585 CHECK_STOP_TRIGGER();
587 int ret = xemusock_select_1(sock, 100000, XEMUSOCK_SELECT_W, &xerr);
589 if (xerr == XSEINTR) {
590 DEBUGPRINT(
"UMON: client: send_raw: select() got EINTR, restarting" NL);
593 DEBUGPRINT(
"UMON: client: send_raw: select() returned with error: %s" NL, xemusock_strerror(xerr));
594 END_CLIENT_THREAD(1);
598 ret = xemusock_send(sock, buffer,
size, &xerr);
601 DEBUGPRINT(
"UMON: client: send_raw: send() returned with zero" NL);
602 END_CLIENT_THREAD(1);
605 if (xemusock_should_repeat_from_error(xerr)) {
606 DEBUGPRINT(
"UMON: client: send_raw: send() non-fatal error, restarting: %s" NL, xemusock_strerror(xerr));
609 DEBUGPRINT(
"UMON: client: send_raw: send() returned with error: %s" NL, xemusock_strerror(xerr));
610 END_CLIENT_THREAD(1);
618 static inline void send_string ( xemusock_socket_t sock,
const char *p )
620 send_raw(sock, p, strlen(p));
624 static void client_run ( xemusock_socket_t sock )
630 CHECK_STOP_TRIGGER();
631 if (read_size >=
sizeof(buffer) - 1)
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");
639 buffer[read_size] = 0;
640 const char *p = strstr(buffer,
"\r\n\r\n");
643 char outbuffer[8192];
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"
659 "Hello, world ;)\r\n"
661 send_string(sock, outbuffer);
680 #undef END_CLIENT_THREAD
681 #undef CHECK_STOP_TRIGGER
690 static int client_thread_initiate (
void *user_param )
692 int num_of_threads = SDL_AtomicAdd(&thread_counter, 1) + 1;
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);
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));
702 if (!setjmp(jmp_finish_client_thread))
704 xemusock_set_nonblocking(sock, XEMUSOCK_BLOCKING, NULL);
707 xemusock_shutdown(sock, NULL);
708 xemusock_close(sock, NULL);
709 (void)SDL_AtomicAdd(&thread_counter, -1);
716 static int main_thread (
void *user_param )
719 SDL_AtomicSet(&thread_counter, 1);
720 while (!SDL_AtomicGet(&thread_stop_trigger)) {
721 struct sockaddr_in sock_st;
725 int select_result = xemusock_select_1(sock_server, 100000, XEMUSOCK_SELECT_R | XEMUSOCK_SELECT_E, &xerr);
728 if (select_result < 0) {
731 DEBUGPRINT(
"UMON: client: select() error: %s" NL, xemusock_strerror(xerr));
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) {
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);
743 SDL_DetachThread(thread);
745 DEBUGPRINT(
"UMON: client: cannot create thread for incomming connection" NL);
746 xemusock_shutdown(sock, NULL);
747 xemusock_close(sock, NULL);
750 DEBUGPRINT(
"UMON: client: accept() error: %s" NL, xemusock_strerror(xerr));
754 (void)SDL_AtomicAdd(&thread_counter, -1);
767 int xumon_init (
int port )
769 SDL_AtomicSet(&thread_counter, 0);
770 sock_server = XS_INVALID_SOCKET;
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);
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);
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));
791 if (xemusock_setsockopt_reuseaddr(sock_server, &xerr)) {
792 ERROR_WINDOW(
"UMON setsockopt for SO_REUSEADDR failed:\n%s", xemusock_strerror(xerr));
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));
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));
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));
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);
815 ERROR_WINDOW(
"%sCannot create monitor thread:\n%s", err_msg, SDL_GetError());
818 SDL_DetachThread(thread);
819 while (!SDL_AtomicGet(&thread_counter)) {
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);
828 DEBUGPRINT(
"UMON: has been initialized for TCP/IP port %d, on-line within %d msecs." NL, port, passed_time);
831 SDL_AtomicSet(&thread_stop_trigger, 1);
832 if (sock_server != XS_INVALID_SOCKET) {
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;
842 int xumon_stop (
void )
844 int count = SDL_AtomicGet(&thread_counter);
847 Uint32 passed_time = 0, start_time = SDL_GetTicks();
848 SDL_AtomicSet(&thread_stop_trigger, 1);
849 while (SDL_AtomicGet(&thread_counter) > 0) {
851 passed_time = SDL_GetTicks() - start_time;
852 if (passed_time > 500) {
853 DEBUGPRINT(
"UMON: timeout while waiting for threads to stop!" NL);
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);