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

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