AcessNative - Fixes mostly, still not yet complete
[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 #endif
19 #include "../syscalls.h"
20 //#include <debug.h>
21
22 #define USE_TCP 0
23 #define MAX_CLIENTS     16
24
25 // === TYPES ===
26 typedef struct {
27          int    ClientID;
28         SDL_Thread      *WorkerThread;
29         #if USE_TCP
30         #else
31         tRequestHeader  *CurrentRequest;
32         struct sockaddr_in      ClientAddr;
33         SDL_cond        *WaitFlag;
34         SDL_mutex       *Mutex;
35         #endif
36 }       tClient;
37
38 // === IMPORTS ===
39 extern tRequestHeader *SyscallRecieve(tRequestHeader *Request, int *ReturnLength);
40 extern int      Threads_CreateRootProcess(void);
41 // HACK: Should have these in a header
42 extern void     Log_Debug(const char *Subsys, const char *Message, ...);
43 extern void     Log_Notice(const char *Subsys, const char *Message, ...);
44
45 // === PROTOTYPES ===
46 tClient *Server_GetClient(int ClientID);
47  int    Server_WorkerThread(void *ClientPtr);
48  int    SyscallServer(void);
49
50 // === GLOBALS ===
51 #ifdef __WIN32__
52 WSADATA gWinsock;
53 SOCKET  gSocket = INVALID_SOCKET;
54 #else
55 # define INVALID_SOCKET -1
56  int    gSocket = INVALID_SOCKET;
57 #endif
58 tClient gaServer_Clients[MAX_CLIENTS];
59
60 // === CODE ===
61 int Server_GetClientID(void)
62 {
63          int    i;
64         Uint32  thisId = SDL_ThreadID();
65         
66         for( i = 0; i < MAX_CLIENTS; i ++ )
67         {
68                 if( SDL_GetThreadID(gaServer_Clients[i].WorkerThread) == thisId )
69                         return gaServer_Clients[i].ClientID;
70         }
71         
72         fprintf(stderr, "ERROR: Server_GetClientID - Thread is not allocated\n");
73         
74         return 0;
75 }
76
77 tClient *Server_GetClient(int ClientID)
78 {
79         tClient *ret = NULL;
80          int    i;
81         
82         // Allocate an ID if needed
83         if(ClientID == 0)
84                 ClientID = Threads_CreateRootProcess();
85         
86         for( i = 0; i < MAX_CLIENTS; i ++ )
87         {
88                 if( gaServer_Clients[i].ClientID == ClientID ) {
89                         return &gaServer_Clients[i];
90                 }
91                 if(!ret && gaServer_Clients[i].ClientID == 0)
92                         ret = &gaServer_Clients[i];
93         }
94         
95         // Uh oh, no free slots
96         // TODO: Dynamic allocation
97         if( !ret )
98                 return NULL;
99         
100         // Allocate a thread for the process
101         ret->ClientID = ClientID;
102         ret->CurrentRequest = NULL;
103                 
104         if( !ret->WorkerThread ) {
105                 ret->WaitFlag = SDL_CreateCond();
106                 ret->Mutex = SDL_CreateMutex();
107                 SDL_mutexP( ret->Mutex );
108                 ret->WorkerThread = SDL_CreateThread( Server_WorkerThread, ret );
109         }
110         
111         return ret;
112 }
113
114 int Server_WorkerThread(void *ClientPtr)
115 {
116         tClient *Client = ClientPtr;
117         tRequestHeader  *retHeader;
118         tRequestHeader  errorHeader;
119          int    retSize = 0;
120          int    sentSize;
121          int    cur_client_id = 0;
122         
123         #if USE_TCP
124         #else
125         for( ;; )
126         {
127                 // Wait for something to do
128                 while( Client->CurrentRequest == NULL )
129                         SDL_CondWait(Client->WaitFlag, Client->Mutex);
130                 
131                 if(Client->ClientID != cur_client_id) {
132                         Threads_SetThread( Client->ClientID );
133                         cur_client_id = Client->ClientID;
134                 }
135                 
136                 Log_Debug("AcessSrv", "Worker %i takes %p",
137                         Client->ClientID, Client->CurrentRequest);
138                 
139                 // Get the response
140                 retHeader = SyscallRecieve(Client->CurrentRequest, &retSize);
141                 
142                 if( !retHeader ) {
143                         // Return an error to the client
144                         printf("ERROR: SyscallRecieve failed\n");
145                         errorHeader.CallID = Client->CurrentRequest->CallID;
146                         errorHeader.NParams = 0;
147                         retHeader = &errorHeader;
148                         retSize = sizeof(errorHeader);
149                 }
150                 
151                 // Set ID
152                 retHeader->ClientID = Client->ClientID;
153                 
154                 // Mark the thread as ready for another job
155                 Client->CurrentRequest = 0;
156                 
157                 Log_Debug("AcessSrv", "Sending %i to %x:%i (Client %i)",
158                         retSize, ntohl(Client->ClientAddr.sin_addr.s_addr),
159                         ntohs(Client->ClientAddr.sin_port),
160                         Client->ClientID
161                         );
162                 
163                 // Return the data
164                 sentSize = sendto(gSocket, retHeader, retSize, 0,
165                         (struct sockaddr*)&Client->ClientAddr, sizeof(Client->ClientAddr)
166                         );
167                 if( sentSize != retSize ) {
168                         perror("Server_WorkerThread - send");
169                 }
170                 
171                 // Free allocated header
172                 if( retHeader != &errorHeader )
173                         free( retHeader );
174         }
175         #endif
176 }
177
178 int SyscallServer(void)
179 {
180         struct sockaddr_in      server;
181         
182         #ifdef __WIN32__
183         /* Open windows connection */
184         if (WSAStartup(0x0101, &gWinsock) != 0)
185         {
186                 fprintf(stderr, "Could not open Windows connection.\n");
187                 exit(0);
188         }
189         #endif
190         
191         #if USE_TCP
192         // Open TCP Connection
193         gSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
194         #else
195         // Open UDP Connection
196         gSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
197         #endif
198         if (gSocket == INVALID_SOCKET)
199         {
200                 fprintf(stderr, "Could not create socket.\n");
201                 #if __WIN32__
202                 WSACleanup();
203                 #endif
204                 exit(0);
205         }
206         
207         // Set server address
208         memset(&server, 0, sizeof(struct sockaddr_in));
209         server.sin_family = AF_INET;
210         server.sin_port = htons(SERVER_PORT);
211         server.sin_addr.s_addr = htonl(INADDR_ANY);
212         
213         // Bind
214         if( bind(gSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1 )
215         {
216                 fprintf(stderr, "Cannot bind address to socket.\n");
217                 perror("SyscallServer - bind");
218                 #if __WIN32__
219                 closesocket(gSocket);
220                 WSACleanup();
221                 #else
222                 close(gSocket);
223                 #endif
224                 exit(0);
225         }
226         
227         #if USE_TCP
228         listen(gSocket, 5);
229         #endif
230         
231         Log_Notice("AcessSrv", "Listening on 0.0.0.0:%i", SERVER_PORT);
232         
233         // Wait for something to do :)
234         for( ;; )
235         {
236                 #if USE_TCP
237                 struct sockaddr_in      client;
238                 uint    clientSize = sizeof(client);
239                  int    clientSock = accept(gSocket, (struct sockaddr*)&client, &clientSize);
240                 if( clientSock < 0 ) {
241                         perror("SyscallServer - accept");
242                         break ;
243                 }
244                 
245                 Log("Client connection %x:%i\n",
246                         ntohl(client.sin_addr), ntohs(client.sin_port)
247                         );
248                 
249                 #else
250                 char    data[BUFSIZ];
251                 tRequestHeader  *req = (void*)data;
252                 struct sockaddr_in      addr;
253                 uint    clientSize = sizeof(addr);
254                  int    length;
255                 tClient *client;
256                 
257                 length = recvfrom(gSocket, data, BUFSIZ, 0, (struct sockaddr*)&addr, &clientSize);
258                 
259                 if( length == -1 ) {
260                         perror("SyscallServer - recv");
261                         break;
262                 }
263                 
264                 // Hand off to a worker thread
265                 // - TODO: Actually have worker threads
266                 printf("%i bytes from %x:%i\n", length,
267                         ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
268                 
269                 client = Server_GetClient(req->ClientID);
270                 if( req->ClientID == 0 )
271                 {
272                         memcpy(&client->ClientAddr, &addr, sizeof(addr));
273                 }
274                 else if( memcmp(&client->ClientAddr, &addr, sizeof(addr)) != 0 )
275                 {
276                         printf("ClientID %i used by %x:%i\n",
277                                 client->ClientID, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
278                         printf(" actually owned by %x:%i\n",
279                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
280                         continue;
281                 }
282                 
283                 if( client->CurrentRequest ) {
284                         printf("Worker thread for %x:%i is busy\n",
285                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
286                         continue;
287                 }
288                 
289                 Log_Debug("AcessSrv", "Message from Client %i (%p)",
290                         client->ClientID, client);
291                 
292                 client->CurrentRequest = req;
293                 SDL_CondSignal(client->WaitFlag);
294                 #endif
295         }
296         
297         return -1;
298 }

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