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

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