AcessNative - Changes to allow _compiling_ on windows.
[tpg/acess2.git] / AcessNative / acesskernel_src / server.c
1 /*
2  * Acess2 Native Kernel
3  * - Acess kernel emulation on another OS using SDL and UDP
4  *
5  * Syscall Server
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <SDL/SDL.h>
11 #ifdef __WIN32__
12 # define _WIN32_WINNT 0x0501
13 # include <windows.h>
14 # include <winsock2.h>
15 # include <ws2tcpip.h>
16 # define close(fd)      closesocket(fd)
17 typedef int     socklen_t;
18 #else
19 # include <unistd.h>
20 # include <sys/socket.h>
21 # include <netinet/in.h>
22 # include <arpa/inet.h> // inet_ntop
23 # include "include/windows_helpers.h"
24 #endif
25 #define DONT_INCLUDE_SYSCALL_NAMES
26 #include "../syscalls.h"
27 #include <logdebug.h>   // acess but std
28 #include <errno.h>
29
30 #define USE_TCP 1
31 #define MAX_CLIENTS     16
32
33 // === TYPES ===
34 typedef struct {
35          int    ClientID;
36         SDL_Thread      *WorkerThread;
37         #if USE_TCP
38          int    Socket;
39         #else
40         tRequestHeader  *CurrentRequest;
41         struct sockaddr_in      ClientAddr;
42         SDL_cond        *WaitFlag;
43         SDL_mutex       *Mutex;
44         #endif
45 }       tClient;
46
47 // === IMPORTS ===
48 extern tRequestHeader *SyscallRecieve(tRequestHeader *Request, int *ReturnLength);
49 extern int      Threads_CreateRootProcess(void);
50 extern void     Threads_SetThread(int TID);
51 extern void     *Threads_GetThread(int TID);
52 extern void     Threads_PostEvent(void *Thread, uint32_t Event);
53
54 // === PROTOTYPES ===
55 tClient *Server_GetClient(int ClientID);
56  int    Server_WorkerThread(void *ClientPtr);
57  int    SyscallServer(void);
58  int    Server_ListenThread(void *Unused);
59
60 // === GLOBALS ===
61 #ifdef __WIN32__
62 WSADATA gWinsock;
63 SOCKET  gSocket = INVALID_SOCKET;
64 #else
65 # define INVALID_SOCKET -1
66  int    gSocket = INVALID_SOCKET;
67 #endif
68 tClient gaServer_Clients[MAX_CLIENTS];
69 SDL_Thread      *gpServer_ListenThread;
70
71 // === CODE ===
72 int Server_GetClientID(void)
73 {
74          int    i;
75         Uint32  thisId = SDL_ThreadID();
76         
77         for( i = 0; i < MAX_CLIENTS; i ++ )
78         {
79                 if( SDL_GetThreadID(gaServer_Clients[i].WorkerThread) == thisId )
80                         return gaServer_Clients[i].ClientID;
81         }
82         
83         fprintf(stderr, "ERROR: Server_GetClientID - Thread is not allocated\n");
84         
85         return 0;
86 }
87
88 tClient *Server_GetClient(int ClientID)
89 {
90         tClient *ret = NULL;
91          int    i;
92         
93         // Allocate an ID if needed
94         if(ClientID == 0)
95                 ClientID = Threads_CreateRootProcess();
96         
97         for( i = 0; i < MAX_CLIENTS; i ++ )
98         {
99                 if( gaServer_Clients[i].ClientID == ClientID ) {
100                         return &gaServer_Clients[i];
101                 }
102                 if(!ret && gaServer_Clients[i].ClientID == 0)
103                         ret = &gaServer_Clients[i];
104         }
105         
106         // Uh oh, no free slots
107         // TODO: Dynamic allocation
108         if( !ret )
109                 return NULL;
110         
111         // Allocate a thread for the process
112         ret->ClientID = ClientID;
113         #if USE_TCP
114         ret->Socket = 0;
115         #else
116         ret->CurrentRequest = NULL;
117         #endif
118                 
119         if( !ret->WorkerThread ) {
120                 #if USE_TCP
121                 #else
122                 ret->WaitFlag = SDL_CreateCond();
123                 ret->Mutex = SDL_CreateMutex();
124                 SDL_mutexP( ret->Mutex );
125                 #endif
126                 Log_Debug("Server", "Creating worker for %p", ret);
127                 ret->WorkerThread = SDL_CreateThread( Server_WorkerThread, ret );
128         }
129         
130         return ret;
131 }
132
133 int Server_WorkerThread(void *ClientPtr)
134 {
135         tClient *Client = ClientPtr;
136
137         Log_Debug("Server", "Worker %p", ClientPtr);    
138
139         #if USE_TCP
140
141         while( *((volatile typeof(Client->Socket)*)&Client->Socket) == 0 )
142                 ;
143         Threads_SetThread( Client->ClientID );
144         
145         while( Client->ClientID != -1 )
146         {
147                 fd_set  fds;
148                  int    nfd = Client->Socket+1;
149                 FD_ZERO(&fds);
150                 FD_SET(Client->Socket, &fds);
151                 
152                 int rv = select(nfd, &fds, NULL, NULL, NULL);   // TODO: Timeouts?
153                 if(rv < 0) {
154                         perror("select");
155                         continue ;
156                 }
157                 Log_Debug("Server", "%p: rv=%i", Client, rv);           
158
159                 if( FD_ISSET(Client->Socket, &fds) )
160                 {
161                         const int       ciMaxParamCount = 6;
162                         char    lbuf[sizeof(tRequestHeader) + ciMaxParamCount*sizeof(tRequestValue)];
163                         tRequestHeader  *hdr = (void*)lbuf;
164                         size_t  len = recv(Client->Socket, (void*)hdr, sizeof(*hdr), 0);
165                         Log_Debug("Server", "%i bytes of header", len);
166                         if( len == 0 )  break;
167                         if( len == -1 ) {
168                                 perror("recv header");
169 //                              Log_Warning("Server", "recv() error - %s", strerror(errno));
170                                 break;
171                         }
172                         if( len != sizeof(*hdr) ) {
173                                 // Oops?
174                                 Log_Warning("Server", "FD%i bad sized (%i != exp %i)",
175                                         Client->Socket, len, sizeof(*hdr));
176                                 continue ;
177                         }
178
179                         if( hdr->NParams > ciMaxParamCount ) {
180                                 // Oops.
181                                 Log_Warning("Server", "FD%i too many params (%i > max %i)",
182                                         Client->Socket, hdr->NParams, ciMaxParamCount);
183                                 break ;
184                         }
185
186                         if( hdr->NParams > 0 )
187                         {
188                                 len = recv(Client->Socket, (void*)hdr->Params, hdr->NParams*sizeof(tRequestValue), 0);
189                                 Log_Debug("Server", "%i bytes of params", len);
190                                 if( len != hdr->NParams*sizeof(tRequestValue) ) {
191                                         // Oops.
192                                         perror("recv params");
193                                         Log_Warning("Sever", "Recieving params failed");
194                                         break ;
195                                 }
196                         }
197                         else
198                         {
199                                 Log_Debug("Server", "No params?");
200                         }
201
202                         // Get buffer size
203                         size_t  hdrsize = sizeof(tRequestHeader) + hdr->NParams*sizeof(tRequestValue);
204                         size_t  bufsize = hdrsize;
205                          int    i;
206                         for( i = 0; i < hdr->NParams; i ++ )
207                         {
208                                 if( hdr->Params[i].Flags & ARG_FLAG_ZEROED )
209                                         ;
210                                 else {
211                                         bufsize += hdr->Params[i].Length;
212                                 }
213                         }
214
215                         // Allocate full buffer
216                         hdr = malloc(bufsize);
217                         memcpy(hdr, lbuf, hdrsize);
218                         if( bufsize > hdrsize )
219                         {
220                                 size_t  rem = bufsize - hdrsize;
221                                 char    *ptr = (void*)( hdr->Params + hdr->NParams );
222                                 while( rem )
223                                 {
224                                         len = recv(Client->Socket, ptr, rem, 0);
225                                         Log_Debug("Server", "%i bytes of data", len);
226                                         if( len == -1 ) {
227                                                 // Oops?
228                                                 perror("recv data");
229                                                 Log_Warning("Sever", "Recieving data failed");
230                                                 break ;
231                                         }
232                                         rem -= len;
233                                         ptr += len;
234                                 }
235                                 if( rem ) {
236                                         break;
237                                 }
238                         }
239                         else
240                                 Log_Debug("Server", "no data");
241
242                          int    retlen;
243                         tRequestHeader  *retHeader;
244                         retHeader = SyscallRecieve(hdr, &retlen);
245                         if( !retHeader ) {
246                                 // Some sort of error
247                                 Log_Warning("Server", "SyscallRecieve failed?");
248                                 continue ;
249                         }
250                         
251                         send(Client->Socket, (void*)retHeader, retlen, 0); 
252
253                         // Clean up
254                         free(hdr);
255                 }
256         }
257         #else
258         tRequestHeader  *retHeader;
259         tRequestHeader  errorHeader;
260          int    retSize = 0;
261          int    sentSize;
262          int    cur_client_id = 0;
263         while( Client->ClientID != -1 )
264         {
265                 // Wait for something to do
266                 if( Client->CurrentRequest == NULL )
267                         SDL_CondWait(Client->WaitFlag, Client->Mutex);
268                 if( Client->CurrentRequest == NULL )
269                         continue ;
270                 
271 //              Log_Debug("AcessSrv", "Worker got message %p", Client->CurrentRequest);
272                 
273                 if(Client->ClientID != cur_client_id) {
274 //                      Log_Debug("AcessSrv", "Client thread ID changed from %i to %i",
275 //                              cur_client_id, Client->ClientID);
276                         Threads_SetThread( Client->ClientID );
277                         cur_client_id = Client->ClientID;
278                 }
279                 
280                 // Debug
281                 {
282                         int     callid = Client->CurrentRequest->CallID;
283                         Log_Debug("AcessSrv", "Client %i request %i %s",
284                                 Client->ClientID, callid,
285                                 callid < N_SYSCALLS ? casSYSCALL_NAMES[callid] : "UNK"
286                                 );
287                 }
288                 
289                 // Get the response
290                 retHeader = SyscallRecieve(Client->CurrentRequest, &retSize);
291
292                 if( !retHeader ) {
293                         // Return an error to the client
294                         printf("ERROR: SyscallRecieve failed\n");
295                         errorHeader.CallID = Client->CurrentRequest->CallID;
296                         errorHeader.NParams = 0;
297                         retHeader = &errorHeader;
298                         retSize = sizeof(errorHeader);
299                 }
300                 
301                 // Set ID
302                 retHeader->ClientID = Client->ClientID;
303                 
304                 // Mark the thread as ready for another job
305                 free(Client->CurrentRequest);
306                 Client->CurrentRequest = 0;
307                 
308 //              Log_Debug("AcessSrv", "Sending %i to %x:%i (Client %i)",
309 //                      retSize, ntohl(Client->ClientAddr.sin_addr.s_addr),
310 //                      ntohs(Client->ClientAddr.sin_port),
311 //                      Client->ClientID
312 //                      );
313                 
314                 // Return the data
315                 sentSize = sendto(gSocket, retHeader, retSize, 0,
316                         (struct sockaddr*)&Client->ClientAddr, sizeof(Client->ClientAddr)
317                         );
318                 if( sentSize != retSize ) {
319                         perror("Server_WorkerThread - send");
320                 }
321                 
322                 // Free allocated header
323                 if( retHeader != &errorHeader )
324                         free( retHeader );
325         }
326         #endif
327         Log_Notice("Server", "Terminated Worker %p", ClientPtr);        
328         return 0;
329 }
330
331 int SyscallServer(void)
332 {
333         struct sockaddr_in      server;
334         
335         #ifdef __WIN32__
336         /* Open windows connection */
337         if (WSAStartup(0x0101, &gWinsock) != 0)
338         {
339                 fprintf(stderr, "Could not open Windows connection.\n");
340                 exit(0);
341         }
342         #endif
343         
344         #if USE_TCP
345         // Open TCP Connection
346         gSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
347         #else
348         // Open UDP Connection
349         gSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
350         #endif
351         if (gSocket == INVALID_SOCKET)
352         {
353                 fprintf(stderr, "Could not create socket.\n");
354                 #if __WIN32__
355                 WSACleanup();
356                 #endif
357                 exit(0);
358         }
359         
360         // Set server address
361         memset(&server, 0, sizeof(struct sockaddr_in));
362         server.sin_family = AF_INET;
363         server.sin_port = htons(SERVER_PORT);
364         server.sin_addr.s_addr = htonl(INADDR_ANY);
365         
366         // Bind
367         if( bind(gSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1 )
368         {
369                 fprintf(stderr, "Cannot bind address to socket.\n");
370                 perror("SyscallServer - bind");
371                 #if __WIN32__
372                 closesocket(gSocket);
373                 WSACleanup();
374                 #else
375                 close(gSocket);
376                 #endif
377                 exit(0);
378         }
379         
380         #if USE_TCP
381         listen(gSocket, 5);
382         #endif
383         
384         Log_Notice("AcessSrv", "Listening on 0.0.0.0:%i", SERVER_PORT);
385         gpServer_ListenThread = SDL_CreateThread( Server_ListenThread, NULL );
386         return 0;
387 }
388
389 int Server_Shutdown(void)
390 {
391         close(gSocket);
392         for( int i = 0; i < MAX_CLIENTS; i ++ )
393         {
394                 if( gaServer_Clients[i].ClientID == 0 )
395                         continue ;
396                 Threads_PostEvent( Threads_GetThread(gaServer_Clients[i].ClientID), 0 );
397                 gaServer_Clients[i].ClientID = -1;
398                 #if USE_TCP
399                 close(gaServer_Clients[i].Socket);
400                 #else
401                 SDL_CondSignal(gaServer_Clients[i].WaitFlag);
402                 #endif
403         }
404         return 0;
405 }
406
407 int Server_ListenThread(void *Unused)
408 {       
409         // Wait for something to do :)
410         for( ;; )
411         {
412                 #if USE_TCP
413                 struct sockaddr_in      clientaddr;
414                 socklen_t       clientSize = sizeof(clientaddr);
415                  int    clientSock = accept(gSocket, (struct sockaddr*)&clientaddr, &clientSize);
416                 if( clientSock < 0 ) {
417                         perror("SyscallServer - accept");
418                         break ;
419                 }
420
421                 char    addrstr[4*8+8+1];
422                 getnameinfo((struct sockaddr*)&clientaddr, sizeof(clientaddr),
423                         addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST);
424                 Log_Debug("Server", "Client connection %s:%i", addrstr, ntohs(clientaddr.sin_port));
425                 
426                 // Perform auth
427                 size_t  len;
428                 tRequestAuthHdr authhdr;
429                 len = recv(clientSock, (void*)&authhdr, sizeof(authhdr), 0);
430                 if( len != sizeof(authhdr) ) {
431                         // Some form of error?
432                         Log_Warning("Server", "Client auth block bad size (%i != exp %i)",
433                                 len, sizeof(authhdr));
434                         close(clientSock);
435                         continue ;
436                 }
437                 
438                 Log_Debug("Server", "Client assumed PID %i", authhdr.pid);
439
440                 tClient *client;
441                 if( authhdr.pid == 0 ) {
442                         // Allocate PID and client structure/thread
443                         client = Server_GetClient(0);
444                         client->Socket = clientSock;
445                         authhdr.pid = client->ClientID;
446                 }
447                 else {
448                         // Get client structure and make sure it's unused
449                         // - Auth token / verifcation?
450                         client = Server_GetClient(authhdr.pid);
451                         if( !client ) {
452                                 Log_Warning("Server", "Can't allocate a client struct for %s:%i",
453                                         addrstr, clientaddr.sin_port);
454                                 close(clientSock);
455                                 continue ;
456                         }
457                         if( client->Socket != 0 ) {
458                                 Log_Warning("Server", "Client (%i)%p owned by FD%i but %s:%i tried to use it",
459                                         authhdr.pid, client, addrstr, clientaddr.sin_port);
460                                 close(clientSock);
461                                 continue;
462                         }
463                         else {
464                                 client->Socket = clientSock;
465                         }
466                 }
467                 Log_Debug("Server", "Client given PID %i - info %p", authhdr.pid, client);
468                 
469                 len = send(clientSock, (void*)&authhdr, sizeof(authhdr), 0);
470                 if( len != sizeof(authhdr) ) {
471                         // Ok, this is an error
472                         perror("Sending auth reply");
473                 }
474
475                 // All done, client thread should be watching now               
476
477                 #else
478                 char    data[BUFSIZ];
479                 tRequestHeader  *req = (void*)data;
480                 struct sockaddr_in      addr;
481                 uint    clientSize = sizeof(addr);
482                  int    length;
483                 tClient *client;
484                 
485                 length = recvfrom(gSocket, data, BUFSIZ, 0, (struct sockaddr*)&addr, &clientSize);
486                 
487                 if( length == -1 ) {
488                         perror("SyscallServer - recv");
489                         break;
490                 }
491                 
492                 // Hand off to a worker thread
493                 // - TODO: Actually have worker threads
494 //              Log_Debug("Server", "%i bytes from %x:%i", length,
495 //                      ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
496                 
497                 client = Server_GetClient(req->ClientID);
498                 // NOTE: Hack - Should check if all zero
499                 if( req->ClientID == 0 || client->ClientAddr.sin_port == 0 )
500                 {
501                         memcpy(&client->ClientAddr, &addr, sizeof(addr));
502                 }
503                 else if( memcmp(&client->ClientAddr, &addr, sizeof(addr)) != 0 )
504                 {
505                         printf("ClientID %i used by %x:%i\n",
506                                 client->ClientID, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
507                         printf(" actually owned by %x:%i\n",
508                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
509                         continue;
510                 }
511                 
512                 if( client->CurrentRequest ) {
513                         printf("Worker thread for %x:%i is busy\n",
514                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
515                         continue;
516                 }
517                 
518 //              Log_Debug("AcessSrv", "Message from Client %i (%p)",
519 //                      client->ClientID, client);
520
521                 // Make a copy of the request data      
522                 req = malloc(length);
523                 memcpy(req, data, length);
524                 client->CurrentRequest = req;
525                 SDL_CondSignal(client->WaitFlag);
526                 #endif
527         }
528         return -1;
529 }

UCC git Repository :: git.ucc.asn.au