swh:1:snp:122bde0cb0e54f3d002c308e151c63f07e45e6be
Raw File
Tip revision: 4e94bc42eebcd34c3c4c56447064b0ea65a1c811 authored by Ruth-Huang6012 on 19 March 2024, 13:25:49 UTC
Fixed bug in integrator_whfast512.c (#760)
Tip revision: 4e94bc4
server.c
/**
 * @file    server.c
 * @brief   Opens a webserver to allow for platform independent visualization.
 * @author  Hanno Rein <hanno@hanno-rein.de>
 * @details These functions provide real time visualizations
 * using OpenGL. Part of the code is by Dave O'Hallaron, Carnegie Mellon (tiny.c).
 * 
 * @section LICENSE
 * Copyright (c) 2023 Hanno Rein, Dave O'Hallaron, Carnegie Mellon
 *
 * This file is part of rebound.
 *
 * rebound 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 3 of the License, or
 * (at your option) any later version.
 *
 * rebound 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 rebound.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "rebound.h"

#ifdef SERVER
#include <stdio.h>
#ifdef _MSC_VER 
//not #if defined(_WIN32) || defined(_WIN64) because we have strncasecmp in mingw
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#ifdef _WIN32
#include <WS2tcpip.h>
#include <tchar.h>
#include <io.h>
#define F_OK 0
#define access _access
#pragma comment(lib, "ws2_32.lib")
#else // _WIN32
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <netinet/in.h>
#endif // _WIN32
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>



#define BUFSIZE 1024

const char* reb_server_header =
        "HTTP/1.1 200 OK\n"
        "Server: REBOUND Webserver\n"
        "Cache-Control: no-cache, no-store, must-revalidate\n"
        "Pragma: no-cache\n"
        "Expires: 0\n"
      //"Access-Control-Allow-Origin: *\n"
      //"Cross-Origin-Opener-Policy: cross-origin\n"
        "Content-type: text/html\n"
        "\r\n";
const char* reb_server_header_png =
        "HTTP/1.1 200 OK\n"
        "Server: REBOUND Webserver\n"
        "Content-type: image/png\n"
        "\r\n";

#ifdef _WIN32
int sendBytes(SOCKET s, const void * buffer, int buflen){
    int total = 0;
    char *pbuf = (char*) buffer;
    while (buflen > 0) {
        int iResult = send(s, pbuf, buflen, 0);
        if (iResult < 0) {
            if (WSAGetLastError() == WSAEWOULDBLOCK) {
                // optionally use select() to wait for the
                // socket to have more space to write before
                // calling send() again...
                continue;
            }

            printf("send error: %d\n", WSAGetLastError());
            return SOCKET_ERROR;
        } else if (iResult == 0) {
            printf("disconnected\n");
            return 0;
        } else {
            pbuf += iResult;
            buflen -= iResult;
            total += iResult;
        }
    }

    return total;
}
#endif // _WIN32


#ifdef _WIN32
static void reb_server_cerror(SOCKET clientS, char cause[]){
#else //_WIN32
static void reb_server_cerror(FILE *stream, char *cause){
#endif //_WIN32
    char* buf = NULL;
    asprintf(&buf,  "HTTP/1.1 501 Not Implemented\n"
                    "Content-type: text/html\n"
                    "\n"
                    "<html><title>REBOUND Webserver Error</title>"
                    "<body>\n"
                    "<h1>Error</h1>\n"
                    "<p>%s</p>\n"
                    "<hr><em>REBOUND Webserver</em>\n"
                    "</body></html>\n"
                    , cause);
    printf("\nREBOUND Webserver error: %s\n", cause);
#ifdef _WIN32
    sendBytes(clientS, buf, strlen(buf));
    closesocket(clientS); // close the Client Socket now that our Work is Complete.
#else //_WIN32
    fwrite(buf, 1, strlen(buf), stream);
#endif //_WIN32
    free(buf);
}

static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/**
 * base64_decode - Base64 decode
 * @src: Data to be decoded
 * @len: Length of the data to be decoded
 * @out_len: Pointer to output length variable
 * Returns: Allocated buffer of out_len bytes of decoded data,
 * or %NULL on failure
 *
 * Caller is responsible for freeing the returned buffer.
 *
 * Source: https://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c
 */
static unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len) {
	unsigned char dtable[256], *out, *pos, block[4], tmp;
	size_t i, count, olen;
	int pad = 0;

	memset(dtable, 0x80, 256);
	for (i = 0; i < sizeof(base64_table) - 1; i++)
		dtable[base64_table[i]] = (unsigned char) i;
	dtable['='] = 0;

	count = 0;
	for (i = 0; i < len; i++) {
		if (dtable[src[i]] != 0x80)
			count++;
	}

	if (count == 0 || count % 4)
		return NULL;

	olen = count / 4 * 3;
	pos = out = malloc(olen);
	if (out == NULL)
		return NULL;

	count = 0;
	for (i = 0; i < len; i++) {
		tmp = dtable[src[i]];
		if (tmp == 0x80)
			continue;

		if (src[i] == '=')
			pad++;
		block[count] = tmp;
		count++;
		if (count == 4) {
			*pos++ = (block[0] << 2) | (block[1] >> 4);
			*pos++ = (block[1] << 4) | (block[2] >> 2);
			*pos++ = (block[2] << 6) | block[3];
			count = 0;
			if (pad) {
				if (pad == 1)
					pos--;
				else if (pad == 2)
					pos -= 2;
				else {
					/* Invalid padding */
					free(out);
					return NULL;
				}
				break;
			}
		}
	}

	*out_len = pos - out;
	return out;
}


#ifndef _WIN32
void* reb_server_start(void* args){
#else //_WIN32
DWORD WINAPI reb_server_start(void* args){
#endif // _WIN32
    struct reb_server_data* data = (struct reb_server_data*)args;
    struct reb_simulation* r = data->r;

    if (access("rebound.html", F_OK)) {
        reb_simulation_warning(r, "File rebound.html not found in current directory. Attempting to download it from github.");
        char curl_cmd[] = "curl -L -s --output rebound.html https://github.com/hannorein/rebound/releases/latest/download/rebound.html";
        system(curl_cmd);
        if (access("rebound.html", F_OK)) {
            reb_simulation_warning(r, "Automatic download failed. Manually download the file from github and place it in the current directory to enable browser based visualization.");
        }else{
            printf("Success: rebound.html downloaded.\n");
        }
    }

#ifndef _WIN32
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);


    /* variables for connection management */
    int childfd;                   /* child socket */
    unsigned int clientlen;        /* byte size of client's address */
    int optval;                    /* flag value for setsockopt */
    struct sockaddr_in serveraddr; /* server's addr */
    struct sockaddr_in clientaddr; /* client addr */

    /* variables for connection I/O */
    FILE *stream;          /* stream version of childfd */
    char buf[BUFSIZE];     /* message buffer */
    char method[BUFSIZE];  /* request method */
    char uri[BUFSIZE];     /* request uri */
    char version[BUFSIZE]; /* request method */

    /* open socket descriptor */
    data->socket = socket(AF_INET, SOCK_STREAM, 0);
    if (data->socket < 0)
        reb_exit("ERROR opening socket");

    /* allows us to restart server immediately */
    optval = 1;
    setsockopt(data->socket, SOL_SOCKET, SO_REUSEADDR,
            (const void *)&optval , sizeof(int));

    /* bind port to socket */
    memset((char *) &serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(data->port);
    if (bind(data->socket, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0){
        char error_msg[BUFSIZE];
        snprintf(error_msg, BUFSIZE, "Error binding to port %d. Port might be in use.\n", data->port);
        reb_simulation_error(r, error_msg);
        data->ready = -1;
        return PTHREAD_CANCELED;
    }

    /* get us ready to accept connection requests */
    if (listen(data->socket, 5) < 0) /* allow 5 requests to queue up */
        reb_exit("ERROR on listen");

    printf("REBOUND Webserver listening on http://localhost:%d ...\n",data->port);
    /*
     * main loop: wait for a connection request, parse HTTP,
     * serve requested content, close connection.
     */
    clientlen = sizeof(clientaddr);
    while (1) {
        /* wait for a connection request */
        data->ready = 1;
        childfd = accept(data->socket, (struct sockaddr *) &clientaddr, &clientlen);
        if (childfd < 0) { // Accept will fail if main thread is closing socket.
            return PTHREAD_CANCELED;
        }

        /* open the child socket descriptor as a stream */
        if ((stream = fdopen(childfd, "r+")) == NULL)
            reb_exit("ERROR on fdopen");

        /* get the HTTP request line */
        char* request = fgets(buf, BUFSIZE, stream);
        if (!request){
            reb_server_cerror(stream, "Did not get request.");
            fclose(stream);
            close(childfd);
            continue;
        }
        sscanf(buf, "%s %s %s\n", method, uri, version);

        /* only support the GET method */
        if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) {
            reb_server_cerror(stream, "Only GET+POST are implemented.");
            fclose(stream);
            close(childfd);
            continue;
        }
           
        /* read (and ignore) the HTTP headers */
        fgets(buf, BUFSIZE, stream);
        unsigned long content_length = 0;
        while(strcmp(buf, "\r\n")) {
            char cl[BUFSIZE];
            int ni = sscanf(buf, "Content-Length: %s\n", cl);
            if (ni){
                content_length = strtol(cl,NULL,10);
            }
            fgets(buf, BUFSIZE, stream);
        }

        if (!strcasecmp(uri, "/simulation")) {
            char* bufp = NULL;
            size_t sizep;
            data->need_copy = 1;
            pthread_mutex_lock(&(data->mutex));
            reb_simulation_save_to_stream(r, &bufp,&sizep);
            data->need_copy = 0;
            pthread_mutex_unlock(&(data->mutex));
            fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
            fwrite(bufp, 1, sizep, stream);
            free(bufp);
        }else if (!strncasecmp(uri, "/keyboard/",10)) {
            int key = 0;
            sscanf(uri, "/keyboard/%d", &key);
            data->need_copy = 1;
            pthread_mutex_lock(&(data->mutex));
            int skip_default_keys = 0;
            if (r->key_callback){
                skip_default_keys = r->key_callback(r, key);
            } 
            data->need_copy = 0;
            pthread_mutex_unlock(&(data->mutex));
            if (!skip_default_keys){
                switch (key){
                    case 'Q':
                        data->r->status = REB_STATUS_USER;
                        fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
                        fprintf(stream, "ok.\n");
                        break;
                    case ' ':
                        if (data->r->status == REB_STATUS_PAUSED){
                            printf("Resume.\n");
                            data->r->status = REB_STATUS_RUNNING;
                        }else if (data->r->status == REB_STATUS_RUNNING){
                            printf("Pause.\n");
                            data->r->status = REB_STATUS_PAUSED;
                        }
                        fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
                        fprintf(stream, "ok.\n");
                        break;
                    case 264: // arrow down
                        if (data->r->status == REB_STATUS_PAUSED){
                            data->r->status = REB_STATUS_SINGLE_STEP;
                            printf("Step.\n");
                        }
                        fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
                        fprintf(stream, "ok.\n");
                        break;
                    case 267: // page down
                        if (data->r->status == REB_STATUS_PAUSED){
                            data->r->status = REB_STATUS_SINGLE_STEP - 50;
                            printf("50 steps.\n");
                        }
                        fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
                        fprintf(stream, "ok.\n");
                        break;
                    default:
                        // reb_server_cerror(stream, "Unsupported key received.");
                        break;
                }
            }else{
                fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
                fprintf(stream, "ok.\n");
            }
        }else if (!strcasecmp(uri, "/") || !strcasecmp(uri, "/index.html") || !strcasecmp(uri, "/rebound.html")) {
            struct stat sbuf;
            if (stat("rebound.html", &sbuf) < 0) {
                reb_server_cerror(stream, "rebound.html not found in current directory. Try `make rebound.html`.");
            }else{
                fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
                int fd = open("rebound.html", O_RDONLY);
                void* p = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
                fwrite(p, 1, sbuf.st_size, stream);
                munmap(p, sbuf.st_size);
            }
        }else if (!strcasecmp(uri, "/favicon.ico")) {
                fwrite(reb_server_header_png, 1, strlen(reb_server_header_png), stream);
                fwrite(reb_favicon_png,1, reb_favicon_len, stream);
        }else if (!strcasecmp(uri, "/screenshot")) {
            data->need_copy = 1;
            pthread_mutex_lock(&(data->mutex));
            
            if (content_length==0){
                printf("Received screenshot with size zero.");
                goto screenshot_finish;
            }
            if (r->status != REB_STATUS_SCREENSHOT){
                printf("Received screenshot but did not expect one.\n");
                goto screenshot_finish;
            }
            if (data->screenshot) {
                printf("Unable to receive screenshot as previous screenshot not freed.\n");
                goto screenshot_finish;
            }

            char* dataURL = malloc(content_length);
            int rc = fread(dataURL, content_length, 1,  stream);

            if (rc!=1){
                printf("Error while reading screenshot data.\n");
                free(dataURL);
                goto screenshot_finish;
            }

            int rc_len = strlen(dataURL)+1;
            char* base64 = strchr(dataURL, ',');
            if (content_length != rc_len){
                printf("Received screenshot with incorrect size.\n");
                free(dataURL);
                goto screenshot_finish;
            }
            if (!base64){
                printf("Unable to decode received screenshot. Data not in dataURL format.\n");
                free(dataURL);
                goto screenshot_finish;
            }
            data->screenshot = base64_decode((unsigned char*)base64+1, strlen(base64+1), &data->N_screenshot);
            if (!data->screenshot){
                printf("An error occured while decoding the screenshot.\n");
            }
            data->r->status = REB_STATUS_PAUSED;
            free(dataURL);
screenshot_finish:
            data->need_copy = 0;
            pthread_mutex_unlock(&(data->mutex));
            fwrite(reb_server_header, 1, strlen(reb_server_header), stream);
            fprintf(stream, "ok.\n");
        }else{
            reb_server_cerror(stream, "Unsupported URI.");
            printf("URI: %s\n", uri);
        }

        /* clean up */
        fflush(stream);
        fclose(stream);
        close(childfd);

    }
    printf("Server shutting down...\n");
    return PTHREAD_CANCELED;

#else // _WIN32


    WSADATA wsa;
    struct sockaddr_in server;
    SOCKET clientS;
    char method[BUFSIZE];
    char uri[BUFSIZE];
    char version[BUFSIZE];

    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        printf("Winsock startup failed");
        exit(1);
    }

    data->socket = socket(AF_INET, SOCK_STREAM, 0);
    if (data->socket == INVALID_SOCKET) {
        printf("Socket error\n");
        exit(1);
    }

    server.sin_family = AF_INET;
    server.sin_port = htons(data->port);
    InetPton(AF_INET, _T("0.0.0.0"), &server.sin_addr);

    int ret_bind = bind(data->socket, (struct sockaddr*)&server, sizeof(server)); // binding the Host Address and Port Number
    if (ret_bind) {
        char error_msg[BUFSIZE];
        snprintf(error_msg, BUFSIZE, "Error binding to port %d. Port might be in use.\n", data->port);
        reb_simulation_error(r, error_msg);
        data->ready = -1;
        return 1;
    }

    int ret_listen = listen(data->socket, AF_INET);
    if (ret_listen){
        printf("Listen error\n");
        exit(1);
    }
    
    printf("REBOUND Webserver listening on http://localhost:%d ...\n",data->port);

    while(1){
        data->ready = 1;
        clientS = accept(data->socket, NULL, NULL);
        if (clientS == INVALID_SOCKET) { // Accept will fail if main thread is closing socket.
            return 1;
        } 
        // Receive entire request.
        int recN = 0;
        char* recbuf = malloc(BUFSIZE);
        int recbufN = 0;
        while((recN = recv(clientS, recbuf+recbufN, BUFSIZE, 0))>0){
            recbufN += recN;
            if (recN<BUFSIZE) break;
            recbuf = realloc(recbuf, recbufN+BUFSIZE);
        }

        // Get method and uri
        sscanf(recbuf, "%s %s %s\n", method, uri, version);
        if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) {
            reb_server_cerror(clientS, "Method not Implemented");
            continue;
        }
        
        /* read (and ignore) the HTTP headers */
        char* curLine = recbuf;
        unsigned long content_length = 0;
        while(curLine){
            char* nextLine = strchr(curLine, '\n');
            if (nextLine) *nextLine = '\0';
            char cl[BUFSIZE];
            int ni = sscanf(curLine, "Content-Length: %s\n", cl);
            if (ni){
                content_length = strtol(cl,NULL,10);
            }
            if (nextLine){
                *nextLine = '\n';
                curLine = nextLine+1;
            }else{
                break;
            }
        }
        
        // Only post data is needed, otherwise free here
        if (strcasecmp(method, "POST")) {
            free(recbuf);
        }
        
        if (!strcasecmp(uri, "/simulation")) {
            char* bufp = NULL;
            size_t sizep;
            data->need_copy = 1;
            WaitForSingleObject(data->mutex, INFINITE);
            reb_simulation_save_to_stream(r, &bufp,&sizep);
            data->need_copy = 0;
            ReleaseMutex(data->mutex);
            sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
            sendBytes(clientS, bufp, sizep);
            free(bufp);
        }else if (!strncasecmp(uri, "/keyboard/",10)) {
            int key = 0;
            const char* ok = "ok.";
            sscanf(uri, "/keyboard/%d", &key);
            int skip_default_keys = 0;
            data->need_copy = 1;
            WaitForSingleObject(data->mutex, INFINITE);
            if (r->key_callback){
                skip_default_keys = r->key_callback(r, key);
            } 
            data->need_copy = 0;
            ReleaseMutex(data->mutex);
            if (!skip_default_keys){
                switch (key){
                    case 'Q':
                        data->r->status = REB_STATUS_USER;
                        sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
                        sendBytes(clientS, ok, strlen(ok));
                        break;
                    case ' ':
                        if (data->r->status == REB_STATUS_PAUSED){
                            printf("Resume.\n");
                            data->r->status = REB_STATUS_RUNNING;
                        }else if (data->r->status == REB_STATUS_RUNNING){
                            printf("Pause.\n");
                            data->r->status = REB_STATUS_PAUSED;
                        }
                        sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
                        sendBytes(clientS, ok, strlen(ok));
                        break;
                    case 264: // down arrow
                        if (data->r->status == REB_STATUS_PAUSED){
                            data->r->status = REB_STATUS_SINGLE_STEP;
                            printf("Step.\n");
                        }
                        sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
                        sendBytes(clientS, ok, strlen(ok));
                        break;
                    case 267: // page down
                        if (data->r->status == REB_STATUS_PAUSED){
                            data->r->status = REB_STATUS_SINGLE_STEP - 50;
                            printf("50 step.\n");
                        }
                        sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
                        sendBytes(clientS, ok, strlen(ok));
                        break;
                    default:
                        // reb_server_cerror(clientS, "Unknown key received.");
                        continue;
                        break;
                }
            }else{
                sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
                sendBytes(clientS, ok, strlen(ok));
            }
        }else if (!strcasecmp(uri, "/") || !strcasecmp(uri, "/index.html") || !strcasecmp(uri, "/rebound.html")) {
            FILE *f = fopen("rebound.html", "rb");
            if (f){
                fseek(f, 0, SEEK_END);
                long fsize = ftell(f);
                fseek(f, 0, SEEK_SET);
                char *buf = malloc(fsize);
                fread(buf, fsize, 1, f);
                fclose(f);
                sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
                sendBytes(clientS, buf, fsize); 
                free(buf);
            }else{
                reb_server_cerror(clientS, "rebound.html not found in current directory. Try `make rebound.html`.");
                continue;
            }
        }else if (!strcasecmp(uri, "/favicon.ico")) {
                sendBytes(clientS, reb_server_header_png, strlen(reb_server_header_png)); 
                sendBytes(clientS, reb_favicon_png, reb_favicon_len); 
        }else if (!strcasecmp(uri, "/screenshot")) {
            data->need_copy = 1;
            WaitForSingleObject(data->mutex, INFINITE);
            if (content_length==0){
                printf("Received screenshot with size zero.");
                goto screenshot_finish;
            }
            if (r->status != REB_STATUS_SCREENSHOT){
                printf("Received screenshot but did not expect one.\n");
                goto screenshot_finish;
            }
            if (data->screenshot) {
                printf("Unable to receive screenshot as previous screenshot not freed.\n");
                goto screenshot_finish;
            }

            char* dataURL = curLine; // Memory!

            int rc_len = strlen(dataURL)+1;
            char* base64 = strchr(dataURL, ',');
            if (content_length != rc_len){
                printf("Received screenshot with incorrect size.\n");
                goto screenshot_finish;
            }
            if (!base64){
                printf("Unable to decode received screenshot. Data not in dataURL format.\n");
                goto screenshot_finish;
            }
            data->screenshot = base64_decode((unsigned char*)base64+1, strlen(base64+1), &data->N_screenshot);
            if (!data->screenshot){
                printf("An error occured while decoding the screenshot.\n");
            }
            data->r->status = REB_STATUS_PAUSED;
screenshot_finish:
            free(recbuf);
            data->need_copy = 0;
            ReleaseMutex(data->mutex);
            const char* ok = "ok.";
            sendBytes(clientS, reb_server_header, strlen(reb_server_header)); 
            sendBytes(clientS, ok, strlen(ok));
        }else{
            reb_server_cerror(clientS, "Unsupported request.");
            printf("URI: %s\n",uri);
            continue;
        }

        closesocket(clientS);
    }
    WSACleanup();
    return 0;
#endif // _WIN32
}

#endif // SERVER


int reb_simulation_start_server(struct reb_simulation* r, int port){    
#ifdef SERVER
    if (port){
        if (r->server_data){
            reb_simulation_error(r,"Server already started.");
            return -1;
        }
        r->server_data = calloc(sizeof(struct reb_server_data),1);
        r->server_data->r = r;
        r->server_data->port = port;
#ifdef _WIN32
        r->server_data->mutex = CreateMutex(NULL, FALSE, NULL);
        HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)reb_server_start, r->server_data, 0, NULL);
#else // _WIN32
        if (pthread_mutex_init(&(r->server_data->mutex), NULL)){
            reb_simulation_error(r,"Mutex creation failed.");
            return -1;
        }
        int ret_create = pthread_create(&(r->server_data->server_thread),NULL,reb_server_start,r->server_data);
        if (ret_create){
            reb_simulation_error(r, "Error creating server thread.");
            return -1;
        }
#endif // _WIN32
        int maxwait = 100;
        while (r->server_data->ready==0 && maxwait){
            usleep(10000);
            maxwait--;
        }
        if (r->server_data->ready==0){
            reb_simulation_warning(r, "Server did not start immediately. This might just take a little bit longer.");
        }
        return 0;
    }else{
        reb_simulation_error(r, "Cannot start server. Invalid port.");
        return -1;
    }
#else // SERVER
#ifndef SERVERHIDEWARNING
    reb_simulation_error(r, "REBOUND has been compiled without SERVER support.");
#endif // SERVERHIDEWARNING
    return -1;
#endif // SERVER
}

void reb_simulation_stop_server(struct reb_simulation* r){    
#ifdef SERVER
    if (r==NULL) return;
    if (r->server_data){
#ifdef _WIN32
        closesocket(r->server_data->socket); // Will cause thread to exit.
#else // _WIN32
        close(r->server_data->socket); // Will cause thread to exit.
        int ret_cancel = pthread_cancel(r->server_data->server_thread);
        if (ret_cancel==ESRCH){
            printf("Did not find server thread while trying to cancel it.\n");
        }
        void* retval = 0;
        pthread_join(r->server_data->server_thread, &retval);
        if (retval!=PTHREAD_CANCELED){
            printf("An error occured while cancelling server thread.\n");
        }
#endif // _WIN32
        free(r->server_data);
        r->server_data = NULL;
    }
#endif //SERVER
}



back to top