Make multiplayer ruleset default to iso topology, to match client default.
[freeciv.git] / client / servers.c
blob39056724a97d2fc1ebab34350f2f31d9c20d48a2
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <errno.h>
19 #include <stdlib.h>
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
23 #endif
24 #ifdef HAVE_SYS_SOCKET_H
25 #include <sys/socket.h>
26 #endif
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
29 #endif
30 #ifdef HAVE_ARPA_INET_H
31 #include <arpa/inet.h>
32 #endif
33 #ifdef HAVE_NETDB_H
34 #include <netdb.h>
35 #endif
36 #ifdef HAVE_PWD_H
37 #include <pwd.h>
38 #endif
39 #ifdef HAVE_SYS_SELECT_H
40 #include <sys/select.h>
41 #endif
42 #ifdef HAVE_SYS_TIME_H
43 #include <sys/time.h>
44 #endif
45 #ifdef HAVE_SYS_UIO_H
46 #include <sys/uio.h>
47 #endif
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 #ifdef HAVE_WINSOCK
52 #include <winsock.h>
53 #endif
55 /* dependencies */
56 #include "cvercmp.h"
58 /* utility */
59 #include "fcintl.h"
60 #include "fcthread.h"
61 #include "log.h"
62 #include "mem.h"
63 #include "netfile.h"
64 #include "netintf.h"
65 #include "rand.h" /* fc_rand() */
66 #include "registry.h"
67 #include "support.h"
69 /* common */
70 #include "capstr.h"
71 #include "dataio.h"
72 #include "game.h"
73 #include "packets.h"
74 #include "version.h"
76 /* client */
77 #include "chatline_common.h"
78 #include "chatline_g.h"
79 #include "client_main.h"
80 #include "servers.h"
82 #include "gui_main_g.h"
84 struct server_scan {
85 enum server_scan_type type;
86 ServerScanErrorFunc error_func;
88 struct srv_list srvrs;
89 int sock;
91 /* Only used for metaserver */
92 struct {
93 enum server_scan_status status;
95 fc_thread thr;
96 fc_mutex mutex;
98 const char *urlpath;
99 FILE *fp; /* temp file */
100 } meta;
103 extern enum announce_type announce;
105 static bool begin_metaserver_scan(struct server_scan *scan);
106 static void delete_server_list(struct server_list *server_list);
108 /**************************************************************************
109 The server sends a stream in a registry 'ini' type format.
110 Read it using secfile functions and fill the server_list structs.
111 **************************************************************************/
112 static struct server_list *parse_metaserver_data(fz_FILE *f)
114 struct server_list *server_list;
115 struct section_file *file;
116 int nservers, i, j;
117 const char *latest_ver;
119 /* This call closes f. */
120 if (!(file = secfile_from_stream(f, TRUE))) {
121 return NULL;
124 latest_ver = secfile_lookup_str_default(file, NULL, "versions." FOLLOWTAG);
126 if (latest_ver != NULL) {
127 const char *my_comparable = fc_comparable_version();
128 char vertext[2048];
130 log_verbose("Metaserver says latest '%s' version is '%s'; we have '%s'",
131 FOLLOWTAG, latest_ver, my_comparable);
132 if (cvercmp_greater(latest_ver, my_comparable)) {
133 const char *const followtag = "?vertag:" FOLLOWTAG;
134 fc_snprintf(vertext, sizeof(vertext),
135 /* TRANS: Type is version tag name like "stable", "S2_4",
136 * "win32" (which can also be localised -- msgids start
137 * '?vertag:') */
138 _("Latest %s release of Freeciv is %s, this is %s."),
139 Q_(followtag), latest_ver, my_comparable);
140 } else {
141 fc_snprintf(vertext, sizeof(vertext),
142 _("There is no newer %s release of Freeciv available."),
143 FOLLOWTAG);
146 version_message(vertext);
149 server_list = server_list_new();
150 nservers = secfile_lookup_int_default(file, 0, "main.nservers");
152 for (i = 0; i < nservers; i++) {
153 const char *host, *port, *version, *state, *message, *nplayers, *nhumans;
154 int n;
155 struct server *pserver = (struct server*)fc_malloc(sizeof(struct server));
157 host = secfile_lookup_str_default(file, "", "server%d.host", i);
158 pserver->host = fc_strdup(host);
160 port = secfile_lookup_str_default(file, "", "server%d.port", i);
161 pserver->port = atoi(port);
163 version = secfile_lookup_str_default(file, "", "server%d.version", i);
164 pserver->version = fc_strdup(version);
166 state = secfile_lookup_str_default(file, "", "server%d.state", i);
167 pserver->state = fc_strdup(state);
169 message = secfile_lookup_str_default(file, "", "server%d.message", i);
170 pserver->message = fc_strdup(message);
172 nplayers = secfile_lookup_str_default(file, "0", "server%d.nplayers", i);
173 n = atoi(nplayers);
174 pserver->nplayers = n;
176 nhumans = secfile_lookup_str_default(file, "-1", "server%d.humans", i);
177 n = atoi(nhumans);
178 pserver->humans = n;
180 if (pserver->nplayers > 0) {
181 pserver->players = fc_malloc(pserver->nplayers * sizeof(*pserver->players));
182 } else {
183 pserver->players = NULL;
186 for (j = 0; j < pserver->nplayers ; j++) {
187 const char *name, *nation, *type, *host;
189 name = secfile_lookup_str_default(file, "",
190 "server%d.player%d.name", i, j);
191 pserver->players[j].name = fc_strdup(name);
193 type = secfile_lookup_str_default(file, "",
194 "server%d.player%d.type", i, j);
195 pserver->players[j].type = fc_strdup(type);
197 host = secfile_lookup_str_default(file, "",
198 "server%d.player%d.host", i, j);
199 pserver->players[j].host = fc_strdup(host);
201 nation = secfile_lookup_str_default(file, "",
202 "server%d.player%d.nation", i, j);
203 pserver->players[j].nation = fc_strdup(nation);
206 server_list_append(server_list, pserver);
209 secfile_destroy(file);
211 return server_list;
214 /****************************************************************************
215 Read the reply string from the metaserver.
216 ****************************************************************************/
217 static bool meta_read_response(struct server_scan *scan)
219 fz_FILE *f;
220 char str[4096];
221 struct server_list *srvrs;
223 f = fz_from_stream(scan->meta.fp);
224 if (NULL == f) {
225 fc_snprintf(str, sizeof(str),
226 _("Failed to read the metaserver data from %s."),
227 metaserver);
228 scan->error_func(scan, str);
230 return FALSE;
233 /* parse message body */
234 fc_allocate_mutex(&scan->srvrs.mutex);
235 srvrs = parse_metaserver_data(f);
236 scan->srvrs.servers = srvrs;
237 fc_release_mutex(&scan->srvrs.mutex);
239 /* 'f' (hence 'meta.fp') was closed in parse_metaserver_data(). */
240 scan->meta.fp = NULL;
242 if (NULL == srvrs) {
243 fc_snprintf(str, sizeof(str),
244 _("Failed to parse the metaserver data from %s:\n"
245 "%s."),
246 metaserver, secfile_error());
247 scan->error_func(scan, str);
249 return FALSE;
252 return TRUE;
255 /****************************************************************************
256 Metaserver scan thread entry point
257 ****************************************************************************/
258 static void metaserver_scan(void *arg)
260 struct server_scan *scan = arg;
262 if (!scan->meta.fp) {
263 #ifdef WIN32_NATIVE
264 char filename[MAX_PATH];
266 GetTempPath(sizeof(filename), filename);
267 cat_snprintf(filename, sizeof(filename), "fctmp%d", fc_rand(1000));
269 scan->meta.fp = fc_fopen(filename, "w+b");
270 #else
271 scan->meta.fp = tmpfile();
272 #endif /* WIN32_NATIVE */
274 if (!scan->meta.fp) {
275 scan->error_func(scan, _("Could not open temp file."));
279 if (scan->meta.fp) {
281 if (!begin_metaserver_scan(scan)) {
282 fc_allocate_mutex(&scan->meta.mutex);
283 scan->meta.status = SCAN_STATUS_ERROR;
284 } else {
286 rewind(scan->meta.fp);
288 if (!meta_read_response(scan)) {
289 fc_allocate_mutex(&scan->meta.mutex);
290 scan->meta.status = SCAN_STATUS_ERROR;
291 } else {
292 fc_allocate_mutex(&scan->meta.mutex);
293 if (scan->meta.status == SCAN_STATUS_WAITING) {
294 scan->meta.status = SCAN_STATUS_DONE;
299 fc_release_mutex(&scan->meta.mutex);
303 /****************************************************************************
304 Begin a metaserver scan for servers.
306 Returns FALSE on error (in which case errbuf will contain an error
307 message).
308 ****************************************************************************/
309 static bool begin_metaserver_scan(struct server_scan *scan)
311 struct netfile_post *post;
312 bool retval = TRUE;
314 post = netfile_start_post();
315 netfile_add_form_str(post, "client_cap", our_capability);
317 if (!netfile_send_post(metaserver, post, scan->meta.fp, NULL)) {
318 scan->error_func(scan, _("Error connecting to metaserver"));
319 retval = FALSE;
322 netfile_close_post(post);
324 return retval;
327 /**************************************************************************
328 Frees everything associated with a server list including
329 the server list itself (so the server_list is no longer
330 valid after calling this function)
331 **************************************************************************/
332 static void delete_server_list(struct server_list *server_list)
334 if (!server_list) {
335 return;
338 server_list_iterate(server_list, ptmp) {
339 int i;
340 int n = ptmp->nplayers;
342 free(ptmp->host);
343 free(ptmp->version);
344 free(ptmp->state);
345 free(ptmp->message);
347 if (ptmp->players) {
348 for (i = 0; i < n; i++) {
349 free(ptmp->players[i].name);
350 free(ptmp->players[i].type);
351 free(ptmp->players[i].host);
352 free(ptmp->players[i].nation);
354 free(ptmp->players);
357 free(ptmp);
358 } server_list_iterate_end;
360 server_list_destroy(server_list);
363 /**************************************************************************
364 Broadcast an UDP package to all servers on LAN, requesting information
365 about the server. The packet is send to all Freeciv servers in the same
366 multicast group as the client.
367 **************************************************************************/
368 static bool begin_lanserver_scan(struct server_scan *scan)
370 union fc_sockaddr addr;
371 struct data_out dout;
372 int sock, opt = 1;
373 #ifndef HAVE_WINSOCK
374 unsigned char buffer[MAX_LEN_PACKET];
375 #else /* HAVE_WINSOCK */
376 char buffer[MAX_LEN_PACKET];
377 #endif /* HAVE_WINSOCK */
378 struct ip_mreq mreq4;
379 const char *group;
380 size_t size;
381 int family;
383 #ifdef IPV6_SUPPORT
384 struct ipv6_mreq mreq6;
385 #endif
387 #ifndef HAVE_WINSOCK
388 unsigned char ttl;
389 #endif
391 if (announce == ANNOUNCE_NONE) {
392 /* Succeeded in doing nothing */
393 return TRUE;
396 #ifdef IPV6_SUPPORT
397 if (announce == ANNOUNCE_IPV6) {
398 family = AF_INET6;
399 } else
400 #endif /* IPv6 support */
402 family = AF_INET;
405 /* Create a socket for broadcasting to servers. */
406 if ((sock = socket(family, SOCK_DGRAM, 0)) < 0) {
407 log_error("socket failed: %s", fc_strerror(fc_get_errno()));
408 return FALSE;
411 /* Set the UDP Multicast group IP address. */
412 group = get_multicast_group(announce == ANNOUNCE_IPV6);
413 memset(&addr, 0, sizeof(addr));
415 #ifndef IPV6_SUPPORT
416 if (family == AF_INET) {
417 #ifdef HAVE_INET_ATON
418 inet_aton(group, &addr.saddr_in4.sin_addr);
419 #else /* HAVE_INET_ATON */
420 addr.saddr_in4.sin_addr.s_addr = inet_addr(group);
421 #endif /* HAVE_INET_ATON */
422 #else /* IPv6 support */
423 if (family == AF_INET6) {
424 addr.saddr.sa_family = AF_INET6;
425 inet_pton(AF_INET6, group, &addr.saddr_in6.sin6_addr);
426 addr.saddr_in6.sin6_port = htons(SERVER_LAN_PORT);
427 } else if (family == AF_INET) {
428 inet_pton(AF_INET, group, &addr.saddr_in4.sin_addr);
429 #endif /* IPv6 support */
430 addr.saddr.sa_family = AF_INET;
431 addr.saddr_in4.sin_port = htons(SERVER_LAN_PORT);
432 } else {
433 fc_assert(FALSE);
435 log_error("Unsupported address family in begin_lanserver_scan()");
437 return FALSE;
440 /* this setsockopt call fails on Windows 98, so we stick with the default
441 * value of 1 on Windows, which should be fine in most cases */
442 #ifndef HAVE_WINSOCK
443 /* Set the Time-to-Live field for the packet */
444 ttl = SERVER_LAN_TTL;
445 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl,
446 sizeof(ttl))) {
447 log_error("setsockopt failed: %s", fc_strerror(fc_get_errno()));
448 return FALSE;
450 #endif /* HAVE_WINSOCK */
452 if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&opt,
453 sizeof(opt))) {
454 log_error("setsockopt failed: %s", fc_strerror(fc_get_errno()));
455 return FALSE;
458 dio_output_init(&dout, buffer, sizeof(buffer));
459 dio_put_uint8(&dout, SERVER_LAN_VERSION);
460 size = dio_output_used(&dout);
463 if (sendto(sock, buffer, size, 0, &addr.saddr,
464 sockaddr_size(&addr)) < 0) {
465 /* This can happen when there's no network connection - it should
466 * give an in-game message. */
467 log_error("lanserver scan sendto failed: %s",
468 fc_strerror(fc_get_errno()));
469 return FALSE;
470 } else {
471 log_debug("Sending request for server announcement on LAN.");
474 fc_closesocket(sock);
476 /* Create a socket for listening for server packets. */
477 if ((scan->sock = socket(family, SOCK_DGRAM, 0)) < 0) {
478 scan->error_func(scan, fc_strerror(fc_get_errno()));
479 return FALSE;
482 fc_nonblock(scan->sock);
484 if (setsockopt(scan->sock, SOL_SOCKET, SO_REUSEADDR,
485 (char *)&opt, sizeof(opt)) == -1) {
486 log_error("SO_REUSEADDR failed: %s", fc_strerror(fc_get_errno()));
489 memset(&addr, 0, sizeof(addr));
491 #ifdef IPV6_SUPPORT
492 if (family == AF_INET6) {
493 addr.saddr.sa_family = AF_INET6;
494 addr.saddr_in6.sin6_port = htons(SERVER_LAN_PORT + 1);
495 addr.saddr_in6.sin6_addr = in6addr_any;
496 } else
497 #endif /* IPv6 support */
498 if (family == AF_INET) {
499 addr.saddr.sa_family = AF_INET;
500 addr.saddr_in4.sin_port = htons(SERVER_LAN_PORT + 1);
501 addr.saddr_in4.sin_addr.s_addr = htonl(INADDR_ANY);
502 } else {
503 /* This is not only error situation worth assert() This
504 * is error situation that has check (with assert) against
505 * earlier already. */
506 fc_assert(FALSE);
508 return FALSE;
511 if (bind(scan->sock, &addr.saddr, sockaddr_size(&addr)) < 0) {
512 scan->error_func(scan, fc_strerror(fc_get_errno()));
513 return FALSE;
516 #ifndef IPV6_SUPPORT
518 #ifdef HAVE_INET_ATON
519 inet_aton(group, &mreq4.imr_multiaddr);
520 #else /* HAVE_INET_ATON */
521 mreq4.imr_multiaddr.s_addr = inet_addr(group);
522 #endif /* HAVE_INET_ATON */
523 #else /* IPv6 support */
524 if (family == AF_INET6) {
525 inet_pton(AF_INET6, group, &mreq6.ipv6mr_multiaddr.s6_addr);
526 mreq6.ipv6mr_interface = 0; /* TODO: Interface selection */
528 if (setsockopt(scan->sock, IPPROTO_IPV6, FC_IPV6_ADD_MEMBERSHIP,
529 (const char*)&mreq6, sizeof(mreq6)) < 0) {
530 scan->error_func(scan, fc_strerror(fc_get_errno()));
532 } else {
533 inet_pton(AF_INET, group, &mreq4.imr_multiaddr.s_addr);
534 #endif /* IPv6 support */
535 mreq4.imr_interface.s_addr = htonl(INADDR_ANY);
537 if (setsockopt(scan->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
538 (const char*)&mreq4, sizeof(mreq4)) < 0) {
539 scan->error_func(scan, fc_strerror(fc_get_errno()));
540 return FALSE;
544 fc_allocate_mutex(&scan->srvrs.mutex);
545 scan->srvrs.servers = server_list_new();
546 fc_release_mutex(&scan->srvrs.mutex);
548 return TRUE;
551 /**************************************************************************
552 Listens for UDP packets broadcasted from a server that responded
553 to the request-packet sent from the client.
554 **************************************************************************/
555 static enum server_scan_status
556 get_lan_server_list(struct server_scan *scan)
558 socklen_t fromlen;
559 union fc_sockaddr fromend;
560 char msgbuf[128];
561 int type;
562 struct data_in din;
563 char servername[512];
564 char portstr[256];
565 int port;
566 char version[256];
567 char status[256];
568 char players[256];
569 char humans[256];
570 char message[1024];
571 bool found_new = FALSE;
573 while (TRUE) {
574 struct server *pserver;
575 bool duplicate = FALSE;
577 dio_input_init(&din, msgbuf, sizeof(msgbuf));
578 fromlen = sizeof(fromend);
580 /* Try to receive a packet from a server. No select loop is needed;
581 * we just keep on reading until recvfrom returns -1. */
582 if (recvfrom(scan->sock, msgbuf, sizeof(msgbuf), 0,
583 &fromend.saddr, &fromlen) < 0) {
584 break;
587 dio_get_uint8(&din, &type);
588 if (type != SERVER_LAN_VERSION) {
589 continue;
591 dio_get_string(&din, servername, sizeof(servername));
592 dio_get_string(&din, portstr, sizeof(portstr));
593 port = atoi(portstr);
594 dio_get_string(&din, version, sizeof(version));
595 dio_get_string(&din, status, sizeof(status));
596 dio_get_string(&din, players, sizeof(players));
597 dio_get_string(&din, humans, sizeof(humans));
598 dio_get_string(&din, message, sizeof(message));
600 if (!fc_strcasecmp("none", servername)) {
601 bool nameinfo = FALSE;
602 #ifdef IPV6_SUPPORT
603 char dst[INET6_ADDRSTRLEN];
604 char host[NI_MAXHOST], service[NI_MAXSERV];
606 if (!getnameinfo(&fromend.saddr, fromlen, host, NI_MAXHOST,
607 service, NI_MAXSERV, NI_NUMERICSERV)) {
608 nameinfo = TRUE;
610 if (!nameinfo) {
611 if (fromend.saddr.sa_family == AF_INET6) {
612 inet_ntop(AF_INET6, &fromend.saddr_in6.sin6_addr,
613 dst, sizeof(dst));
614 } else if (fromend.saddr.sa_family == AF_INET) {
615 inet_ntop(AF_INET, &fromend.saddr_in4.sin_addr, dst, sizeof(dst));;
616 } else {
617 fc_assert(FALSE);
619 log_error("Unsupported address family in get_lan_server_list()");
621 fc_snprintf(dst, sizeof(dst), "Unknown");
624 #else /* IPv6 support */
625 const char *dst = NULL;
626 struct hostent *from;
627 const char *host = NULL;
629 from = gethostbyaddr((char *) &fromend.saddr_in4.sin_addr,
630 sizeof(fromend.saddr_in4.sin_addr), AF_INET);
631 if (from) {
632 host = from->h_name;
633 nameinfo = TRUE;
635 if (!nameinfo) {
636 dst = inet_ntoa(fromend.saddr_in4.sin_addr);
638 #endif /* IPv6 support */
640 sz_strlcpy(servername, nameinfo ? host : dst);
643 /* UDP can send duplicate or delayed packets. */
644 fc_allocate_mutex(&scan->srvrs.mutex);
645 server_list_iterate(scan->srvrs.servers, aserver) {
646 if (0 == fc_strcasecmp(aserver->host, servername)
647 && aserver->port == port) {
648 duplicate = TRUE;
649 break;
651 } server_list_iterate_end;
653 if (duplicate) {
654 fc_release_mutex(&scan->srvrs.mutex);
655 continue;
658 log_debug("Received a valid announcement from a server on the LAN.");
660 pserver = fc_malloc(sizeof(*pserver));
661 pserver->host = fc_strdup(servername);
662 pserver->port = port;
663 pserver->version = fc_strdup(version);
664 pserver->state = fc_strdup(status);
665 pserver->nplayers = atoi(players);
666 pserver->humans = atoi(humans);
667 pserver->message = fc_strdup(message);
668 pserver->players = NULL;
669 found_new = TRUE;
671 server_list_prepend(scan->srvrs.servers, pserver);
672 fc_release_mutex(&scan->srvrs.mutex);
675 if (found_new) {
676 return SCAN_STATUS_PARTIAL;
678 return SCAN_STATUS_WAITING;
681 /****************************************************************************
682 Creates a new server scan and returns it, or NULL if impossible.
684 Depending on 'type' the scan will look for either local or internet
685 games.
687 error_func provides a callback to be used in case of error; this
688 callback probably should call server_scan_finish.
690 NB: You must call server_scan_finish() when you are done with the
691 scan to free the memory and resources allocated by it.
692 ****************************************************************************/
693 struct server_scan *server_scan_begin(enum server_scan_type type,
694 ServerScanErrorFunc error_func)
696 struct server_scan *scan;
697 bool ok = FALSE;
699 scan = fc_calloc(1, sizeof(*scan));
700 scan->type = type;
701 scan->error_func = error_func;
702 scan->sock = -1;
703 fc_init_mutex(&scan->srvrs.mutex);
705 switch (type) {
706 case SERVER_SCAN_GLOBAL:
708 int thr_ret;
710 fc_init_mutex(&scan->meta.mutex);
711 scan->meta.status = SCAN_STATUS_WAITING;
712 thr_ret = fc_thread_start(&scan->meta.thr, metaserver_scan, scan);
713 if (thr_ret) {
714 ok = FALSE;
715 } else {
716 ok = TRUE;
719 break;
720 case SERVER_SCAN_LOCAL:
721 ok = begin_lanserver_scan(scan);
722 break;
723 default:
724 break;
727 if (!ok) {
728 server_scan_finish(scan);
729 scan = NULL;
732 return scan;
735 /****************************************************************************
736 A simple query function to determine the type of a server scan (previously
737 allocated in server_scan_begin).
738 ****************************************************************************/
739 enum server_scan_type server_scan_get_type(const struct server_scan *scan)
741 if (!scan) {
742 return SERVER_SCAN_LAST;
744 return scan->type;
747 /****************************************************************************
748 A function to query servers of the server scan. This will check any
749 pending network data and update the server list.
751 The return value indicates the status of the server scan:
752 SCAN_STATUS_ERROR - The scan failed and should be aborted.
753 SCAN_STATUS_WAITING - The scan is in progress (continue polling).
754 SCAN_STATUS_PARTIAL - The scan received some data, with more expected.
755 Get the servers with server_scan_get_list(), and
756 continue polling.
757 SCAN_STATUS_DONE - The scan received all data it expected to receive.
758 Get the servers with server_scan_get_list(), and
759 stop calling this function.
760 SCAN_STATUS_ABORT - The scan has been aborted
761 ****************************************************************************/
762 enum server_scan_status server_scan_poll(struct server_scan *scan)
764 if (!scan) {
765 return SCAN_STATUS_ERROR;
768 switch (scan->type) {
769 case SERVER_SCAN_GLOBAL:
771 enum server_scan_status status;
773 fc_allocate_mutex(&scan->meta.mutex);
774 status = scan->meta.status;
775 fc_release_mutex(&scan->meta.mutex);
777 return status;
779 break;
780 case SERVER_SCAN_LOCAL:
781 return get_lan_server_list(scan);
782 break;
783 default:
784 break;
787 return SCAN_STATUS_ERROR;
790 /**************************************************************************
791 Returns the srv_list currently held by the scan (may be NULL).
792 **************************************************************************/
793 struct srv_list *
794 server_scan_get_list(struct server_scan *scan)
796 if (!scan) {
797 return NULL;
800 return &scan->srvrs;
803 /**************************************************************************
804 Closes the socket listening on the scan, frees the list of servers, and
805 frees the memory allocated for 'scan' by server_scan_begin().
806 **************************************************************************/
807 void server_scan_finish(struct server_scan *scan)
809 if (!scan) {
810 return;
813 if (scan->type == SERVER_SCAN_GLOBAL) {
814 /* Signal metserver scan thread to stop */
815 fc_allocate_mutex(&scan->meta.mutex);
816 scan->meta.status = SCAN_STATUS_ABORT;
817 fc_release_mutex(&scan->meta.mutex);
819 /* Wait thread to stop */
820 fc_thread_wait(&scan->meta.thr);
821 fc_destroy_mutex(&scan->meta.mutex);
823 /* This mainly duplicates code from below "else" block.
824 * That's intentional, since they will be completely different in future versions.
825 * We are better prepared for that by having them separately already. */
826 if (scan->sock >= 0) {
827 fc_closesocket(scan->sock);
828 scan->sock = -1;
831 if (scan->srvrs.servers) {
832 fc_allocate_mutex(&scan->srvrs.mutex);
833 delete_server_list(scan->srvrs.servers);
834 scan->srvrs.servers = NULL;
835 fc_release_mutex(&scan->srvrs.mutex);
838 if (scan->meta.fp) {
839 fclose(scan->meta.fp);
840 scan->meta.fp = NULL;
842 } else {
843 if (scan->sock >= 0) {
844 fc_closesocket(scan->sock);
845 scan->sock = -1;
848 if (scan->srvrs.servers) {
849 delete_server_list(scan->srvrs.servers);
850 scan->srvrs.servers = NULL;
854 fc_destroy_mutex(&scan->srvrs.mutex);
856 free(scan);