]> git.cworth.org Git - vogl/blob - src/voglserver/server.cpp
Initial vogl checkin
[vogl] / src / voglserver / server.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <time.h>
33
34 #include <vogl_core.h>
35 #include <vogl_json.h>
36
37 #include <iomanip>
38 #include <sstream>
39
40  #include <pthread.h>
41
42 #include "../common/SimpleOpt.h"
43
44 #include "../common/vogllogging.h"
45 #include "../common/channel.h"
46
47 #include "../common/commands.h"
48 #include "../common/launchsteamgame.h"
49 #include "../common/toclientmsg.h"
50 #include "../common/listfiles.h"
51
52 enum
53 {
54     OPT_HELP = 0,
55     OPT_PORT,
56     OPT_MAX
57 };
58
59 CSimpleOpt::SOption g_rgOptions[] =
60 {
61     //  Prints out help for these command line parameters.
62     { OPT_HELP, "-?", SO_NONE },
63     { OPT_HELP, "-h", SO_NONE },
64     { OPT_HELP, "-help", SO_NONE },
65     { OPT_HELP, "--help", SO_NONE },
66
67     //  The network port the server should listen to.
68     { OPT_PORT, "-p", SO_REQ_SEP },
69     { OPT_PORT, "-port", SO_REQ_SEP },
70     { OPT_PORT, "--port", SO_REQ_SEP },
71
72     SO_END_OF_OPTIONS
73 };
74
75 bool g_KeepRunning = true;
76 char *g_processName = NULL;
77 network::channelmgr *g_clientChannelMgr = NULL;
78 network::channelmgr *g_gameChannelMgr = NULL;
79 bool g_fAttemptingGameConnection = false;
80
81 void ShowUsage(char *szAppName);
82 static const char *GetLastErrorText(int a_nError);
83
84 void send_status_to_client(const char *message);
85
86 void ProcessCommand(void *callbackParam, unsigned int cbData, char *pbData);
87
88 int main(int argc, char *argv[])
89 {
90     int port = DEFAULT_PORT;
91     network::CHEC ec = network::EC_NONE;
92
93     CSimpleOpt args(argc, argv, g_rgOptions);
94     while (args.Next())
95     {
96         if (args.LastError() != SO_SUCCESS)
97         {
98             printf("%s: '%s' (use --help to get command line help)\n",
99                    GetLastErrorText(args.LastError()), args.OptionText());
100             ShowUsage(argv[0]);
101             return -1;
102         }
103
104         switch (args.OptionId())
105         {
106
107             case OPT_HELP:
108             {
109                 ShowUsage(argv[0]);
110                 return 0;
111             }
112
113             case OPT_PORT:
114             {
115                 sscanf(args.OptionArg(), "%d", &port); //or atoi()
116                 printf("Server configured to bind to port %d\n", port);
117                 break;
118             }
119
120             default:
121             {
122                 if (args.OptionArg())
123                 {
124                     printf("option: %2d, text: '%s', arg: '%s'\n",
125                            args.OptionId(), args.OptionText(), args.OptionArg());
126                 }
127                 else
128                 {
129                     printf("option: %2d, text: '%s'\n",
130                            args.OptionId(), args.OptionText());
131                 }
132                 return -1;
133             }
134         }
135     }
136
137     g_processName = argv[0];
138
139     {
140         g_clientChannelMgr = new network::channelmgr();
141         if (NULL == g_clientChannelMgr)
142         {
143             syslog(VOGL_ERROR, "%s:%d %s Unable to allocation networking channelmgr.  OOM?\n", __FILE__, __LINE__, __func__);
144             return -1;
145         }
146
147         //  Create a client channel
148         ec = g_clientChannelMgr->Accept( port, (RECVASYNC|SENDASYNC), false,  ProcessCommand, NULL, NULL, NULL );
149         if (network::EC_NONE != ec)
150         {
151             syslog(VOGL_ERROR, "%s:%d %s Unable to listen on port %d\nExiting.\n", __FILE__, __LINE__, __func__, port);
152             return -1;
153         }
154
155         //
156         //  Loop 'til dead?
157         int cLoop = 0;
158         while (g_KeepRunning)
159         {
160
161             sleep(1);
162             cLoop++;
163             if (0 == (cLoop%10))
164                 send_status_to_client("Server is still alive.");
165         }
166
167     }
168
169     return 0;
170 }
171
172 void ShowUsage(char *szAppName)
173 {
174     printf("Usage: %s [Options]\n\n", szAppName);
175     printf("Where Options are:\n\n");
176
177     printf("Setting Ports:\n");
178     printf("[Optional] Specifies the port that this server should listen to for connections.  The default value is %d.\n", DEFAULT_PORT);
179     printf("-p <portNumber>\n");
180     printf("-port <portNumber>\n");
181     printf("--port <portNumber>\n\n");
182
183     printf("Help message:\n");
184     printf("Gives this useful help message again.\n");
185     printf("-?\n");
186     printf("-h\n");
187     printf("-help\n");
188     printf("--help\n\n");
189 }
190
191 static const char *
192 GetLastErrorText(int a_nError)
193 {
194     switch (a_nError)
195     {
196         case SO_SUCCESS:
197             return ("Success");
198         case SO_OPT_INVALID:
199             return ("Unrecognized option");
200         case SO_OPT_MULTIPLE:
201             return ("Option matched multiple strings");
202         case SO_ARG_INVALID:
203             return ("Option does not accept argument");
204         case SO_ARG_INVALID_TYPE:
205             return ("Invalid argument format");
206         case SO_ARG_MISSING:
207             return ("Required argument is missing");
208         case SO_ARG_INVALID_DATA:
209             return ("Invalid argument data");
210         default:
211             return ("Unknown error");
212     }
213 }
214
215
216
217 void *connect_to_launched_game(void *arg);
218
219 int g_gameport = TRACE_PORT;
220 void ProcessCommand(void * /*callbackParam*/, unsigned int buffer_size, char *buffer)
221 {
222
223     int32_t command = -1;
224     pthread_t threadGameConnect = 0;
225
226     int ec = 0;
227
228     command = *(int32_t *)buffer;
229
230     unsigned int buffer_size_temp = buffer_size - sizeof(int32_t);
231     char *buffer_temp = buffer + sizeof(int32_t);
232
233     switch (command)
234     {
235         case LAUNCHSTEAMGAME:
236         {
237
238             //send_status_to_client("Attempting to launch game.");
239             ec = LaunchSteamGame(buffer_size_temp, buffer_temp);
240             if (0 > ec)
241             {
242                 //  Failed to parse?
243                 syslog(VOGL_ERROR, "%s:%d %s Unable to parse LaunchSteamGame json\n", __FILE__, __LINE__, __func__);
244                 send_status_to_client("Failed to launch game. OOM.");
245                 break;
246             }
247
248             if (0 != ec)
249                 g_gameport = ec;
250
251             send_status_to_client("Connecting to game.");
252
253             if (NULL == g_gameChannelMgr && !g_fAttemptingGameConnection)
254             {
255                 g_fAttemptingGameConnection = true;
256                 ec = pthread_create(&threadGameConnect, NULL, connect_to_launched_game, NULL);
257                 if (0 != ec)
258                     g_fAttemptingGameConnection = false;
259             }
260             break;
261         }
262
263         case TRACE_LIST_TRACES:
264         {
265             network::CHEC chec = network::EC_NONE;
266             unsigned int buffer_size_list = 0;
267             char *buffer_list = NULL;
268             //  The command just is.  We simply send back the list of files.
269             //
270             ec = ListTraceFiles(&buffer_size_list, &buffer_list);
271             if (0 != ec)
272                 syslog(VOGL_ERROR, "%s:%d %s Unable to create trace list message (%d)\n", __FILE__, __LINE__, __func__, command);
273
274             chec = g_clientChannelMgr->SendData(buffer_size_list, buffer_list);
275             if (network::EC_NONE != chec)
276             {
277                 syslog(VOGL_ERROR, "%s:%d %s Unable to send trace list message (%d)\n", __FILE__, __LINE__, __func__, chec);
278             }
279
280             if (NULL != buffer_list)
281                 free(buffer_list);
282
283             break;
284         }
285
286         default:
287         {
288             //  Don't know what this is, so just forwarding on the original buffer to the game.
289             if (NULL != g_gameChannelMgr)
290             {
291                 network::CHEC chec = network::EC_NONE;
292
293                 //syslog(VOGL_INFO, "%s:%d %s Sending message (%d) on to game\n", __FILE__, __LINE__, __func__, command);
294
295                 chec = g_gameChannelMgr->SendData(buffer_size, buffer);
296                 if (network::EC_NONE != chec)
297                 {
298                     syslog(VOGL_ERROR, "%s:%d %s Unable to send message (%d) on to game\n", __FILE__, __LINE__, __func__, command);
299                 }
300             }
301
302             break;
303         }
304     }
305
306     return;
307
308 }
309
310 void send_status_to_client(const char *message)
311 {
312     int ec = 0;
313     unsigned int buffer_size = 0;
314     char *buffer = NULL;
315     //
316     //  Prepare message to send
317
318     ec = ToClientMsgReq(g_processName, 0, message, &buffer_size, &buffer);
319
320     (void)g_clientChannelMgr->SendData(buffer_size, buffer);
321
322     if (buffer)
323         free(buffer);
324
325     return;
326 }
327
328 void process_command_from_game(void * /*callbackParam*/, unsigned int buffer_size, char *buffer);
329
330 void *connect_to_launched_game(void * /*arg*/)
331 {
332
333     network::channelmgr *gameChannelMgr = NULL;
334     network::CHEC ec = network::EC_NONE;
335     pthread_t thisThread = pthread_self();
336     int err = 0;
337
338     err = pthread_setname_np(thisThread, "cnt2game");
339     if (0 != err)
340     {
341         syslog(VOGL_ERROR, "%s:%d %s Unable to set the name of the thread. returned %d, errno %d\n", __FILE__, __LINE__, __func__, err, errno);
342     }
343
344     gameChannelMgr = new network::channelmgr();
345     if (NULL == gameChannelMgr)
346         goto out;
347
348     //ec = gameChannelMgr->Connect( const_cast<char *>("localhost"), g_gameport, (SENDASYNC|RECVASYNC), process_command_from_game, NULL );
349     ec = gameChannelMgr->Accept( g_gameport, (RECVASYNC|SENDASYNC), true,  process_command_from_game, NULL, NULL, NULL );
350     if (network::EC_NONE != ec)
351     {
352         delete gameChannelMgr;
353         syslog(VOGL_ERROR, "%s:%d %s Unable to listen to game on port %d\n", __FILE__, __LINE__, __func__, g_gameport);
354         goto out;
355     }
356
357     /*
358     if (NULL != g_gameChannelMgr)
359     {
360         delete g_gameChannelMgr;
361         g_gameChannelMgr = NULL;
362     }
363     */
364
365     g_gameChannelMgr = gameChannelMgr;
366 out:
367     g_fAttemptingGameConnection = false;
368
369     return NULL;
370 }
371
372 void process_command_from_game(void * /*callbackParam*/, unsigned int buffer_size, char *buffer)
373 {
374     network::CHEC chec = network::EC_NONE;
375
376     //syslog(VOGL_INFO, "%s:%d %s Sending message on to client\n", __FILE__, __LINE__, __func__);
377
378     chec = g_clientChannelMgr->SendData(buffer_size, buffer);
379     if (network::EC_NONE != chec)
380     {
381         syslog(VOGL_ERROR, "%s:%d %s Unable to send message on to client\n", __FILE__, __LINE__, __func__);
382     }
383
384     return;
385 }
386