AcessNative - Loads login process and runs again (shell doesn't spawn)
[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                 ret->WorkerThread = SDL_CreateThread( Server_WorkerThread, ret );
122         }
123         
124         return ret;
125 }
126
127 int Server_WorkerThread(void *ClientPtr)
128 {
129         tClient *Client = ClientPtr;
130
131         Log_Debug("Server", "Worker %p", ClientPtr);    
132
133         #if USE_TCP
134
135         while( *((volatile typeof(Client->Socket)*)&Client->Socket) == 0 )
136                 SDL_Delay(10);
137         Threads_SetThread( Client->ClientID );
138         
139         for( ;; )
140         {
141                 fd_set  fds;
142                  int    nfd = Client->Socket+1;
143                 FD_ZERO(&fds);
144                 FD_SET(Client->Socket, &fds);
145                 
146                 int rv = select(nfd, &fds, NULL, NULL, NULL);   // TODO: Timeouts?
147                 if(rv < 0) {
148                         perror("select");
149                         continue ;
150                 }
151                 Log_Debug("Server", "%p: rv=%i", Client, rv);           
152
153                 if( FD_ISSET(Client->Socket, &fds) )
154                 {
155                         const int       ciMaxParamCount = 6;
156                         char    lbuf[sizeof(tRequestHeader) + ciMaxParamCount*sizeof(tRequestValue)];
157                         tRequestHeader  *hdr = (void*)lbuf;
158                         size_t  len = recv(Client->Socket, hdr, sizeof(*hdr), 0);
159                         Log_Debug("Server", "%i bytes of header", len);
160                         if( len == 0 )  break;
161                         if( len != sizeof(*hdr) ) {
162                                 // Oops?
163                                 Log_Warning("Server", "FD%i bad sized (%i != exp %i)",
164                                         Client->Socket, len, sizeof(*hdr));
165                                 continue ;
166                         }
167
168                         if( hdr->NParams > ciMaxParamCount ) {
169                                 // Oops.
170                                 Log_Warning("Server", "FD%i too many params (%i > max %i)",
171                                         Client->Socket, hdr->NParams, ciMaxParamCount);
172                                 continue ;
173                         }
174
175                         len = recv(Client->Socket, hdr->Params, hdr->NParams*sizeof(tRequestValue), 0);
176                         Log_Debug("Server", "%i bytes of params", len);
177                         if( len != hdr->NParams*sizeof(tRequestValue) ) {
178                                 // Oops.
179                         }
180
181                         // Get buffer size
182                         size_t  hdrsize = sizeof(tRequestHeader) + hdr->NParams*sizeof(tRequestValue);
183                         size_t  bufsize = hdrsize;
184                          int    i;
185                         for( i = 0; i < hdr->NParams; i ++ )
186                         {
187                                 if( hdr->Params[i].Flags & ARG_FLAG_ZEROED )
188                                         ;
189                                 else {
190                                         bufsize += hdr->Params[i].Length;
191                                 }
192                         }
193
194                         // Allocate full buffer
195                         hdr = malloc(bufsize);
196                         memcpy(hdr, lbuf, hdrsize);
197                         len = recv(Client->Socket, hdr->Params + hdr->NParams, bufsize - hdrsize, 0);
198                         Log_Debug("Server", "%i bytes of data", len);
199                         if( len != bufsize - hdrsize ) {
200                                 // Oops?
201                         }
202
203                          int    retlen;
204                         tRequestHeader  *retHeader;
205                         retHeader = SyscallRecieve(hdr, &retlen);
206                         if( !retHeader ) {
207                                 // Some sort of error
208                                 Log_Warning("Server", "SyscallRecieve failed?");
209                                 continue ;
210                         }
211                         
212                         send(Client->Socket, retHeader, retlen, 0); 
213
214                         // Clean up
215                         free(hdr);
216                 }
217         }
218         #else
219         tRequestHeader  *retHeader;
220         tRequestHeader  errorHeader;
221          int    retSize = 0;
222          int    sentSize;
223          int    cur_client_id = 0;
224         for( ;; )
225         {
226                 // Wait for something to do
227                 while( Client->CurrentRequest == NULL )
228                         SDL_CondWait(Client->WaitFlag, Client->Mutex);
229                 
230 //              Log_Debug("AcessSrv", "Worker got message %p", Client->CurrentRequest);
231                 
232                 if(Client->ClientID != cur_client_id) {
233 //                      Log_Debug("AcessSrv", "Client thread ID changed from %i to %i",
234 //                              cur_client_id, Client->ClientID);
235                         Threads_SetThread( Client->ClientID );
236                         cur_client_id = Client->ClientID;
237                 }
238                 
239                 // Debug
240                 {
241                         int     callid = Client->CurrentRequest->CallID;
242                         Log_Debug("AcessSrv", "Client %i request %i %s",
243                                 Client->ClientID, callid,
244                                 callid < N_SYSCALLS ? casSYSCALL_NAMES[callid] : "UNK"
245                                 );
246                 }
247                 
248                 // Get the response
249                 retHeader = SyscallRecieve(Client->CurrentRequest, &retSize);
250
251                 if( !retHeader ) {
252                         // Return an error to the client
253                         printf("ERROR: SyscallRecieve failed\n");
254                         errorHeader.CallID = Client->CurrentRequest->CallID;
255                         errorHeader.NParams = 0;
256                         retHeader = &errorHeader;
257                         retSize = sizeof(errorHeader);
258                 }
259                 
260                 // Set ID
261                 retHeader->ClientID = Client->ClientID;
262                 
263                 // Mark the thread as ready for another job
264                 free(Client->CurrentRequest);
265                 Client->CurrentRequest = 0;
266                 
267 //              Log_Debug("AcessSrv", "Sending %i to %x:%i (Client %i)",
268 //                      retSize, ntohl(Client->ClientAddr.sin_addr.s_addr),
269 //                      ntohs(Client->ClientAddr.sin_port),
270 //                      Client->ClientID
271 //                      );
272                 
273                 // Return the data
274                 sentSize = sendto(gSocket, retHeader, retSize, 0,
275                         (struct sockaddr*)&Client->ClientAddr, sizeof(Client->ClientAddr)
276                         );
277                 if( sentSize != retSize ) {
278                         perror("Server_WorkerThread - send");
279                 }
280                 
281                 // Free allocated header
282                 if( retHeader != &errorHeader )
283                         free( retHeader );
284         }
285         #endif
286         Log_Notice("Server", "Terminated Worker %p", ClientPtr);        
287         return 0;
288 }
289
290 int SyscallServer(void)
291 {
292         struct sockaddr_in      server;
293         
294         #ifdef __WIN32__
295         /* Open windows connection */
296         if (WSAStartup(0x0101, &gWinsock) != 0)
297         {
298                 fprintf(stderr, "Could not open Windows connection.\n");
299                 exit(0);
300         }
301         #endif
302         
303         #if USE_TCP
304         // Open TCP Connection
305         gSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
306         #else
307         // Open UDP Connection
308         gSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
309         #endif
310         if (gSocket == INVALID_SOCKET)
311         {
312                 fprintf(stderr, "Could not create socket.\n");
313                 #if __WIN32__
314                 WSACleanup();
315                 #endif
316                 exit(0);
317         }
318         
319         // Set server address
320         memset(&server, 0, sizeof(struct sockaddr_in));
321         server.sin_family = AF_INET;
322         server.sin_port = htons(SERVER_PORT);
323         server.sin_addr.s_addr = htonl(INADDR_ANY);
324         
325         // Bind
326         if( bind(gSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1 )
327         {
328                 fprintf(stderr, "Cannot bind address to socket.\n");
329                 perror("SyscallServer - bind");
330                 #if __WIN32__
331                 closesocket(gSocket);
332                 WSACleanup();
333                 #else
334                 close(gSocket);
335                 #endif
336                 exit(0);
337         }
338         
339         #if USE_TCP
340         listen(gSocket, 5);
341         #endif
342         
343         Log_Notice("AcessSrv", "Listening on 0.0.0.0:%i", SERVER_PORT);
344         gpServer_ListenThread = SDL_CreateThread( Server_ListenThread, NULL );
345         return 0;
346 }
347
348 int Server_ListenThread(void *Unused)
349 {       
350         // Wait for something to do :)
351         for( ;; )
352         {
353                 #if USE_TCP
354                 struct sockaddr_in      clientaddr;
355                 socklen_t       clientSize = sizeof(clientaddr);
356                  int    clientSock = accept(gSocket, (struct sockaddr*)&clientaddr, &clientSize);
357                 if( clientSock < 0 ) {
358                         perror("SyscallServer - accept");
359                         break ;
360                 }
361
362                 char    addrstr[4*8+8+1];
363                 inet_ntop(clientaddr.sin_family, &clientaddr.sin_addr, addrstr, sizeof(addrstr));
364                 Log_Debug("Server", "Client connection %s:%i\n", addrstr, ntohs(clientaddr.sin_port));
365                 
366                 // Perform auth
367                 size_t  len;
368                 tRequestAuthHdr authhdr;
369                 len = recv(clientSock, &authhdr, sizeof(authhdr), 0);
370                 if( len != sizeof(authhdr) ) {
371                         // Some form of error?
372                         Log_Warning("Server", "Client auth block bad size (%i != exp %i)",
373                                 len, sizeof(authhdr));
374                         continue ;
375                 }
376                 
377                 Log_Debug("Server", "Client assumed PID %i", authhdr.pid);
378
379                 tClient *client;
380                 if( authhdr.pid == 0 ) {
381                         // Allocate PID and client structure/thread
382                         client = Server_GetClient(0);
383                         client->Socket = clientSock;
384                         authhdr.pid = client->ClientID;
385                 }
386                 else {
387                         // Get client structure and make sure it's unused
388                         // - Auth token / verifcation?
389                         client = Server_GetClient(authhdr.pid);
390                         if( client->Socket != 0 ) {
391                                 Log_Warning("Server", "Client (%i)%p owned by FD%i but %s:%i tried to use it",
392                                         authhdr.pid, client, addrstr, clientaddr.sin_port);
393                                 authhdr.pid = 0;
394                         }
395                         else {
396                                 client->Socket = clientSock;
397                         }
398                 }
399                 Log_Debug("Server", "Client given PID %i - info %p", authhdr.pid, client);
400                 
401                 len = send(clientSock, &authhdr, sizeof(authhdr), 0);
402                 if( len != sizeof(authhdr) ) {
403                         // Ok, this is an error
404                         perror("Sending auth reply");
405                 }
406
407                 // All done, client thread should be watching now               
408
409                 #else
410                 char    data[BUFSIZ];
411                 tRequestHeader  *req = (void*)data;
412                 struct sockaddr_in      addr;
413                 uint    clientSize = sizeof(addr);
414                  int    length;
415                 tClient *client;
416                 
417                 length = recvfrom(gSocket, data, BUFSIZ, 0, (struct sockaddr*)&addr, &clientSize);
418                 
419                 if( length == -1 ) {
420                         perror("SyscallServer - recv");
421                         break;
422                 }
423                 
424                 // Hand off to a worker thread
425                 // - TODO: Actually have worker threads
426 //              Log_Debug("Server", "%i bytes from %x:%i", length,
427 //                      ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
428                 
429                 client = Server_GetClient(req->ClientID);
430                 // NOTE: Hack - Should check if all zero
431                 if( req->ClientID == 0 || client->ClientAddr.sin_port == 0 )
432                 {
433                         memcpy(&client->ClientAddr, &addr, sizeof(addr));
434                 }
435                 else if( memcmp(&client->ClientAddr, &addr, sizeof(addr)) != 0 )
436                 {
437                         printf("ClientID %i used by %x:%i\n",
438                                 client->ClientID, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
439                         printf(" actually owned by %x:%i\n",
440                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
441                         continue;
442                 }
443                 
444                 if( client->CurrentRequest ) {
445                         printf("Worker thread for %x:%i is busy\n",
446                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
447                         continue;
448                 }
449                 
450 //              Log_Debug("AcessSrv", "Message from Client %i (%p)",
451 //                      client->ClientID, client);
452
453                 // Make a copy of the request data      
454                 req = malloc(length);
455                 memcpy(req, data, length);
456                 client->CurrentRequest = req;
457                 SDL_CondSignal(client->WaitFlag);
458                 #endif
459         }
460         return -1;
461 }

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