AcessNative - Mouse implimented, woot!
[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 )  break;
165                         if( len == -1 ) {
166                                 perror("recv header");
167 //                              Log_Warning("Server", "recv() error - %s", strerror(errno));
168                                 break;
169                         }
170                         if( len != sizeof(*hdr) ) {
171                                 // Oops?
172                                 Log_Warning("Server", "FD%i bad sized (%i != exp %i)",
173                                         Client->Socket, len, sizeof(*hdr));
174                                 continue ;
175                         }
176
177                         if( hdr->NParams > ciMaxParamCount ) {
178                                 // Oops.
179                                 Log_Warning("Server", "FD%i too many params (%i > max %i)",
180                                         Client->Socket, hdr->NParams, ciMaxParamCount);
181                                 break ;
182                         }
183
184                         if( hdr->NParams > 0 )
185                         {
186                                 len = recv(Client->Socket, (void*)hdr->Params, hdr->NParams*sizeof(tRequestValue), 0);
187 //                              Log_Debug("Server", "%i bytes of params", len);
188                                 if( len != hdr->NParams*sizeof(tRequestValue) ) {
189                                         // Oops.
190                                         perror("recv params");
191                                         Log_Warning("Sever", "Recieving params failed");
192                                         break ;
193                                 }
194                         }
195                         else
196                         {
197 //                              Log_Debug("Server", "No params?");
198                         }
199
200                         // Get buffer size
201                         size_t  hdrsize = sizeof(tRequestHeader) + hdr->NParams*sizeof(tRequestValue);
202                         size_t  bufsize = hdrsize;
203                          int    i;
204                         for( i = 0; i < hdr->NParams; i ++ )
205                         {
206                                 if( hdr->Params[i].Flags & ARG_FLAG_ZEROED )
207                                         ;
208                                 else {
209                                         bufsize += hdr->Params[i].Length;
210                                 }
211                         }
212
213                         // Allocate full buffer
214                         hdr = malloc(bufsize);
215                         memcpy(hdr, lbuf, hdrsize);
216                         if( bufsize > hdrsize )
217                         {
218                                 size_t  rem = bufsize - hdrsize;
219                                 char    *ptr = (void*)( hdr->Params + hdr->NParams );
220                                 while( rem )
221                                 {
222                                         len = recv(Client->Socket, ptr, rem, 0);
223 //                                      Log_Debug("Server", "%i bytes of data", len);
224                                         if( len == -1 ) {
225                                                 // Oops?
226                                                 perror("recv data");
227                                                 Log_Warning("Sever", "Recieving data failed");
228                                                 break ;
229                                         }
230                                         rem -= len;
231                                         ptr += len;
232                                 }
233                                 if( rem ) {
234                                         break;
235                                 }
236                         }
237 //                      else
238 //                              Log_Debug("Server", "no data");
239
240                          int    retlen;
241                         tRequestHeader  *retHeader;
242                         retHeader = SyscallRecieve(hdr, &retlen);
243                         if( !retHeader ) {
244                                 // Some sort of error
245                                 Log_Warning("Server", "SyscallRecieve failed?");
246                                 continue ;
247                         }
248                         
249                         send(Client->Socket, (void*)retHeader, retlen, 0); 
250
251                         // Clean up
252                         free(hdr);
253                 }
254         }
255         #else
256         tRequestHeader  *retHeader;
257         tRequestHeader  errorHeader;
258          int    retSize = 0;
259          int    sentSize;
260          int    cur_client_id = 0;
261         while( Client->ClientID != -1 )
262         {
263                 // Wait for something to do
264                 if( Client->CurrentRequest == NULL )
265                         SDL_CondWait(Client->WaitFlag, Client->Mutex);
266                 if( Client->CurrentRequest == NULL )
267                         continue ;
268                 
269 //              Log_Debug("AcessSrv", "Worker got message %p", Client->CurrentRequest);
270                 
271                 if(Client->ClientID != cur_client_id) {
272 //                      Log_Debug("AcessSrv", "Client thread ID changed from %i to %i",
273 //                              cur_client_id, Client->ClientID);
274                         Threads_SetThread( Client->ClientID );
275                         cur_client_id = Client->ClientID;
276                 }
277                 
278                 // Debug
279                 {
280                         int     callid = Client->CurrentRequest->CallID;
281                         Log_Debug("AcessSrv", "Client %i request %i %s",
282                                 Client->ClientID, callid,
283                                 callid < N_SYSCALLS ? casSYSCALL_NAMES[callid] : "UNK"
284                                 );
285                 }
286                 
287                 // Get the response
288                 retHeader = SyscallRecieve(Client->CurrentRequest, &retSize);
289
290                 if( !retHeader ) {
291                         // Return an error to the client
292                         printf("ERROR: SyscallRecieve failed\n");
293                         errorHeader.CallID = Client->CurrentRequest->CallID;
294                         errorHeader.NParams = 0;
295                         retHeader = &errorHeader;
296                         retSize = sizeof(errorHeader);
297                 }
298                 
299                 // Set ID
300                 retHeader->ClientID = Client->ClientID;
301                 
302                 // Mark the thread as ready for another job
303                 free(Client->CurrentRequest);
304                 Client->CurrentRequest = 0;
305                 
306 //              Log_Debug("AcessSrv", "Sending %i to %x:%i (Client %i)",
307 //                      retSize, ntohl(Client->ClientAddr.sin_addr.s_addr),
308 //                      ntohs(Client->ClientAddr.sin_port),
309 //                      Client->ClientID
310 //                      );
311                 
312                 // Return the data
313                 sentSize = sendto(gSocket, retHeader, retSize, 0,
314                         (struct sockaddr*)&Client->ClientAddr, sizeof(Client->ClientAddr)
315                         );
316                 if( sentSize != retSize ) {
317                         perror("Server_WorkerThread - send");
318                 }
319                 
320                 // Free allocated header
321                 if( retHeader != &errorHeader )
322                         free( retHeader );
323         }
324         #endif
325         Log_Notice("Server", "Terminated Worker %p", ClientPtr);        
326         return 0;
327 }
328
329 int SyscallServer(void)
330 {
331         struct sockaddr_in      server;
332         
333         #ifdef __WIN32__
334         /* Open windows connection */
335         if (WSAStartup(0x0101, &gWinsock) != 0)
336         {
337                 fprintf(stderr, "Could not open Windows connection.\n");
338                 exit(0);
339         }
340         #endif
341         
342         #if USE_TCP
343         // Open TCP Connection
344         gSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
345         #else
346         // Open UDP Connection
347         gSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
348         #endif
349         if (gSocket == INVALID_SOCKET)
350         {
351                 fprintf(stderr, "Could not create socket.\n");
352                 #if __WIN32__
353                 WSACleanup();
354                 #endif
355                 exit(0);
356         }
357         
358         // Set server address
359         memset(&server, 0, sizeof(struct sockaddr_in));
360         server.sin_family = AF_INET;
361         server.sin_port = htons(SERVER_PORT);
362         server.sin_addr.s_addr = htonl(INADDR_ANY);
363         
364         // Bind
365         if( bind(gSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1 )
366         {
367                 fprintf(stderr, "Cannot bind address to socket.\n");
368                 perror("SyscallServer - bind");
369                 #if __WIN32__
370                 closesocket(gSocket);
371                 WSACleanup();
372                 #else
373                 close(gSocket);
374                 #endif
375                 exit(0);
376         }
377         
378         #if USE_TCP
379         listen(gSocket, 5);
380         #endif
381         
382         Log_Notice("AcessSrv", "Listening on 0.0.0.0:%i", SERVER_PORT);
383         gpServer_ListenThread = SDL_CreateThread( Server_ListenThread, NULL );
384         return 0;
385 }
386
387 int Server_Shutdown(void)
388 {
389         close(gSocket);
390         for( int i = 0; i < MAX_CLIENTS; i ++ )
391         {
392                 if( gaServer_Clients[i].ClientID == 0 )
393                         continue ;
394                 Threads_PostEvent( Threads_GetThread(gaServer_Clients[i].ClientID), 0 );
395                 gaServer_Clients[i].ClientID = -1;
396                 #if USE_TCP
397                 close(gaServer_Clients[i].Socket);
398                 #else
399                 SDL_CondSignal(gaServer_Clients[i].WaitFlag);
400                 #endif
401         }
402         return 0;
403 }
404
405 int Server_ListenThread(void *Unused)
406 {       
407         // Wait for something to do :)
408         for( ;; )
409         {
410                 #if USE_TCP
411                 struct sockaddr_in      clientaddr;
412                 socklen_t       clientSize = sizeof(clientaddr);
413                  int    clientSock = accept(gSocket, (struct sockaddr*)&clientaddr, &clientSize);
414                 if( clientSock < 0 ) {
415                         perror("SyscallServer - accept");
416                         break ;
417                 }
418
419                 char    addrstr[4*8+8+1];
420                 getnameinfo((struct sockaddr*)&clientaddr, sizeof(clientaddr),
421                         addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST);
422                 Log_Debug("Server", "Client connection %s:%i", addrstr, ntohs(clientaddr.sin_port));
423                 
424                 // Perform auth
425                 size_t  len;
426                 tRequestAuthHdr authhdr;
427                 len = recv(clientSock, (void*)&authhdr, sizeof(authhdr), 0);
428                 if( len != sizeof(authhdr) ) {
429                         // Some form of error?
430                         Log_Warning("Server", "Client auth block bad size (%i != exp %i)",
431                                 len, sizeof(authhdr));
432                         close(clientSock);
433                         continue ;
434                 }
435                 
436                 Log_Debug("Server", "Client assumed PID %i", authhdr.pid);
437
438                 tClient *client;
439                 if( authhdr.pid == 0 ) {
440                         // Allocate PID and client structure/thread
441                         client = Server_GetClient(0);
442                         client->Socket = clientSock;
443                         authhdr.pid = client->ClientID;
444                 }
445                 else {
446                         // Get client structure and make sure it's unused
447                         // - Auth token / verifcation?
448                         client = Server_GetClient(authhdr.pid);
449                         if( !client ) {
450                                 Log_Warning("Server", "Can't allocate a client struct for %s:%i",
451                                         addrstr, clientaddr.sin_port);
452                                 close(clientSock);
453                                 continue ;
454                         }
455                         if( client->Socket != 0 ) {
456                                 Log_Warning("Server", "Client (%i)%p owned by FD%i but %s:%i tried to use it",
457                                         authhdr.pid, client, addrstr, clientaddr.sin_port);
458                                 close(clientSock);
459                                 continue;
460                         }
461                         else {
462                                 client->Socket = clientSock;
463                         }
464                 }
465                 Log_Debug("Server", "Client given PID %i - info %p", authhdr.pid, client);
466                 
467                 len = send(clientSock, (void*)&authhdr, sizeof(authhdr), 0);
468                 if( len != sizeof(authhdr) ) {
469                         // Ok, this is an error
470                         perror("Sending auth reply");
471                 }
472
473                 // All done, client thread should be watching now               
474
475                 #else
476                 char    data[BUFSIZ];
477                 tRequestHeader  *req = (void*)data;
478                 struct sockaddr_in      addr;
479                 uint    clientSize = sizeof(addr);
480                  int    length;
481                 tClient *client;
482                 
483                 length = recvfrom(gSocket, data, BUFSIZ, 0, (struct sockaddr*)&addr, &clientSize);
484                 
485                 if( length == -1 ) {
486                         perror("SyscallServer - recv");
487                         break;
488                 }
489                 
490                 // Hand off to a worker thread
491                 // - TODO: Actually have worker threads
492 //              Log_Debug("Server", "%i bytes from %x:%i", length,
493 //                      ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
494                 
495                 client = Server_GetClient(req->ClientID);
496                 // NOTE: Hack - Should check if all zero
497                 if( req->ClientID == 0 || client->ClientAddr.sin_port == 0 )
498                 {
499                         memcpy(&client->ClientAddr, &addr, sizeof(addr));
500                 }
501                 else if( memcmp(&client->ClientAddr, &addr, sizeof(addr)) != 0 )
502                 {
503                         printf("ClientID %i used by %x:%i\n",
504                                 client->ClientID, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
505                         printf(" actually owned by %x:%i\n",
506                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
507                         continue;
508                 }
509                 
510                 if( client->CurrentRequest ) {
511                         printf("Worker thread for %x:%i is busy\n",
512                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
513                         continue;
514                 }
515                 
516 //              Log_Debug("AcessSrv", "Message from Client %i (%p)",
517 //                      client->ClientID, client);
518
519                 // Make a copy of the request data      
520                 req = malloc(length);
521                 memcpy(req, data, length);
522                 client->CurrentRequest = req;
523                 SDL_CondSignal(client->WaitFlag);
524                 #endif
525         }
526         return -1;
527 }

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