Stop sharing requirement_unit_state_ereq().
[freeciv.git] / client / servers.c
blob45fc02699796c49bc5e1eb00d9b39c316dd6f06a
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 "fc_prehdrs.h"
20 #include <errno.h>
21 #include <stdlib.h>
23 #ifdef FREECIV_HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26 #ifdef HAVE_SYS_SOCKET_H
27 #include <sys/socket.h>
28 #endif
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
34 #endif
35 #ifdef HAVE_NETDB_H
36 #include <netdb.h>
37 #endif
38 #ifdef HAVE_PWD_H
39 #include <pwd.h>
40 #endif
41 #ifdef HAVE_SYS_SELECT_H
42 #include <sys/select.h>
43 #endif
44 #ifdef HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #endif
47 #ifdef HAVE_SYS_UIO_H
48 #include <sys/uio.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
54 /* dependencies */
55 #include "cvercmp.h"
57 /* utility */
58 #include "fcintl.h"
59 #include "fcthread.h"
60 #include "log.h"
61 #include "mem.h"
62 #include "netfile.h"
63 #include "netintf.h"
64 #include "rand.h" /* fc_rand() */
65 #include "registry.h"
66 #include "support.h"
68 /* common */
69 #include "capstr.h"
70 #include "dataio.h"
71 #include "game.h"
72 #include "packets.h"
73 #include "version.h"
75 /* client */
76 #include "chatline_common.h"
77 #include "chatline_g.h"
78 #include "client_main.h"
79 #include "servers.h"
81 #include "gui_main_g.h"
83 struct server_scan {
84 enum server_scan_type type;
85 ServerScanErrorFunc error_func;
87 struct srv_list srvrs;
88 int sock;
90 /* Only used for metaserver */
91 struct {
92 enum server_scan_status status;
94 fc_thread thr;
95 fc_mutex mutex;
97 const char *urlpath;
98 struct netfile_write_cb_data mem;
99 } meta;
102 extern enum announce_type announce;
104 static bool begin_metaserver_scan(struct server_scan *scan);
105 static void delete_server_list(struct server_list *server_list);
107 /**************************************************************************
108 The server sends a stream in a registry 'ini' type format.
109 Read it using secfile functions and fill the server_list structs.
110 **************************************************************************/
111 static struct server_list *parse_metaserver_data(fz_FILE *f)
113 struct server_list *server_list;
114 struct section_file *file;
115 int nservers, i, j;
116 const char *latest_ver;
117 const char *comment;
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);
125 comment = secfile_lookup_str_default(file, NULL, "version_comments." FOLLOWTAG);
127 if (latest_ver != NULL) {
128 const char *my_comparable = fc_comparable_version();
129 char vertext[2048];
131 log_verbose("Metaserver says latest '" FOLLOWTAG "' version is '%s'; we have '%s'",
132 latest_ver, my_comparable);
133 if (cvercmp_greater(latest_ver, my_comparable)) {
134 const char *const followtag = "?vertag:" FOLLOWTAG;
135 fc_snprintf(vertext, sizeof(vertext),
136 /* TRANS: Type is version tag name like "stable", "S2_4",
137 * "win32" (which can also be localised -- msgids start
138 * '?vertag:') */
139 _("Latest %s release of Freeciv is %s, this is %s."),
140 Q_(followtag), latest_ver, my_comparable);
142 version_message(vertext);
143 } else if (comment == NULL) {
144 fc_snprintf(vertext, sizeof(vertext),
145 _("There is no newer %s release of Freeciv available."),
146 FOLLOWTAG);
148 version_message(vertext);
152 if (comment != NULL) {
153 log_verbose("Mesaserver comment about '" FOLLOWTAG "': %s", comment);
154 version_message(comment);
157 server_list = server_list_new();
158 nservers = secfile_lookup_int_default(file, 0, "main.nservers");
160 for (i = 0; i < nservers; i++) {
161 const char *host, *port, *version, *state, *message, *nplayers, *nhumans;
162 int n;
163 struct server *pserver = (struct server*)fc_malloc(sizeof(struct server));
165 host = secfile_lookup_str_default(file, "", "server%d.host", i);
166 pserver->host = fc_strdup(host);
168 port = secfile_lookup_str_default(file, "", "server%d.port", i);
169 pserver->port = atoi(port);
171 version = secfile_lookup_str_default(file, "", "server%d.version", i);
172 pserver->version = fc_strdup(version);
174 state = secfile_lookup_str_default(file, "", "server%d.state", i);
175 pserver->state = fc_strdup(state);
177 message = secfile_lookup_str_default(file, "", "server%d.message", i);
178 pserver->message = fc_strdup(message);
180 nplayers = secfile_lookup_str_default(file, "0", "server%d.nplayers", i);
181 n = atoi(nplayers);
182 pserver->nplayers = n;
184 nhumans = secfile_lookup_str_default(file, "-1", "server%d.humans", i);
185 n = atoi(nhumans);
186 pserver->humans = n;
188 if (pserver->nplayers > 0) {
189 pserver->players = fc_malloc(pserver->nplayers * sizeof(*pserver->players));
190 } else {
191 pserver->players = NULL;
194 for (j = 0; j < pserver->nplayers ; j++) {
195 const char *name, *nation, *type, *plrhost;
197 name = secfile_lookup_str_default(file, "",
198 "server%d.player%d.name", i, j);
199 pserver->players[j].name = fc_strdup(name);
201 type = secfile_lookup_str_default(file, "",
202 "server%d.player%d.type", i, j);
203 pserver->players[j].type = fc_strdup(type);
205 plrhost = secfile_lookup_str_default(file, "",
206 "server%d.player%d.host", i, j);
207 pserver->players[j].host = fc_strdup(plrhost);
209 nation = secfile_lookup_str_default(file, "",
210 "server%d.player%d.nation", i, j);
211 pserver->players[j].nation = fc_strdup(nation);
214 server_list_append(server_list, pserver);
217 secfile_destroy(file);
219 return server_list;
222 /****************************************************************************
223 Read the reply string from the metaserver.
224 ****************************************************************************/
225 static bool meta_read_response(struct server_scan *scan)
227 fz_FILE *f;
228 char str[4096];
229 struct server_list *srvrs;
231 f = fz_from_memory(scan->meta.mem.mem, scan->meta.mem.size, TRUE);
232 if (NULL == f) {
233 fc_snprintf(str, sizeof(str),
234 _("Failed to read the metaserver data from %s."),
235 metaserver);
236 scan->error_func(scan, str);
238 return FALSE;
241 /* parse message body */
242 fc_allocate_mutex(&scan->srvrs.mutex);
243 srvrs = parse_metaserver_data(f);
244 scan->srvrs.servers = srvrs;
245 fc_release_mutex(&scan->srvrs.mutex);
247 /* 'f' (hence 'meta.mem.mem') was closed in parse_metaserver_data(). */
248 scan->meta.mem.mem = NULL;
250 if (NULL == srvrs) {
251 fc_snprintf(str, sizeof(str),
252 _("Failed to parse the metaserver data from %s:\n"
253 "%s."),
254 metaserver, secfile_error());
255 scan->error_func(scan, str);
257 return FALSE;
260 return TRUE;
263 /****************************************************************************
264 Metaserver scan thread entry point
265 ****************************************************************************/
266 static void metaserver_scan(void *arg)
268 struct server_scan *scan = arg;
270 if (!begin_metaserver_scan(scan)) {
271 fc_allocate_mutex(&scan->meta.mutex);
272 scan->meta.status = SCAN_STATUS_ERROR;
273 } else {
274 if (!meta_read_response(scan)) {
275 fc_allocate_mutex(&scan->meta.mutex);
276 scan->meta.status = SCAN_STATUS_ERROR;
277 } else {
278 fc_allocate_mutex(&scan->meta.mutex);
279 if (scan->meta.status == SCAN_STATUS_WAITING) {
280 scan->meta.status = SCAN_STATUS_DONE;
285 fc_release_mutex(&scan->meta.mutex);
288 /****************************************************************************
289 Begin a metaserver scan for servers.
291 Returns FALSE on error (in which case errbuf will contain an error
292 message).
293 ****************************************************************************/
294 static bool begin_metaserver_scan(struct server_scan *scan)
296 struct netfile_post *post;
297 bool retval = TRUE;
299 post = netfile_start_post();
300 netfile_add_form_str(post, "client_cap", our_capability);
302 if (!netfile_send_post(metaserver, post, NULL, &scan->meta.mem, NULL)) {
303 scan->error_func(scan, _("Error connecting to metaserver"));
304 retval = FALSE;
307 netfile_close_post(post);
309 return retval;
312 /**************************************************************************
313 Frees everything associated with a server list including
314 the server list itself (so the server_list is no longer
315 valid after calling this function)
316 **************************************************************************/
317 static void delete_server_list(struct server_list *server_list)
319 if (!server_list) {
320 return;
323 server_list_iterate(server_list, ptmp) {
324 int i;
325 int n = ptmp->nplayers;
327 free(ptmp->host);
328 free(ptmp->version);
329 free(ptmp->state);
330 free(ptmp->message);
332 if (ptmp->players) {
333 for (i = 0; i < n; i++) {
334 free(ptmp->players[i].name);
335 free(ptmp->players[i].type);
336 free(ptmp->players[i].host);
337 free(ptmp->players[i].nation);
339 free(ptmp->players);
342 free(ptmp);
343 } server_list_iterate_end;
345 server_list_destroy(server_list);
348 /**************************************************************************
349 Broadcast an UDP package to all servers on LAN, requesting information
350 about the server. The packet is send to all Freeciv servers in the same
351 multicast group as the client.
352 **************************************************************************/
353 static bool begin_lanserver_scan(struct server_scan *scan)
355 union fc_sockaddr addr;
356 struct raw_data_out dout;
357 int send_sock, opt = 1;
358 #ifndef FREECIV_HAVE_WINSOCK
359 unsigned char buffer[MAX_LEN_PACKET];
360 #else /* FREECIV_HAVE_WINSOCK */
361 char buffer[MAX_LEN_PACKET];
362 #endif /* FREECIV_HAVE_WINSOCK */
363 #ifdef HAVE_IP_MREQN
364 struct ip_mreqn mreq4;
365 #else
366 struct ip_mreq mreq4;
367 #endif
368 const char *group;
369 size_t size;
370 int family;
372 #ifdef FREECIV_IPV6_SUPPORT
373 struct ipv6_mreq mreq6;
374 #endif
376 #ifndef FREECIV_HAVE_WINSOCK
377 unsigned char ttl;
378 #endif
380 if (announce == ANNOUNCE_NONE) {
381 /* Succeeded in doing nothing */
382 return TRUE;
385 #ifdef FREECIV_IPV6_SUPPORT
386 if (announce == ANNOUNCE_IPV6) {
387 family = AF_INET6;
388 } else
389 #endif /* IPv6 support */
391 family = AF_INET;
394 /* Set the UDP Multicast group IP address. */
395 group = get_multicast_group(announce == ANNOUNCE_IPV6);
397 /* Create a socket for listening for server packets. */
398 if ((scan->sock = socket(family, SOCK_DGRAM, 0)) < 0) {
399 char errstr[2048];
401 fc_snprintf(errstr, sizeof(errstr),
402 _("Opening socket to listen LAN announcements failed:\n%s"),
403 fc_strerror(fc_get_errno()));
404 scan->error_func(scan, errstr);
406 return FALSE;
409 fc_nonblock(scan->sock);
411 if (setsockopt(scan->sock, SOL_SOCKET, SO_REUSEADDR,
412 (char *)&opt, sizeof(opt)) == -1) {
413 log_error("SO_REUSEADDR failed: %s", fc_strerror(fc_get_errno()));
416 memset(&addr, 0, sizeof(addr));
418 #ifdef FREECIV_IPV6_SUPPORT
419 if (family == AF_INET6) {
420 addr.saddr.sa_family = AF_INET6;
421 addr.saddr_in6.sin6_port = htons(SERVER_LAN_PORT + 1);
422 addr.saddr_in6.sin6_addr = in6addr_any;
423 } else
424 #endif /* IPv6 support */
425 if (family == AF_INET) {
426 addr.saddr.sa_family = AF_INET;
427 addr.saddr_in4.sin_port = htons(SERVER_LAN_PORT + 1);
428 addr.saddr_in4.sin_addr.s_addr = htonl(INADDR_ANY);
429 } else {
430 /* This is not only error situation worth assert() This
431 * is error situation that has check (with assert) against
432 * earlier already. */
433 fc_assert(FALSE);
435 return FALSE;
438 if (bind(scan->sock, &addr.saddr, sockaddr_size(&addr)) < 0) {
439 char errstr[2048];
441 fc_snprintf(errstr, sizeof(errstr),
442 _("Binding socket to listen LAN announcements failed:\n%s"),
443 fc_strerror(fc_get_errno()));
444 scan->error_func(scan, errstr);
446 return FALSE;
449 #ifdef FREECIV_IPV6_SUPPORT
450 if (family == AF_INET6) {
451 inet_pton(AF_INET6, group, &mreq6.ipv6mr_multiaddr.s6_addr);
452 mreq6.ipv6mr_interface = 0; /* TODO: Interface selection */
454 if (setsockopt(scan->sock, IPPROTO_IPV6, FC_IPV6_ADD_MEMBERSHIP,
455 (const char*)&mreq6, sizeof(mreq6)) < 0) {
456 char errstr[2048];
458 fc_snprintf(errstr, sizeof(errstr),
459 _("Adding membership for IPv6 LAN announcement group failed:\n%s"),
460 fc_strerror(fc_get_errno()));
461 scan->error_func(scan, errstr);
463 } else
464 #endif /* IPv6 support */
466 fc_inet_aton(group, &mreq4.imr_multiaddr, FALSE);
467 #ifdef HAVE_IP_MREQN
468 mreq4.imr_address.s_addr = htonl(INADDR_ANY);
469 mreq4.imr_ifindex = 0;
470 #else
471 mreq4.imr_interface.s_addr = htonl(INADDR_ANY);
472 #endif
474 if (setsockopt(scan->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
475 (const char*)&mreq4, sizeof(mreq4)) < 0) {
476 char errstr[2048];
478 fc_snprintf(errstr, sizeof(errstr),
479 _("Adding membership for IPv4 LAN announcement group failed:\n%s"),
480 fc_strerror(fc_get_errno()));
481 scan->error_func(scan, errstr);
483 return FALSE;
487 /* Create a socket for broadcasting to servers. */
488 if ((send_sock = socket(family, SOCK_DGRAM, 0)) < 0) {
489 log_error("socket failed: %s", fc_strerror(fc_get_errno()));
490 return FALSE;
493 memset(&addr, 0, sizeof(addr));
495 #ifdef FREECIV_IPV6_SUPPORT
496 if (family == AF_INET6) {
497 addr.saddr.sa_family = AF_INET6;
498 inet_pton(AF_INET6, group, &addr.saddr_in6.sin6_addr);
499 addr.saddr_in6.sin6_port = htons(SERVER_LAN_PORT);
500 } else
501 #endif /* IPv6 Support */
502 if (family == AF_INET) {
503 fc_inet_aton(group, &addr.saddr_in4.sin_addr, FALSE);
504 addr.saddr.sa_family = AF_INET;
505 addr.saddr_in4.sin_port = htons(SERVER_LAN_PORT);
506 } else {
507 fc_assert(FALSE);
509 log_error("Unsupported address family in begin_lanserver_scan()");
511 return FALSE;
514 /* this setsockopt call fails on Windows 98, so we stick with the default
515 * value of 1 on Windows, which should be fine in most cases */
516 #ifndef FREECIV_HAVE_WINSOCK
517 /* Set the Time-to-Live field for the packet */
518 ttl = SERVER_LAN_TTL;
519 if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl,
520 sizeof(ttl))) {
521 log_error("setsockopt failed: %s", fc_strerror(fc_get_errno()));
522 return FALSE;
524 #endif /* FREECIV_HAVE_WINSOCK */
526 if (setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (const char*)&opt,
527 sizeof(opt))) {
528 log_error("setsockopt failed: %s", fc_strerror(fc_get_errno()));
529 return FALSE;
532 dio_output_init(&dout, buffer, sizeof(buffer));
533 dio_put_uint8_raw(&dout, SERVER_LAN_VERSION);
534 size = dio_output_used(&dout);
537 if (sendto(send_sock, buffer, size, 0, &addr.saddr,
538 sockaddr_size(&addr)) < 0) {
539 /* This can happen when there's no network connection - it should
540 * give an in-game message. */
541 log_error("lanserver scan sendto failed: %s",
542 fc_strerror(fc_get_errno()));
543 return FALSE;
544 } else {
545 log_debug("Sending request for server announcement on LAN.");
548 fc_closesocket(send_sock);
550 fc_allocate_mutex(&scan->srvrs.mutex);
551 scan->srvrs.servers = server_list_new();
552 fc_release_mutex(&scan->srvrs.mutex);
554 return TRUE;
557 /**************************************************************************
558 Listens for UDP packets broadcasted from a server that responded
559 to the request-packet sent from the client.
560 **************************************************************************/
561 static enum server_scan_status
562 get_lan_server_list(struct server_scan *scan)
564 socklen_t fromlen;
565 union fc_sockaddr fromend;
566 char msgbuf[128];
567 int type;
568 struct data_in din;
569 char servername[512];
570 char portstr[256];
571 int port;
572 char version[256];
573 char status[256];
574 char players[256];
575 char humans[256];
576 char message[1024];
577 bool found_new = FALSE;
579 while (TRUE) {
580 struct server *pserver;
581 bool duplicate = FALSE;
583 dio_input_init(&din, msgbuf, sizeof(msgbuf));
584 fromlen = sizeof(fromend);
586 /* Try to receive a packet from a server. No select loop is needed;
587 * we just keep on reading until recvfrom returns -1. */
588 if (recvfrom(scan->sock, msgbuf, sizeof(msgbuf), 0,
589 &fromend.saddr, &fromlen) < 0) {
590 break;
593 dio_get_uint8_raw(&din, &type);
594 if (type != SERVER_LAN_VERSION) {
595 continue;
597 dio_get_string_raw(&din, servername, sizeof(servername));
598 dio_get_string_raw(&din, portstr, sizeof(portstr));
599 port = atoi(portstr);
600 dio_get_string_raw(&din, version, sizeof(version));
601 dio_get_string_raw(&din, status, sizeof(status));
602 dio_get_string_raw(&din, players, sizeof(players));
603 dio_get_string_raw(&din, humans, sizeof(humans));
604 dio_get_string_raw(&din, message, sizeof(message));
606 if (!fc_strcasecmp("none", servername)) {
607 bool nameinfo = FALSE;
608 #ifdef FREECIV_IPV6_SUPPORT
609 char dst[INET6_ADDRSTRLEN];
610 char host[NI_MAXHOST], service[NI_MAXSERV];
612 if (!getnameinfo(&fromend.saddr, fromlen, host, NI_MAXHOST,
613 service, NI_MAXSERV, NI_NUMERICSERV)) {
614 nameinfo = TRUE;
616 if (!nameinfo) {
617 if (fromend.saddr.sa_family == AF_INET6) {
618 inet_ntop(AF_INET6, &fromend.saddr_in6.sin6_addr,
619 dst, sizeof(dst));
620 } else if (fromend.saddr.sa_family == AF_INET) {
621 inet_ntop(AF_INET, &fromend.saddr_in4.sin_addr, dst, sizeof(dst));;
622 } else {
623 fc_assert(FALSE);
625 log_error("Unsupported address family in get_lan_server_list()");
627 fc_snprintf(dst, sizeof(dst), "Unknown");
630 #else /* IPv6 support */
631 const char *dst = NULL;
632 struct hostent *from;
633 const char *host = NULL;
635 from = gethostbyaddr((char *) &fromend.saddr_in4.sin_addr,
636 sizeof(fromend.saddr_in4.sin_addr), AF_INET);
637 if (from) {
638 host = from->h_name;
639 nameinfo = TRUE;
641 if (!nameinfo) {
642 dst = inet_ntoa(fromend.saddr_in4.sin_addr);
644 #endif /* IPv6 support */
646 sz_strlcpy(servername, nameinfo ? host : dst);
649 /* UDP can send duplicate or delayed packets. */
650 fc_allocate_mutex(&scan->srvrs.mutex);
651 server_list_iterate(scan->srvrs.servers, aserver) {
652 if (0 == fc_strcasecmp(aserver->host, servername)
653 && aserver->port == port) {
654 duplicate = TRUE;
655 break;
657 } server_list_iterate_end;
659 if (duplicate) {
660 fc_release_mutex(&scan->srvrs.mutex);
661 continue;
664 log_debug("Received a valid announcement from a server on the LAN.");
666 pserver = fc_malloc(sizeof(*pserver));
667 pserver->host = fc_strdup(servername);
668 pserver->port = port;
669 pserver->version = fc_strdup(version);
670 pserver->state = fc_strdup(status);
671 pserver->nplayers = atoi(players);
672 pserver->humans = atoi(humans);
673 pserver->message = fc_strdup(message);
674 pserver->players = NULL;
675 found_new = TRUE;
677 server_list_prepend(scan->srvrs.servers, pserver);
678 fc_release_mutex(&scan->srvrs.mutex);
681 if (found_new) {
682 return SCAN_STATUS_PARTIAL;
684 return SCAN_STATUS_WAITING;
687 /****************************************************************************
688 Creates a new server scan and returns it, or NULL if impossible.
690 Depending on 'type' the scan will look for either local or internet
691 games.
693 error_func provides a callback to be used in case of error; this
694 callback probably should call server_scan_finish.
696 NB: You must call server_scan_finish() when you are done with the
697 scan to free the memory and resources allocated by it.
698 ****************************************************************************/
699 struct server_scan *server_scan_begin(enum server_scan_type type,
700 ServerScanErrorFunc error_func)
702 struct server_scan *scan;
703 bool ok = FALSE;
705 scan = fc_calloc(1, sizeof(*scan));
706 scan->type = type;
707 scan->error_func = error_func;
708 scan->sock = -1;
709 fc_init_mutex(&scan->srvrs.mutex);
711 switch (type) {
712 case SERVER_SCAN_GLOBAL:
714 int thr_ret;
716 fc_init_mutex(&scan->meta.mutex);
717 scan->meta.status = SCAN_STATUS_WAITING;
718 thr_ret = fc_thread_start(&scan->meta.thr, metaserver_scan, scan);
719 if (thr_ret) {
720 ok = FALSE;
721 } else {
722 ok = TRUE;
725 break;
726 case SERVER_SCAN_LOCAL:
727 ok = begin_lanserver_scan(scan);
728 break;
729 default:
730 break;
733 if (!ok) {
734 server_scan_finish(scan);
735 scan = NULL;
738 return scan;
741 /****************************************************************************
742 A simple query function to determine the type of a server scan (previously
743 allocated in server_scan_begin).
744 ****************************************************************************/
745 enum server_scan_type server_scan_get_type(const struct server_scan *scan)
747 if (!scan) {
748 return SERVER_SCAN_LAST;
750 return scan->type;
753 /****************************************************************************
754 A function to query servers of the server scan. This will check any
755 pending network data and update the server list.
757 The return value indicates the status of the server scan:
758 SCAN_STATUS_ERROR - The scan failed and should be aborted.
759 SCAN_STATUS_WAITING - The scan is in progress (continue polling).
760 SCAN_STATUS_PARTIAL - The scan received some data, with more expected.
761 Get the servers with server_scan_get_list(), and
762 continue polling.
763 SCAN_STATUS_DONE - The scan received all data it expected to receive.
764 Get the servers with server_scan_get_list(), and
765 stop calling this function.
766 SCAN_STATUS_ABORT - The scan has been aborted
767 ****************************************************************************/
768 enum server_scan_status server_scan_poll(struct server_scan *scan)
770 if (!scan) {
771 return SCAN_STATUS_ERROR;
774 switch (scan->type) {
775 case SERVER_SCAN_GLOBAL:
777 enum server_scan_status status;
779 fc_allocate_mutex(&scan->meta.mutex);
780 status = scan->meta.status;
781 fc_release_mutex(&scan->meta.mutex);
783 return status;
785 break;
786 case SERVER_SCAN_LOCAL:
787 return get_lan_server_list(scan);
788 break;
789 default:
790 break;
793 return SCAN_STATUS_ERROR;
796 /**************************************************************************
797 Returns the srv_list currently held by the scan (may be NULL).
798 **************************************************************************/
799 struct srv_list *
800 server_scan_get_list(struct server_scan *scan)
802 if (!scan) {
803 return NULL;
806 return &scan->srvrs;
809 /**************************************************************************
810 Closes the socket listening on the scan, frees the list of servers, and
811 frees the memory allocated for 'scan' by server_scan_begin().
812 **************************************************************************/
813 void server_scan_finish(struct server_scan *scan)
815 if (!scan) {
816 return;
819 if (scan->type == SERVER_SCAN_GLOBAL) {
820 /* Signal metaserver scan thread to stop */
821 fc_allocate_mutex(&scan->meta.mutex);
822 scan->meta.status = SCAN_STATUS_ABORT;
823 fc_release_mutex(&scan->meta.mutex);
825 /* Wait thread to stop */
826 fc_thread_wait(&scan->meta.thr);
827 fc_destroy_mutex(&scan->meta.mutex);
829 /* This mainly duplicates code from below "else" block.
830 * That's intentional, since they will be completely different in future versions.
831 * We are better prepared for that by having them separately already. */
832 if (scan->sock >= 0) {
833 fc_closesocket(scan->sock);
834 scan->sock = -1;
837 if (scan->srvrs.servers) {
838 fc_allocate_mutex(&scan->srvrs.mutex);
839 delete_server_list(scan->srvrs.servers);
840 scan->srvrs.servers = NULL;
841 fc_release_mutex(&scan->srvrs.mutex);
844 if (scan->meta.mem.mem) {
845 FC_FREE(scan->meta.mem.mem);
846 scan->meta.mem.mem = NULL;
848 } else {
849 if (scan->sock >= 0) {
850 fc_closesocket(scan->sock);
851 scan->sock = -1;
854 if (scan->srvrs.servers) {
855 delete_server_list(scan->srvrs.servers);
856 scan->srvrs.servers = NULL;
860 fc_destroy_mutex(&scan->srvrs.mutex);
862 free(scan);