Stop sharing requirement_unit_state_ereq().
[freeciv.git] / client / clinet.c
blob17a37a381d2f44f80846cddad61e5f8f8fc3f326
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
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 <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #ifdef FREECIV_HAVE_SYS_TYPES_H
26 #include <sys/types.h>
27 #endif
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
30 #endif
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
33 #endif
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
36 #endif
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
40 #ifdef HAVE_PWD_H
41 #include <pwd.h>
42 #endif
43 #ifdef HAVE_SYS_SELECT_H
44 #include <sys/select.h>
45 #endif
46 #ifdef HAVE_SYS_TIME_H
47 #include <sys/time.h>
48 #endif
49 #ifdef HAVE_SYS_UIO_H
50 #include <sys/uio.h>
51 #endif
52 #ifdef HAVE_SYS_UTSNAME_H
53 #include <sys/utsname.h>
54 #endif
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
59 /* utility */
60 #include "capstr.h"
61 #include "dataio.h"
62 #include "fcintl.h"
63 #include "log.h"
64 #include "mem.h"
65 #include "netintf.h"
66 #include "registry.h"
67 #include "support.h"
69 /* common */
70 #include "game.h"
71 #include "packets.h"
72 #include "version.h"
74 /* client */
75 #include "agents.h"
76 #include "attribute.h"
77 #include "chatline_g.h"
78 #include "client_main.h"
79 #include "climisc.h"
80 #include "connectdlg_common.h"
81 #include "connectdlg_g.h"
82 #include "dialogs_g.h" /* popdown_races_dialog() */
83 #include "gui_main_g.h" /* add_net_input(), remove_net_input() */
84 #include "mapview_common.h" /* unqueue_mapview_update */
85 #include "menu_g.h"
86 #include "messagewin_g.h"
87 #include "options.h"
88 #include "packhand.h"
89 #include "pages_g.h"
90 #include "plrdlg_g.h"
91 #include "repodlgs_g.h"
93 #include "clinet.h"
95 /* In autoconnect mode, try to connect to once a second */
96 #define AUTOCONNECT_INTERVAL 500
98 /* In autoconnect mode, try to connect 100 times */
99 #define MAX_AUTOCONNECT_ATTEMPTS 100
101 static struct fc_sockaddr_list *list = NULL;
102 static int name_count;
104 /*************************************************************************
105 Close socket and cleanup. This one doesn't print a message, so should
106 do so before-hand if necessary.
107 **************************************************************************/
108 static void close_socket_nomessage(struct connection *pc)
110 connection_common_close(pc);
111 remove_net_input();
112 popdown_races_dialog();
113 close_connection_dialog();
115 set_client_state(C_S_DISCONNECTED);
118 /****************************************************************************
119 Client connection close socket callback. It shouldn't be called directy.
120 Use connection_close() instead.
121 ****************************************************************************/
122 static void client_conn_close_callback(struct connection *pconn)
124 char reason[256];
126 if (NULL != pconn->closing_reason) {
127 fc_strlcpy(reason, pconn->closing_reason, sizeof(reason));
128 } else {
129 fc_strlcpy(reason, _("unknown reason"), sizeof(reason));
132 close_socket_nomessage(pconn);
133 /* If we lost connection to the internal server - kill it. */
134 client_kill_server(TRUE);
135 log_error("Lost connection to server: %s.", reason);
136 output_window_printf(ftc_client, _("Lost connection to server (%s)!"),
137 reason);
140 /**************************************************************************
141 Get ready to [try to] connect to a server:
142 - translate HOSTNAME and PORT (with defaults of "localhost" and
143 DEFAULT_SOCK_PORT respectively) to a raw IP address and port number, and
144 store them in the `names' variable
145 - return 0 on success
146 or put an error message in ERRBUF and return -1 on failure
147 **************************************************************************/
148 static int get_server_address(const char *hostname, int port,
149 char *errbuf, int errbufsize)
151 if (port == 0) {
152 #ifdef FREECIV_JSON_CONNECTION
153 port = FREECIV_JSON_PORT;
154 #else /* FREECIV_JSON_CONNECTION */
155 port = DEFAULT_SOCK_PORT;
156 #endif /* FREECIV_JSON_CONNECTION */
159 /* use name to find TCP/IP address of server */
160 if (!hostname) {
161 hostname = "localhost";
164 if (list != NULL) {
165 fc_sockaddr_list_destroy(list);
168 /* Any supported family will do */
169 list = net_lookup_service(hostname, port, FC_ADDR_ANY);
171 name_count = fc_sockaddr_list_size(list);
173 if (name_count <= 0) {
174 (void) fc_strlcpy(errbuf, _("Failed looking up host."), errbufsize);
175 return -1;
178 return 0;
181 /**************************************************************************
182 Try to connect to a server (get_server_address() must be called first!):
183 - try to create a TCP socket and connect it to `names'
184 - if successful:
185 - start monitoring the socket for packets from the server
186 - send a "login request" packet to the server
187 and - return 0
188 - if unable to create the connection, close the socket, put an error
189 message in ERRBUF and return the Unix error code (ie., errno, which
190 will be non-zero).
191 **************************************************************************/
192 static int try_to_connect(const char *username, char *errbuf, int errbufsize)
194 int sock = -1;
195 fc_errno err = 0;
197 connections_set_close_callback(client_conn_close_callback);
199 /* connection in progress? wait. */
200 if (client.conn.used) {
201 (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize);
202 return -1;
205 /* Try all (IPv4, IPv6, ...) addresses until we have a connection. */
206 sock = -1;
207 fc_sockaddr_list_iterate(list, paddr) {
208 if ((sock = socket(paddr->saddr.sa_family, SOCK_STREAM, 0)) == -1) {
209 if (err == 0) {
210 err = fc_get_errno();
212 /* Probably EAFNOSUPPORT or EPROTONOSUPPORT. */
213 continue;
216 if (fc_connect(sock, &paddr->saddr,
217 sockaddr_size(paddr)) == -1) {
218 err = fc_get_errno(); /* Save errno value before calling anything */
219 fc_closesocket(sock);
220 sock = -1;
221 continue;
222 } else {
223 /* We have a connection! */
224 break;
226 } fc_sockaddr_list_iterate_end;
228 client.conn.sock = sock;
229 if (client.conn.sock == -1) {
230 (void) fc_strlcpy(errbuf, fc_strerror(err), errbufsize);
231 #ifdef FREECIV_HAVE_WINSOCK
232 return -1;
233 #else
234 return err;
235 #endif /* FREECIV_HAVE_WINSOCK */
238 make_connection(client.conn.sock, username);
240 return 0;
243 /**************************************************************************
244 Connect to a freeciv-server instance -- or at least try to. On success,
245 return 0; on failure, put an error message in ERRBUF and return -1.
246 **************************************************************************/
247 int connect_to_server(const char *username, const char *hostname, int port,
248 char *errbuf, int errbufsize)
250 if (errbufsize > 0 && errbuf != NULL) {
251 errbuf[0] = '\0';
254 if (0 != get_server_address(hostname, port, errbuf, errbufsize)) {
255 return -1;
258 if (0 != try_to_connect(username, errbuf, errbufsize)) {
259 return -1;
262 if (gui_options.use_prev_server) {
263 sz_strlcpy(gui_options.default_server_host, hostname);
264 gui_options.default_server_port = port;
267 return 0;
270 /**************************************************************************
271 Called after a connection is completed (e.g., in try_to_connect).
272 **************************************************************************/
273 void make_connection(int sock, const char *username)
275 struct packet_server_join_req req;
277 connection_common_init(&client.conn);
278 client.conn.sock = sock;
279 client.conn.client.last_request_id_used = 0;
280 client.conn.client.last_processed_request_id_seen = 0;
281 client.conn.client.request_id_of_currently_handled_packet = 0;
282 client.conn.incoming_packet_notify = notify_about_incoming_packet;
283 client.conn.outgoing_packet_notify = notify_about_outgoing_packet;
285 /* call gui-dependent stuff in gui_main.c */
286 add_net_input(client.conn.sock);
288 /* now send join_request package */
290 req.major_version = MAJOR_VERSION;
291 req.minor_version = MINOR_VERSION;
292 req.patch_version = PATCH_VERSION;
293 sz_strlcpy(req.version_label, VERSION_LABEL);
294 sz_strlcpy(req.capability, our_capability);
295 sz_strlcpy(req.username, username);
297 send_packet_server_join_req(&client.conn, &req);
300 /**************************************************************************
301 Get rid of server connection. This also kills internal server if it's
302 used.
303 **************************************************************************/
304 void disconnect_from_server(void)
306 const bool force = !client.conn.used;
308 attribute_flush();
310 stop_turn_change_wait();
312 /* If it's internal server - kill him
313 * We assume that we are always connected to the internal server */
314 if (!force) {
315 client_kill_server(FALSE);
317 close_socket_nomessage(&client.conn);
318 if (force) {
319 client_kill_server(TRUE);
321 output_window_append(ftc_client, _("Disconnected from server."));
323 if (gui_options.save_options_on_exit) {
324 options_save(NULL);
328 /****************************************************************************
329 A wrapper around read_socket_data() which also handles the case the
330 socket becomes writeable and there is still data which should be sent
331 to the server.
333 Returns:
334 -1 : an error occurred - you should close the socket
335 -2 : the connection was closed
336 >0 : number of bytes read
337 =0 : no data read, would block
338 ****************************************************************************/
339 static int read_from_connection(struct connection *pc, bool block)
341 for (;;) {
342 fd_set readfs, writefs, exceptfs;
343 int socket_fd = pc->sock;
344 bool have_data_for_server = (pc->used && pc->send_buffer
345 && 0 < pc->send_buffer->ndata);
346 int n;
347 fc_timeval tv;
349 tv.tv_sec = 0;
350 tv.tv_usec = 0;
352 FC_FD_ZERO(&readfs);
353 FD_SET(socket_fd, &readfs);
355 FC_FD_ZERO(&exceptfs);
356 FD_SET(socket_fd, &exceptfs);
358 if (have_data_for_server) {
359 FC_FD_ZERO(&writefs);
360 FD_SET(socket_fd, &writefs);
361 n = fc_select(socket_fd + 1, &readfs, &writefs, &exceptfs,
362 block ? NULL : &tv);
363 } else {
364 n = fc_select(socket_fd + 1, &readfs, NULL, &exceptfs,
365 block ? NULL : &tv);
368 /* the socket is neither readable, writeable nor got an
369 * exception */
370 if (n == 0) {
371 return 0;
374 if (n == -1) {
375 if (errno == EINTR) {
376 /* EINTR can happen sometimes, especially when compiling with -pg.
377 * Generally we just want to run select again. */
378 log_debug("select() returned EINTR");
379 continue;
382 log_error("select() return=%d errno=%d (%s)",
383 n, errno, fc_strerror(fc_get_errno()));
384 return -1;
387 if (FD_ISSET(socket_fd, &exceptfs)) {
388 return -1;
391 if (have_data_for_server && FD_ISSET(socket_fd, &writefs)) {
392 flush_connection_send_buffer_all(pc);
395 if (FD_ISSET(socket_fd, &readfs)) {
396 return read_socket_data(socket_fd, pc->buffer);
401 /**************************************************************************
402 This function is called when the client received a new input from the
403 server.
404 **************************************************************************/
405 void input_from_server(int fd)
407 int nb;
409 fc_assert_ret(fd == client.conn.sock);
411 nb = read_from_connection(&client.conn, FALSE);
412 if (0 <= nb) {
413 agents_freeze_hint();
414 while (client.conn.used) {
415 enum packet_type type;
416 void *packet = get_packet_from_connection(&client.conn, &type);
418 if (NULL != packet) {
419 client_packet_input(packet, type);
420 free(packet);
421 } else {
422 break;
425 if (client.conn.used) {
426 agents_thaw_hint();
428 } else if (-2 == nb) {
429 connection_close(&client.conn, _("server disconnected"));
430 } else {
431 connection_close(&client.conn, _("read error"));
435 /**************************************************************************
436 This function will sniff at the given fd, get the packet and call
437 client_packet_input. It will return if there is a network error or if
438 the PACKET_PROCESSING_FINISHED packet for the given request is
439 received.
440 **************************************************************************/
441 void input_from_server_till_request_got_processed(int fd,
442 int expected_request_id)
444 fc_assert_ret(expected_request_id);
445 fc_assert_ret(fd == client.conn.sock);
447 log_debug("input_from_server_till_request_got_processed("
448 "expected_request_id=%d)", expected_request_id);
450 while (TRUE) {
451 int nb = read_from_connection(&client.conn, TRUE);
453 if (0 <= nb) {
454 enum packet_type type;
456 while (TRUE) {
457 void *packet = get_packet_from_connection(&client.conn, &type);
458 if (NULL == packet) {
459 break;
462 client_packet_input(packet, type);
463 free(packet);
465 if (type == PACKET_PROCESSING_FINISHED) {
466 log_debug("ifstrgp: expect=%d, seen=%d",
467 expected_request_id,
468 client.conn.client.last_processed_request_id_seen);
469 if (client.conn.client.last_processed_request_id_seen >=
470 expected_request_id) {
471 log_debug("ifstrgp: got it; returning");
472 return;
476 } else if (-2 == nb) {
477 connection_close(&client.conn, _("server disconnected"));
478 break;
479 } else {
480 connection_close(&client.conn, _("read error"));
481 break;
486 static bool autoconnecting = FALSE;
487 /**************************************************************************
488 Make an attempt to autoconnect to the server.
489 It returns number of seconds it should be called again.
490 **************************************************************************/
491 double try_to_autoconnect(void)
493 char errbuf[512];
494 static int count = 0;
495 #ifndef FREECIV_MSWINDOWS
496 static int warning_shown = 0;
497 #endif
499 if (!autoconnecting) {
500 return FC_INFINITY;
503 count++;
505 if (count >= MAX_AUTOCONNECT_ATTEMPTS) {
506 log_fatal(_("Failed to contact server \"%s\" at port "
507 "%d as \"%s\" after %d attempts"),
508 server_host, server_port, user_name, count);
509 exit(EXIT_FAILURE);
512 switch (try_to_connect(user_name, errbuf, sizeof(errbuf))) {
513 case 0: /* Success! */
514 /* Don't call me again */
515 autoconnecting = FALSE;
516 return FC_INFINITY;
517 #ifndef FREECIV_MSWINDOWS
518 /* See PR#4042 for more info on issues with try_to_connect() and errno. */
519 case ECONNREFUSED: /* Server not available (yet) */
520 if (!warning_shown) {
521 log_error("Connection to server refused. Please start the server.");
522 output_window_append(ftc_client, _("Connection to server refused. "
523 "Please start the server."));
524 warning_shown = 1;
526 /* Try again in 0.5 seconds */
527 return 0.001 * AUTOCONNECT_INTERVAL;
528 #endif /* FREECIV_MSWINDOWS */
529 default: /* All other errors are fatal */
530 log_fatal(_("Error contacting server \"%s\" at port %d "
531 "as \"%s\":\n %s\n"),
532 server_host, server_port, user_name, errbuf);
533 exit(EXIT_FAILURE);
537 /**************************************************************************
538 Start trying to autoconnect to freeciv-server. Calls
539 get_server_address(), then arranges for try_to_autoconnect(), which
540 calls try_to_connect(), to be called roughly every
541 AUTOCONNECT_INTERVAL milliseconds, until success, fatal error or
542 user intervention.
543 **************************************************************************/
544 void start_autoconnecting_to_server(void)
546 char buf[512];
548 output_window_printf(ftc_client,
549 _("Auto-connecting to server \"%s\" at port %d "
550 "as \"%s\" every %f second(s) for %d times"),
551 server_host, server_port, user_name,
552 0.001 * AUTOCONNECT_INTERVAL,
553 MAX_AUTOCONNECT_ATTEMPTS);
555 if (get_server_address(server_host, server_port, buf, sizeof(buf)) < 0) {
556 log_fatal(_("Error contacting server \"%s\" at port %d "
557 "as \"%s\":\n %s\n"),
558 server_host, server_port, user_name, buf);
559 exit(EXIT_FAILURE);
561 autoconnecting = TRUE;