Freeciv21
Develop your civilization from humble roots to a global empire
civserver.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include <fc_config.h>
15 
16 #include <QString>
17 
18 #include <csignal>
19 #include <cstdio>
20 #include <cstdlib>
21 #ifdef FREECIV_MSWINDOWS
22 #include <windows.h>
23 #else
24 #include <unistd.h> // SIGHUP, SIGPIPE
25 #endif
26 
27 // Qt
28 #include <QCommandLineParser>
29 #include <QCoreApplication>
30 
31 // utility
32 #include "deprecations.h"
33 #include "fciconv.h"
34 #include "fcintl.h"
35 #include "log.h"
36 #include "support.h"
37 #include "timing.h"
38 
39 // common
40 #include "capstr.h"
41 #include "game.h"
42 #include "version.h"
43 
44 // server
45 #include "aiiface.h"
46 #include "console.h"
47 #include "sernet.h"
48 #include "server.h"
49 #include "srv_main.h"
50 
51 #define save_and_exit(sig) \
52  if (S_S_RUNNING == server_state()) { \
53  save_game_auto(#sig, AS_INTERRUPT); \
54  } \
55  QCoreApplication::exit(EXIT_SUCCESS);
56 
61 static void signal_handler(int sig)
62 {
63  static civtimer *timer = nullptr;
64 
65  switch (sig) {
66  case SIGINT:
67  if (timer && timer_read_seconds(timer) <= 1.0) {
68  save_and_exit(SIGINT);
69  } else {
70  if (game.info.timeout == -1) {
71  qInfo(_("Setting timeout to 0. Autogame will stop."));
72  game.info.timeout = 0;
73  }
74  if (!timer) {
75  qInfo(_("You must interrupt Freeciv21 twice "
76  "within one second to make it exit."));
77  }
78  }
79  timer = timer_renew(timer, TIMER_USER, TIMER_ACTIVE);
80  timer_start(timer);
81  break;
82 
83 #ifdef SIGHUP
84  case SIGHUP:
85  save_and_exit(SIGHUP);
86  break;
87 #endif // SIGHUP
88 
89  case SIGTERM:
90  save_and_exit(SIGTERM);
91  break;
92 
93 #ifdef SIGPIPE
94  case SIGPIPE:
95  if (signal(SIGPIPE, signal_handler) == SIG_ERR) {
96  /* Because the signal may have interrupted arbitrary code, we use
97  * fprintf() and _exit() here instead of log_*() and exit() so
98  * that we don't accidentally call any "unsafe" functions here
99  * (see the manual page for the signal function). */
100  fprintf(stderr, "\nFailed to reset SIGPIPE handler "
101  "while handling SIGPIPE.\n");
102  _exit(EXIT_FAILURE);
103  }
104  break;
105 #endif // SIGPIPE
106  }
107 }
108 
114 int main(int argc, char *argv[])
115 {
116  if (SIG_ERR == signal(SIGINT, signal_handler)) {
117  fc_fprintf(stderr, _("Failed to install SIGINT handler: %s\n"),
119  exit(EXIT_FAILURE);
120  }
121 
122 #ifdef SIGHUP
123  if (SIG_ERR == signal(SIGHUP, signal_handler)) {
124  fc_fprintf(stderr, _("Failed to install SIGHUP handler: %s\n"),
126  exit(EXIT_FAILURE);
127  }
128 #endif // SIGHUP
129 
130  if (SIG_ERR == signal(SIGTERM, signal_handler)) {
131  fc_fprintf(stderr, _("Failed to install SIGTERM handler: %s\n"),
133  exit(EXIT_FAILURE);
134  }
135 
136 #ifdef SIGPIPE
137  /* Ignore SIGPIPE, the error is handled by the return value
138  * of the write call. */
139  if (SIG_ERR == signal(SIGPIPE, signal_handler)) {
140  fc_fprintf(stderr, _("Failed to ignore SIGPIPE: %s\n"),
142  exit(EXIT_FAILURE);
143  }
144 #endif // SIGPIPE
145 
146  QCoreApplication app(argc, argv);
147  QCoreApplication::setApplicationVersion(freeciv21_version());
148 
149  // initialize server
150  srv_init();
151 
152  // parse command-line arguments...
154 
155  game.server.meta_info.type[0] = '\0';
156 
157  QCommandLineParser parser;
158  parser.addHelpOption();
159  parser.addVersionOption();
160 
161  // List of all the supported options
162  bool ok = parser.addOptions({
163  {{"A", "Announce"},
164  // TRANS: Do not translate IPv4, IPv6 and none
165  _("Announce game in LAN using protocol PROTO (IPv4/IPv6/none)."),
166  // TRANS: Command-line argument
167  _("PROTO"),
168  "IPv4"},
169  {{"B", "Bind-meta"},
170  _("Connect to metaserver from this address."),
171  // TRANS: Command-line argument
172  _("ADDR")},
173  {{"b", "bind"},
174  _("Listen for clients on ADDR."),
175  // TRANS: Command-line argument
176  _("ADDR")},
177  {QStringLiteral("local"), _("Listens to the local socket NAME"),
178  // TRANS: Command-line argument
179  _("NAME")},
180  {{"d", _("debug")},
181  // TRANS: Do not translate "fatal", "critical", "warning", "info" or
182  // "debug". It's exactly what the user must type.
183  _("Set debug log level (fatal/critical/warning/info/debug)."),
184  _("LEVEL"),
185  QStringLiteral("info")},
186  {{"e", "exit-on-end"},
187  _("When a game ends, exit instead of restarting.")},
188  {{"F", "Fatal"}, _("Raise a signal on failed assertion.")},
189  {{"f", "file"},
190  _("Load saved game."),
191  // TRANS: Command-line argument
192  _("FILE")},
193  {{"i", "identity"},
194  _("Be known as ADDR at metaserver or LAN client."),
195  // TRANS: Command-line argument
196  _("ADDR")},
197  {{"k", "keep"},
198  _("Keep updating game information on metaserver even after "
199  "failure.")},
200  {{"l", "log"},
201  _("Use FILE as logfile."),
202  // TRANS: Command-line argument
203  _("FILE")},
204  {{"M", "Metaserver"},
205  _("Set ADDR as metaserver address."),
206  // TRANS: Command-line argument
207  _("ADDR")},
208  {{"m", "meta"}, _("Notify metaserver and send server's info.")},
209  {{"p", "port"},
210  _("Listen for clients on port PORT."),
211  // TRANS: Command-line argument
212  _("PORT")},
213  {{"q", "quitidle"},
214  _("Quit if no players for TIME seconds."),
215  // TRANS: Command-line argument
216  _("TIME")},
217  {{"R", "Ranklog"},
218  _("Use FILE as ranking logfile."),
219  // TRANS: Command-line argument
220  _("FILE")},
221  {{"r", "read"},
222  _("Read startup file FILE."),
223  // TRANS: Command-line argument
224  _("FILE")},
225  {{"S", "Serverid"},
226  _("Sets the server id to ID."),
227  // TRANS: Command-line argument
228  _("ID")},
229  {{"s", "saves"},
230  _("Save games to directory DIR."),
231  // TRANS: Command-line argument
232  _("DIR")},
233  {{"t", "timetrack"},
234  _("Prints stats about elapsed time on misc tasks.")},
235  {{"w", "warnings"}, _("Warn about deprecated modpack constructs.")},
236  {"ruleset", _("Load ruleset RULESET."),
237  // TRANS: Command-line argument
238  _("RULESET")},
239  {"scenarios", _("Save scenarios to directory DIR."),
240  // TRANS: Command-line argument
241  _("DIR")},
242  {{"a", "auth"},
243  _("Enable database authentication (requires --Database).")},
244  {{"D", "Database"},
245  _("Enable database connection with configuration from FILE."),
246  // TRANS: Command-line argument
247  _("FILE")},
248  {{"G", "Guests"}, _("Allow guests to login if auth is enabled.")},
249  {{"N", "Newusers"}, _("Allow new users to login if auth is enabled.")},
250 #ifdef AI_MODULES
251  {"LoadAI", _("Load ai module MODULE. Can appear multiple times."),
252  // TRANS: Command-line argument
253  _("MODULE")},
254 #endif // AI_MODULES
255  });
256  if (!ok) {
257  qCritical("Adding command line arguments failed.");
258  exit(EXIT_FAILURE);
259  }
260 
261  // Parse
262  parser.process(app);
263 
264  // Process the parsed options
265  if (!log_init(parser.value(QStringLiteral("debug")))) {
266  exit(EXIT_FAILURE);
267  }
268  if (parser.isSet(QStringLiteral("file"))) {
269  srvarg.load_filename = parser.value(QStringLiteral("file"));
270  }
271  if (parser.isSet(QStringLiteral("log"))) {
272  srvarg.log_filename = parser.value(QStringLiteral("log"));
273  }
274  fc_assert_set_fatal(parser.isSet(QStringLiteral("Fatal")));
275  if (parser.isSet(QStringLiteral("Ranklog"))) {
276  srvarg.ranklog_filename = parser.value(QStringLiteral("Ranklog"));
277  }
278  if (parser.isSet(QStringLiteral("keep"))) {
280  // Implies --meta
281  srvarg.metaserver_no_send = false;
282  }
283  if (parser.isSet(QStringLiteral("meta"))) {
284  srvarg.metaserver_no_send = false;
285  }
286  if (parser.isSet(QStringLiteral("Metaserver"))) {
287  srvarg.ranklog_filename = parser.value(QStringLiteral("Metaserver"));
288  // Implies --meta
289  srvarg.metaserver_no_send = false;
290  }
291  if (parser.isSet(QStringLiteral("identity"))) {
292  srvarg.ranklog_filename = parser.value(QStringLiteral("identity"));
293  }
294  if (parser.isSet(QStringLiteral("local"))) {
295  srvarg.local_addr = parser.value(QStringLiteral("local"));
296  }
297  if (parser.isSet(QStringLiteral("port"))) {
298  if (!srvarg.local_addr.isEmpty()) {
299  qCritical(_("Cannot use --port with --local"));
300  exit(EXIT_FAILURE);
301  }
302  bool conversion_ok;
303  srvarg.port =
304  parser.value(QStringLiteral("port")).toUInt(&conversion_ok);
306  if (!conversion_ok) {
307  qCritical(_("Invalid port number %s"),
308  qUtf8Printable(parser.value("port")));
309  exit(EXIT_FAILURE);
310  }
311  }
312  if (parser.isSet(QStringLiteral("bind"))) {
313  auto addr = parser.value(QStringLiteral("bind"));
314  // Check consistency
315  if (!srvarg.bind_addr.setAddress(addr)) {
316  qCritical(_("Not a valid IP address: %s"), qUtf8Printable(addr));
317  exit(EXIT_FAILURE);
318  }
319  if (!srvarg.local_addr.isEmpty()) {
320  qCritical(_("Cannot use --bind with --local"));
321  exit(EXIT_FAILURE);
322  }
323  }
324  if (parser.isSet(QStringLiteral("Bind-meta"))) {
325  srvarg.bind_meta_addr = parser.value(QStringLiteral("Bind-meta"));
326  }
327  if (parser.isSet(QStringLiteral("read"))) {
328  srvarg.script_filename = parser.value(QStringLiteral("read"));
329  }
330  if (parser.isSet(QStringLiteral("quitidle"))) {
331  bool conversion_ok;
332  srvarg.quitidle =
333  parser.value(QStringLiteral("quitidle")).toUInt(&conversion_ok);
334  if (!conversion_ok) {
335  qCritical(_("Invalid number %s"),
336  qUtf8Printable(parser.value("quitidle")));
337  exit(EXIT_FAILURE);
338  }
339  }
340  if (parser.isSet(QStringLiteral("exit-on-end"))) {
341  srvarg.exit_on_end = true;
342  }
343  if (parser.isSet(QStringLiteral("timetrack"))) {
344  srvarg.timetrack = true;
345  log_time(QStringLiteral("Time tracking enabled"), true);
346  }
347  if (parser.isSet("Database")) {
348  srvarg.fcdb_enabled = true;
349  srvarg.fcdb_conf = parser.value("Database");
350  }
351  if (parser.isSet("auth")) {
352  srvarg.auth_enabled = true;
353  }
354  if (parser.isSet("Guests")) {
355  srvarg.auth_allow_guests = true;
356  }
357  if (parser.isSet("Newusers")) {
359  }
360  if (parser.isSet(QStringLiteral("Serverid"))) {
361  srvarg.serverid = parser.value(QStringLiteral("Serverid"));
362  }
363  if (parser.isSet(QStringLiteral("saves"))) {
364  srvarg.saves_pathname = parser.value(QStringLiteral("saves"));
365  }
366  if (parser.isSet(QStringLiteral("scenarios"))) {
367  srvarg.scenarios_pathname = parser.value(QStringLiteral("scenarios"));
368  }
369  if (parser.isSet(QStringLiteral("ruleset"))) {
370  srvarg.ruleset = parser.value(QStringLiteral("ruleset"));
371  }
372  if (parser.isSet(QStringLiteral("Announce"))) {
373  auto value = parser.value(QStringLiteral("Announce")).toLower();
374  if (value == QLatin1String("ipv4")) {
376  } else if (value == QLatin1String("ipv6")) {
378  } else if (value == QLatin1String("none")) {
380  } else {
381  qCritical(_("Illegal value \"%s\" for --Announce"),
382  qUtf8Printable(parser.value("Announce")));
383  }
384  }
385  if (parser.isSet(QStringLiteral("warnings"))) {
386  qCWarning(deprecations_category,
387  // TRANS: Do not translate --warnings
388  _("The --warnings option is deprecated."));
389  }
390 #ifdef AI_MODULES
391  if (parser.isSet("LoadAI")) {
392  for (const auto &mod : parser.values("LoadAI")) {
393  if (!load_ai_module(qUtf8Printable(mod)) {
394  fc_fprintf(stderr, _("Failed to load AI module \"%s\"\n"), option);
395  exit(EXIT_FAILURE);
396  }
397  }
398  }
399 #endif
400 
401  con_write(C_VERSION, _("This is the server for %s"),
403  // TRANS: No full stop after the URL, could cause confusion.
404  con_write(C_COMMENT, _("You can learn a lot about Freeciv21 at %s"),
405  WIKI_URL);
407  fc_fprintf(stderr, _("Requested authentication with --auth, "
408  "but no --Database given\n"));
409  exit(EXIT_FAILURE);
410  }
411 
412  // disallow running as root -- too dangerous
413  dont_run_as_root(argv[0], "freeciv21-server");
414 
416 
417  // have arguments, call the main server loop...
418  auto *server = new freeciv::server;
419  if (!server->is_ready()) {
420  delete server;
421  exit(EXIT_FAILURE);
422  }
423  QObject::connect(&app, &QCoreApplication::aboutToQuit, server,
424  &QObject::deleteLater);
425 
426  return app.exec();
427 }
bool load_ai_module(const char *modname)
void init_our_capability()
Setup our internal network capability string.
Definition: capstr.cpp:83
int main(int argc, char *argv[])
Entry point for Freeciv21 server.
Definition: civserver.cpp:114
#define save_and_exit(sig)
Definition: civserver.cpp:51
static void signal_handler(int sig)
This function is called when a SIGINT (ctrl-c) is received.
Definition: civserver.cpp:61
void con_write(enum rfc_status rfc_status, const char *message,...)
Write to console and add line-break, and show prompt if required.
Definition: console.cpp:139
@ C_COMMENT
Definition: console.h:37
@ C_VERSION
Definition: console.h:38
void fc_fprintf(FILE *stream, const char *format,...)
Do a fprintf from the internal charset into the local charset.
Definition: fciconv.cpp:132
#define _(String)
Definition: fcintl.h:50
struct civ_game game
Definition: game.cpp:47
void log_time(const QString &msg, bool log)
Definition: log.cpp:253
bool log_init(const QString &level_str, const QStringList &extra_rules)
Parses a log level string as provided by the user on the command line, and installs the corresponding...
Definition: log.cpp:55
void fc_assert_set_fatal(bool fatal)
Set what signal the assert* macros should raise on failed assertion (-1 to disable).
Definition: log.cpp:226
@ ANNOUNCE_IPV6
Definition: net_types.h:19
@ ANNOUNCE_IPV4
Definition: net_types.h:19
@ ANNOUNCE_NONE
Definition: net_types.h:19
#define ANNOUNCE_DEFAULT
Definition: net_types.h:21
void dont_run_as_root(const char *argv0, const char *fallback)
If we have root privileges, die with an error.
Definition: shared.cpp:948
struct civserver server
Definition: srv_main.cpp:121
struct server_arguments srvarg
Definition: srv_main.cpp:118
void srv_init()
Initialize freeciv server.
Definition: srv_main.cpp:162
struct civ_game::@28::@32 server
struct packet_game_info info
Definition: game.h:80
The base class for options.
Definition: options.cpp:209
bool metaconnection_persistent
Definition: srv_main.h:32
QString log_filename
Definition: srv_main.h:45
bool exit_on_end
Definition: srv_main.h:56
enum announce_type announce
Definition: srv_main.h:64
QString bind_meta_addr
Definition: srv_main.h:43
QString local_addr
Definition: srv_main.h:36
QString script_filename
Definition: srv_main.h:48
bool user_specified_port
Definition: srv_main.h:41
bool auth_allow_guests
Definition: srv_main.h:62
bool fcdb_enabled
Definition: srv_main.h:59
QHostAddress bind_addr
Definition: srv_main.h:38
bool metaserver_no_send
Definition: srv_main.h:30
QString fcdb_conf
Definition: srv_main.h:60
QString saves_pathname
Definition: srv_main.h:49
QString serverid
Definition: srv_main.h:52
QString ruleset
Definition: srv_main.h:51
QString load_filename
Definition: srv_main.h:47
bool auth_enabled
Definition: srv_main.h:61
QString scenarios_pathname
Definition: srv_main.h:50
bool auth_allow_newusers
Definition: srv_main.h:63
QString ranklog_filename
Definition: srv_main.h:46
Definition: servers.h:55
const char * fc_strerror(fc_errno err)
Return a string which describes a given error (errno-style.) The string is converted as necessary fro...
Definition: support.cpp:328
fc_errno fc_get_errno()
Returns last error code.
Definition: support.cpp:311
double timer_read_seconds(civtimer *t)
Read value from timer.
Definition: timing.cpp:137
void timer_start(civtimer *t)
Start timing, adding to previous accumulated time if timer has not been cleared.
Definition: timing.cpp:95
civtimer * timer_renew(civtimer *t, enum timer_timetype type, enum timer_use use)
Allocate a new timer, or reuse t, with specified "type" and "use".
Definition: timing.cpp:51
@ TIMER_ACTIVE
Definition: timing.h:25
@ TIMER_USER
Definition: timing.h:21
const char * freeciv21_version()
Returns the raw version string.
Definition: version.cpp:29
const char * freeciv_name_version()
Return string containing both name of Freeciv21 and version.
Definition: version.cpp:34