3
|
1 /* $Id: parsemeta.c,v 1.1.1.1 1997/12/06 05:41:29 darius Exp $ */
|
|
2
|
|
3 /*
|
|
4 * meta.c - Nick Trown May 1993
|
|
5 */
|
|
6
|
|
7 #ifdef METASERVER
|
|
8
|
|
9 #include "copyright.h"
|
|
10 #include <fcntl.h>
|
|
11 #include <stdio.h>
|
|
12 #include <ctype.h>
|
|
13 #include <sys/types.h>
|
|
14 #include <sys/time.h>
|
|
15 #include <stdlib.h>
|
|
16 #include <sys/socket.h>
|
|
17 #include <unistd.h>
|
|
18
|
|
19 #ifdef RS6K
|
|
20 #include <sys/select.h>
|
|
21 #include <fcntl.h>
|
|
22 #endif
|
|
23
|
|
24 #ifndef DNET
|
|
25 #include <netinet/in.h>
|
|
26 #include <arpa/inet.h>
|
|
27 #endif
|
|
28 #include <netdb.h>
|
|
29 #include <errno.h>
|
|
30 #include "Wlib.h"
|
|
31 #include "defs.h"
|
|
32 #include "struct.h"
|
|
33 #include "data.h"
|
|
34 #include "proto.h"
|
|
35
|
|
36 #include <string.h>
|
|
37 #if !defined(SYSV) && !defined(apollo) && !defined(SVR4)
|
|
38 #include <strings.h>
|
|
39 #endif
|
|
40
|
|
41 #define BUF 4096
|
|
42
|
|
43 static void metarefresh P((int i));
|
|
44 static void metadone P((void));
|
|
45
|
|
46 int pid = 0;
|
|
47 int num_servers = 0;
|
|
48 int max_servers = 20;
|
|
49 struct servers *serverlist = NULL;
|
|
50 char *keystrings[] = {"OPEN:", "Wait queue:", "Nobody"};
|
|
51
|
|
52 /*
|
|
53 I was planning to use this function a lot but it doesn't look like
|
|
54 I need it more than once. It finds the next number after position
|
|
55 'start'.
|
|
56 */
|
|
57
|
|
58 int
|
|
59 getnumber(string, start)
|
|
60 char *string;
|
|
61 int start;
|
|
62 {
|
|
63 #if 0 /* this is stupid [BDyess] */
|
|
64 char temp[LINE];
|
|
65 int c;
|
|
66 int tc = 0;
|
|
67
|
|
68 for (c = start; c <= (int) strlen(string); c++) {
|
|
69 if ((string[c] >= '0') && (string[c] <= '9')) {
|
|
70 temp[tc++] = string[c];
|
|
71 } else if (tc > 0) {
|
|
72 temp[tc] = '\0';
|
|
73 return (atoi(temp));
|
|
74 }
|
|
75 }
|
|
76 return 0;
|
|
77 #endif /*0*/
|
|
78
|
|
79 string += start;
|
|
80 while(!isdigit(*string) && *string) string++;
|
|
81 return atoi(string);
|
|
82 }
|
|
83
|
|
84
|
|
85 /*
|
|
86 The connection to the metaserver is by Andy McFadden.
|
|
87 This calls the metaserver and parses the output into something
|
|
88 useful
|
|
89 */
|
|
90
|
|
91 static int
|
|
92 open_port(host, port, verbose)
|
|
93 char *host;
|
|
94 int port;
|
|
95 int verbose;
|
|
96 {
|
|
97 #ifdef DNET
|
|
98 return DNetOpenMeta(host, port);
|
|
99 #else
|
|
100 struct sockaddr_in addr;
|
|
101 struct hostent *hp;
|
|
102 int s;
|
|
103
|
|
104
|
|
105 /* Connect to the metaserver */
|
|
106 /* get numeric form */
|
|
107 if ((addr.sin_addr.s_addr = inet_addr(host)) == -1) {
|
|
108 if ((hp = gethostbyname(host)) == NULL) {
|
|
109 if (verbose)
|
|
110 fprintf(stderr, "unknown host '%s'\n", host);
|
|
111 return (-1);
|
|
112 } else {
|
|
113 addr.sin_addr.s_addr = *(long *) hp->h_addr;
|
|
114 }
|
|
115 }
|
|
116 addr.sin_family = AF_INET;
|
|
117 addr.sin_port = htons(port);
|
|
118 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
119 if (verbose)
|
|
120 perror("socket");
|
|
121 return (-1);
|
|
122 }
|
|
123 if (connect(s, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
|
|
124 if (verbose)
|
|
125 perror("connect");
|
|
126 close(s);
|
|
127 return (-1);
|
|
128 }
|
|
129 return (s);
|
|
130 #endif /* DNET */
|
|
131 }
|
|
132
|
|
133 static int
|
|
134 readmeta(s)
|
|
135 int s;
|
|
136 {
|
|
137 int cc, lc = 0, c, tc;
|
|
138
|
|
139 static char buf[BUF + 1];
|
|
140 static char *numstr;
|
|
141 static char line[LINE + 1];
|
|
142
|
|
143 #ifndef DEBUG
|
|
144 while (1) {
|
|
145 #ifdef DNET /* annoying... */
|
|
146 if ((cc = sock_read(s, buf, BUF)) < 0) {
|
|
147 #else /* } */ /* to balance the {'s for emacs */
|
|
148 if ((cc = sock_read(s, buf, BUF)) <= 0) {
|
|
149 if (cc < 0)
|
|
150 perror("read");
|
|
151 #endif /* DNET */
|
|
152 sock_close(s);
|
|
153 return (cc);
|
|
154 }
|
|
155 #else
|
|
156 /*
|
|
157 cat the output of the metaserver port 3521 to a file called "output"
|
|
158 for testing purposes
|
|
159 */
|
|
160 s = open("output", O_RDONLY);
|
|
161
|
|
162 if ((cc = read(s, buf, BUF)) <= 0) {
|
|
163 if (cc < 0)
|
|
164 perror("read");
|
|
165 close(s);
|
|
166 return (cc);
|
|
167 }
|
|
168 fwrite(buf, cc, 1, stdout);
|
|
169 close(s);
|
|
170 #endif
|
|
171
|
|
172 for (c = 0; c < cc; c++) {
|
|
173 if (buf[c] != '\n') /* Break up the buffer into lines */
|
|
174 line[lc++] = buf[c];
|
|
175 else {
|
|
176 line[lc] = 0; /* random problems without this. No terminator
|
|
177 means invalid strlen(). [BDyess] */
|
|
178 if (sscanf(line, "-h %s -p %d %d", /* parse line */
|
|
179 serverlist[num_servers].address,
|
|
180 &serverlist[num_servers].port,
|
|
181 &serverlist[num_servers].time) == 3) {
|
|
182 /* get what type of server it is */
|
|
183 serverlist[num_servers].typeflag = line[strlen(line) - 1];
|
|
184 if (serverlist[num_servers].typeflag == 'P') {
|
|
185 serverlist[num_servers].status = 2;
|
|
186 serverlist[num_servers].players = 0;
|
|
187 } else
|
|
188 serverlist[num_servers].status = -1;
|
|
189
|
|
190 /*
|
|
191 I don't have it checking the servers with nobody playing
|
|
192 because the menu window would then be too large to fit on
|
|
193 a 1024 x 768 window. :( - NBT
|
|
194 */
|
|
195
|
|
196 for (tc = 0; tc < KEY; tc++)
|
|
197 if ((numstr = strstr(line, keystrings[tc]))) {
|
|
198 serverlist[num_servers].status = tc;
|
|
199 serverlist[num_servers].players = getnumber(numstr, 0);
|
|
200 }
|
|
201 if (strrchr(line, 'R'))
|
|
202 serverlist[num_servers].RSA_client = 1;
|
|
203 else
|
|
204 serverlist[num_servers].RSA_client = 0;
|
|
205
|
|
206 if (serverlist[num_servers].status >= 0 &&
|
|
207 (serverlist[num_servers].status != 2 ||
|
|
208 serverlist[num_servers].typeflag == 'P')) {
|
|
209
|
|
210 #ifdef DEBUG
|
|
211 printf("HOST:%-30s PORT:%-6d %12s %-5d %d\n",
|
|
212 serverlist[num_servers].address,
|
|
213 serverlist[num_servers].port,
|
|
214 keystrings[serverlist[num_servers].status],
|
|
215 serverlist[num_servers].players,
|
|
216 serverlist[num_servers].RSA_client);
|
|
217 #endif
|
|
218
|
|
219 serverlist[num_servers].hilited = 0;
|
|
220 num_servers++; /* It's valid */
|
|
221 if(num_servers == max_servers) {
|
|
222 /* double the size every time it's exceeded [BDyess] */
|
|
223 serverlist = (struct servers*) realloc(serverlist,
|
|
224 sizeof(struct servers) * (max_servers *= 2));
|
|
225 }
|
|
226 }
|
|
227 }
|
|
228 lc = 0;
|
|
229 /*line[0] = '\0'; silly [BDyess] */
|
|
230 }
|
|
231 }
|
|
232 }
|
|
233 /* close(s); */
|
|
234 }
|
|
235
|
|
236
|
|
237 void
|
|
238 parsemeta()
|
|
239 {
|
|
240 int s;
|
|
241
|
|
242 num_servers = 0;
|
|
243
|
|
244 /* serverlist is now dynamic and grows. This fixes the random coredump
|
|
245 and data corruption problems the previous static method caused.
|
|
246 [BDyess] */
|
|
247 if(serverlist) free(serverlist);
|
|
248 serverlist = (struct servers*) malloc(sizeof(struct servers) * max_servers);
|
|
249
|
|
250 if (serverName) {
|
|
251 strcpy(serverlist[num_servers].address, serverName);
|
|
252 serverlist[num_servers].port = xtrekPort;
|
|
253
|
|
254 #ifdef RSA
|
|
255 serverlist[num_servers].RSA_client = RSA_Client;
|
|
256 #endif
|
|
257
|
|
258 serverlist[num_servers].status = KEY + 1;
|
|
259 serverlist[num_servers].players = 0;
|
|
260 num_servers++;
|
|
261 }
|
|
262 printf("connecting to metaserver...");
|
|
263 fflush(stdout);
|
|
264
|
|
265 #ifdef DEBUG
|
|
266 readmeta(0);
|
|
267 #else
|
|
268 if ((s = open_port(metaserverAddress, METAPORT, 1)) > 0) {
|
|
269 readmeta(s);
|
|
270 } else {
|
|
271 printf("failed (%s , %d)\n", metaserverAddress, METAPORT);
|
|
272 }
|
|
273 #endif
|
|
274 printf("\n");
|
|
275 }
|
|
276
|
|
277
|
|
278 /* Show the meta server menu window */
|
|
279
|
|
280 void
|
|
281 metawindow()
|
|
282 {
|
|
283 register int i;
|
|
284 static int old_num_servers = -1;
|
|
285
|
|
286 if (old_num_servers != num_servers) {
|
|
287 if (metaWin)
|
|
288 W_DestroyWindow(metaWin);
|
|
289 metaWin = W_MakeMenu("MetaServer List", WINSIDE + 10, -BORDER + 10, 69,
|
|
290 num_servers + 2, NULL, 2);
|
|
291 }
|
|
292 for (i = 0; i < num_servers; i++)
|
|
293 metarefresh(i);
|
|
294
|
|
295 /* add refresh option [BDyess] */
|
|
296 W_WriteText(metaWin, 0, num_servers, textColor, "Refresh", 7, 0);
|
|
297
|
|
298 /* Add quit option */
|
|
299 W_WriteText(metaWin, 0, num_servers + 1, textColor, "Quit", 4, W_RegularFont);
|
|
300
|
|
301 /* Map window */
|
|
302 if (!W_IsMapped(metaWin))
|
|
303 W_MapWindow(metaWin);
|
|
304 }
|
|
305
|
|
306 /*
|
|
307 * Refresh item i
|
|
308 */
|
|
309 static void
|
|
310 metarefresh(i)
|
|
311 int i;
|
|
312 {
|
|
313 char buf[BUFSIZ];
|
|
314
|
|
315 if (serverlist[i].status < KEY) {
|
|
316 sprintf(buf, "%-40s %12s ",
|
|
317 serverlist[i].address,
|
|
318 keystrings[serverlist[i].status]);
|
|
319 if (serverlist[i].status != 2)
|
|
320 sprintf(buf + strlen(buf), "%-5d ", serverlist[i].players);
|
|
321 else
|
|
322 strcat(buf, " ");
|
|
323 switch (serverlist[i].typeflag) {
|
|
324 case 'P':
|
|
325 strcat(buf, "Paradise");
|
|
326 break;
|
|
327 case 'B':
|
|
328 strcat(buf, "Bronco");
|
|
329 break;
|
|
330 case 'C':
|
|
331 strcat(buf, "Chaos");
|
|
332 break;
|
|
333 case 'I':
|
|
334 strcat(buf, "INL");
|
|
335 break;
|
|
336 case 'S':
|
|
337 strcat(buf, "Sturgeon");
|
|
338 break;
|
|
339 case 'H':
|
|
340 strcat(buf, "Hockey");
|
|
341 break;
|
|
342 case 'F':
|
|
343 strcat(buf, "Dogfight");
|
|
344 break;
|
|
345 }
|
|
346 } else if (serverlist[i].status == KEY) { /* For when I have checking
|
|
347 code */
|
|
348 sprintf(buf, "%-40s CANNOT CONNECT", serverlist[i].address);
|
|
349 } else if (serverlist[i].status == KEY + 1) {
|
|
350 sprintf(buf, "%-40s DEFAULT SERVER", serverlist[i].address);
|
|
351 }
|
|
352 W_WriteText(metaWin, 0, i,
|
|
353 serverlist[i].hilited ? W_Yellow : textColor,
|
|
354 buf, (int)strlen(buf),
|
|
355 serverlist[i].hilited ? W_HighlightFont : W_RegularFont);
|
|
356 }
|
|
357
|
|
358
|
|
359 /*
|
|
360 Check selection to see if was valid. If it was then we have a winner!
|
|
361 */
|
|
362 static void
|
|
363 metaaction(data)
|
|
364 W_Event *data;
|
|
365 {
|
|
366 int s;
|
|
367 static time_t lastRefresh = 0;
|
|
368 static time_t t;
|
|
369
|
|
370 if ((data->y >= 0) && (data->y < num_servers)) {
|
|
371 xtrekPort = serverlist[data->y].port;
|
|
372 serverName = serverlist[data->y].address;
|
|
373
|
|
374 #ifdef RSA
|
|
375 RSA_Client = serverlist[data->y].RSA_client;
|
|
376 #endif
|
|
377 serverlist[data->y].hilited = 1;
|
|
378 metarefresh(data->y);
|
|
379
|
|
380 if ((s = open_port(serverName, xtrekPort, 0)) <= 0) {
|
|
381 serverlist[data->y].status = KEY;
|
|
382 serverlist[data->y].hilited = 0;
|
|
383 metarefresh(data->y);
|
|
384 } else {
|
|
385 sock_close(s);
|
|
386 #ifndef AMIGA
|
|
387 /* allow spawning off multiple clients [BDyess] */
|
|
388 if (metaFork) {
|
|
389 /* just blink yellow [BDyess] */
|
|
390 serverlist[data->y].hilited = 0;
|
|
391 metarefresh(data->y);
|
|
392 pid = fork();
|
|
393 } else
|
|
394 #else
|
|
395 /*
|
|
396 OUCH. fork() sucks ;-) Using "run" is equivalent to appending
|
|
397 a & to a command in Unix. (my shell allows &, but it's not
|
|
398 built in to AmigaDOS.) This is a stupid kludge. I
|
|
399 should write a silly shell or rexx script to do all the
|
|
400 metaserver stuff instead, and not compile PARSEMETA at all.
|
|
401 */
|
|
402 if (metaFork) {
|
|
403 extern char *command_line; /* main.c, argv */
|
|
404 extern int global_argc;
|
|
405 int ac;
|
|
406 char buf2[256];
|
|
407 char buf[80];
|
|
408
|
|
409 sprintf(buf,"run %s ",command_line[0]);
|
|
410 for(ac=1;ac<global_argc;ac++) {
|
|
411 if(strcmp(command_line[ac], "-m") == 0)
|
|
412 ac++;
|
|
413 else {
|
|
414 strcat(buf,command_line[ac]);
|
|
415 strcat(buf," ");
|
|
416 }
|
|
417 }
|
|
418 printf(buf2,"-h %s -p %d", command_line[0], serverName, xtrekPort);
|
|
419 strcat(buf, buf2);
|
|
420 Execute(buf, Input(), Output());
|
|
421 } else
|
|
422 #endif /* AMIGA */
|
|
423 {
|
|
424 pid = 1;
|
|
425 metadone();
|
|
426 }
|
|
427 }
|
|
428 } else if (data->y == num_servers) { /* refresh [BDyess] */
|
|
429 /*
|
|
430 they can bang on refresh all day, but it won't refresh any faster
|
|
431 than once/minute. [BDyess]
|
|
432 */
|
|
433 if ((t = time(NULL)) < lastRefresh + 60)
|
|
434 return;
|
|
435 lastRefresh = t;
|
|
436 parsemeta(); /* connect and parse info */
|
|
437 metawindow(); /* refresh */
|
|
438 } else { /* quit */
|
|
439 metadone();
|
|
440 exit(0);
|
|
441 }
|
|
442 }
|
|
443
|
|
444
|
|
445 /*
|
|
446 Unmap the metaWindow
|
|
447 */
|
|
448
|
|
449 static void
|
|
450 metadone()
|
|
451 {
|
|
452 /* Unmap window */
|
|
453 W_UnmapWindow(metaWin);
|
|
454 }
|
|
455
|
|
456
|
|
457 /*
|
|
458 My own little input() function. I needed this so I don't have
|
|
459 to use all the bull in the main input(). Plus to use it I'd have
|
|
460 to call mapAll() first and the client would read in the default
|
|
461 server and then call it up before I can select a server.
|
|
462 */
|
|
463
|
|
464 void
|
|
465 metainput()
|
|
466 {
|
|
467 W_Event data;
|
|
468
|
|
469 while (W_IsMapped(metaWin) && pid == 0) {
|
|
470 W_GetEvent(&data);
|
|
471 switch ((int) data.type) {
|
|
472 case W_EV_KEY:
|
|
473 if (data.Window == metaWin)
|
|
474 metaaction(&data);
|
|
475 break;
|
|
476 case W_EV_BUTTON:
|
|
477 if (data.Window == metaWin)
|
|
478 metaaction(&data);
|
|
479 break;
|
|
480 case W_EV_EXPOSE:
|
|
481 break;
|
|
482 default:
|
|
483 break;
|
|
484 }
|
|
485 }
|
|
486 }
|
|
487 #endif /* METASERVER */
|