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

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