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

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