示例#1
0
/**************************************************************** 
forks a server if it can. returns FALSE is we find we couldn't start
the server.
*****************************************************************/ 
bool client_start_server(void)
{
#if !defined(HAVE_WORKING_FORK) && !defined(WIN32_NATIVE)
  /* Can't do much without fork */
  return FALSE;
#else /* HAVE_WORKING_FORK || WIN32_NATIVE */
  char buf[512];
  int connect_tries = 0;
# ifdef WIN32_NATIVE
  STARTUPINFO si;
  PROCESS_INFORMATION pi;

  char savesdir[MAX_LEN_PATH];
  char scensdir[MAX_LEN_PATH];
  char options[512];
  char cmdline1[512];
  char cmdline2[512];
  char cmdline3[512];
  char cmdline4[512];
  char logcmdline[512];
  char scriptcmdline[512];
  char savescmdline[512];
  char scenscmdline[512];
# endif /* WIN32_NATIVE */

#ifdef IPV6_SUPPORT
  /* We want port that is free in IPv4 even if we (the client) have
   * IPv6 support. In the unlikely case that local server is IPv4-only
   * (meaning that it has to be from different build than client) we
   * have to give port that it can use. IPv6-enabled client would first
   * try same port in IPv6 and if that fails, fallback to IPv4 too. */
  enum fc_addr_family family = FC_ADDR_IPV4;
#else
  enum fc_addr_family family = FC_ADDR_IPV4;
#endif /* IPV6_SUPPORT */

  /* only one server (forked from this client) shall be running at a time */
  /* This also resets client_has_hack. */
  client_kill_server(TRUE);

  output_window_append(ftc_client, _("Starting server..."));

  /* find a free port */
  internal_server_port = find_next_free_port(DEFAULT_SOCK_PORT, family);

# ifdef HAVE_WORKING_FORK
  server_pid = fork();
  
  if (server_pid == 0) {
    int fd, argc = 0;
    const int max_nargs = 18;
    char *argv[max_nargs + 1], port_buf[32];

    /* inside the child */

    /* Set up the command-line parameters. */
    fc_snprintf(port_buf, sizeof(port_buf), "%d", internal_server_port);
    argv[argc++] = "freeciv-server";
    argv[argc++] = "-p";
    argv[argc++] = port_buf;
    argv[argc++] = "--bind";
    argv[argc++] = "localhost";
    argv[argc++] = "-q";
    argv[argc++] = "1";
    argv[argc++] = "-e";
    argv[argc++] = "--saves";
    argv[argc++] = "~/.freeciv/saves";
    argv[argc++] = "--scenarios";
    argv[argc++] = "~/.freeciv/scenarios";
    if (logfile) {
      argv[argc++] = "--debug";
      argv[argc++] = "3";
      argv[argc++] = "--log";
      argv[argc++] = logfile;
    }
    if (scriptfile) {
      argv[argc++] = "--read";
      argv[argc++] = scriptfile;
    }
    argv[argc] = NULL;
    fc_assert(argc <= max_nargs);

    /* avoid terminal spam, but still make server output available */ 
    fclose(stdout);
    fclose(stderr);

    /* FIXME: include the port to avoid duplication? */
    if (logfile) {
      fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0644);

      if (fd != 1) {
        dup2(fd, 1);
      }
      if (fd != 2) {
        dup2(fd, 2);
      }
      fchmod(1, 0644);
    }

    /* If it's still attatched to our terminal, things get messed up, 
      but freeciv-server needs *something* */ 
    fclose(stdin);
    fd = open("/dev/null", O_RDONLY);
    if (fd != 0) {
      dup2(fd, 0);
    }

    /* these won't return on success */
#ifdef DEBUG
    /* Search under current directory (what ever that happens to be)
     * only in debug builds. This allows running freeciv directly from build
     * tree, but could be considered security risk in release builds. */
    execvp("./fcser", argv);
    execvp("./server/freeciv-server", argv);
#endif /* DEBUG */
    execvp(BINDIR "/freeciv-server", argv);
    execvp("freeciv-server", argv);

    /* This line is only reached if freeciv-server cannot be started, 
     * so we kill the forked process.
     * Calling exit here is dangerous due to X11 problems (async replies) */ 
    _exit(1);
  } 
# else /* HAVE_WORKING_FORK */
#  ifdef WIN32_NATIVE
  if (logfile) {
    loghandle = CreateFile(logfile, GENERIC_WRITE,
                           FILE_SHARE_READ | FILE_SHARE_WRITE,
                           NULL,
			   OPEN_ALWAYS, 0, NULL);
  }

  ZeroMemory(&si, sizeof(si));
  si.cb = sizeof(si);
  si.hStdOutput = loghandle;
  si.hStdInput = INVALID_HANDLE_VALUE;
  si.hStdError = loghandle;
  si.dwFlags = STARTF_USESTDHANDLES;

  /* Set up the command-line parameters. */
  logcmdline[0] = 0;
  scriptcmdline[0] = 0;

  /* the server expects command line arguments to be in local encoding */ 
  if (logfile) {
    char *logfile_in_local_encoding =
        internal_to_local_string_malloc(logfile);

    fc_snprintf(logcmdline, sizeof(logcmdline), " --debug 3 --log %s",
                logfile_in_local_encoding);
    free(logfile_in_local_encoding);
  }
  if (scriptfile) {
    char *scriptfile_in_local_encoding =
        internal_to_local_string_malloc(scriptfile);

    fc_snprintf(scriptcmdline, sizeof(scriptcmdline),  " --read %s",
                scriptfile_in_local_encoding);
    free(scriptfile_in_local_encoding);
  }

  interpret_tilde(savesdir, sizeof(savesdir), "~/.freeciv/saves");
  internal_to_local_string_buffer(savesdir, savescmdline, sizeof(savescmdline));

  interpret_tilde(scensdir, sizeof(scensdir), "~/.freeciv/scenarios");
  internal_to_local_string_buffer(scensdir, scenscmdline, sizeof(scenscmdline));

  fc_snprintf(options, sizeof(options),
              "-p %d --bind localhost -q 1 -e%s%s --saves \"%s\" "
              "--scenarios \"%s\"",
              internal_server_port, logcmdline, scriptcmdline, savescmdline,
              scenscmdline);
  fc_snprintf(cmdline1, sizeof(cmdline1), "./fcser %s", options);
  fc_snprintf(cmdline2, sizeof(cmdline2),
              "./server/freeciv-server %s", options);
  fc_snprintf(cmdline3, sizeof(cmdline3),
              BINDIR "/freeciv-server %s", options);
  fc_snprintf(cmdline4, sizeof(cmdline4),
              "freeciv-server %s", options);

  if (
#ifdef DEBUG
      !CreateProcess(NULL, cmdline1, NULL, NULL, TRUE,
                     DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
                     NULL, NULL, &si, &pi)
      && !CreateProcess(NULL, cmdline2, NULL, NULL, TRUE,
                        DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
                        NULL, NULL, &si, &pi)
      &&
#endif /* DEBUG */
      !CreateProcess(NULL, cmdline3, NULL, NULL, TRUE,
                     DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
                     NULL, NULL, &si, &pi)
      && !CreateProcess(NULL, cmdline4, NULL, NULL, TRUE,
                        DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
                        NULL, NULL, &si, &pi)) {
    output_window_append(ftc_client, _("Couldn't start the server."));
    output_window_append(ftc_client,
                         _("You'll have to start one manually. Sorry..."));
    return FALSE;
  }

  server_process = pi.hProcess;

#  endif /* WIN32_NATIVE */
# endif /* HAVE_WORKING_FORK */
 
  /* a reasonable number of tries */ 
  while (connect_to_server(user_name, "localhost", internal_server_port, 
                           buf, sizeof(buf)) == -1) {
    fc_usleep(WAIT_BETWEEN_TRIES);
#ifdef HAVE_WORKING_FORK
#ifndef WIN32_NATIVE
    if (waitpid(server_pid, NULL, WNOHANG) != 0) {
      break;
    }
#endif /* WIN32_NATIVE */
#endif /* HAVE_WORKING_FORK */
    if (connect_tries++ > NUMBER_OF_TRIES) {
      break;
    }
  }

  /* weird, but could happen, if server doesn't support new startup stuff
   * capabilities won't help us here... */ 
  if (!client.conn.used) {
    /* possible that server is still running. kill it */ 
    client_kill_server(TRUE);

    output_window_append(ftc_client, _("Couldn't connect to the server."));
    output_window_append(ftc_client,
                         _("We probably couldn't start it from here."));
    output_window_append(ftc_client,
                         _("You'll have to start one manually. Sorry..."));
    return FALSE;
  }

  /* We set the topology to match the view.
   *
   * When a typical player launches a game, he wants the map orientation to
   * match the tileset orientation.  So if you use an isometric tileset you
   * get an iso-map and for a classic tileset you get a classic map.  In
   * both cases the map wraps in the X direction by default.
   *
   * This works with hex maps too now.  A hex map always has
   * tileset_is_isometric(tileset) return TRUE.  An iso-hex map has
   * tileset_hex_height(tileset) != 0, while a non-iso hex map
   * has tileset_hex_width(tileset) != 0.
   *
   * Setting the option here is a bit of a hack, but so long as the client
   * has sufficient permissions to do so (it doesn't have HACK access yet) it
   * is safe enough.  Note that if you load a savegame the topology will be
   * set but then overwritten during the load.
   *
   * Don't send it now, it will be sent to the server when receiving the
   * server setting infos. */
  {
    char buf[16];

    fc_strlcpy(buf, "WRAPX", sizeof(buf));
    if (tileset_is_isometric(tileset) && 0 == tileset_hex_height(tileset)) {
      fc_strlcat(buf, "|ISO", sizeof(buf));
    }
    if (0 < tileset_hex_width(tileset) || 0 < tileset_hex_height(tileset)) {
      fc_strlcat(buf, "|HEX", sizeof(buf));
    }
    desired_settable_option_update("topology", buf, FALSE);
  }

  return TRUE;
#endif /* HAVE_WORKING_FORK || WIN32_NATIVE */
}
示例#2
0
/**************************************************************** 
forks a server if it can. returns FALSE is we find we couldn't start
the server.
This is so system-intensive that it's *nix only.  VMS and Windows 
code will come later 
*****************************************************************/ 
bool client_start_server(void)
{
#if !defined(HAVE_WORKING_FORK) && !defined(WIN32_NATIVE)
  /* Can't do much without fork */
  return FALSE;
#else /* HAVE_WORKING_FORK || WIN32_NATIVE */
  char buf[512];
  int connect_tries = 0;
# ifdef WIN32_NATIVE
  STARTUPINFO si;
  PROCESS_INFORMATION pi;

  char savesdir[MAX_LEN_PATH];
  char options[512];
  char cmdline1[512];
  char cmdline2[512];
  char cmdline3[512];
  char logcmdline[512];
  char scriptcmdline[512];
# endif /* WIN32_NATIVE */

  /* only one server (forked from this client) shall be running at a time */
  /* This also resets client_has_hack. */
  client_kill_server(TRUE);
  
  output_window_append(FTC_CLIENT_INFO, NULL, _("Starting server..."));

  /* find a free port */ 
  internal_server_port = find_next_free_port(DEFAULT_SOCK_PORT);

# ifdef HAVE_WORKING_FORK
  server_pid = fork();
  
  if (server_pid == 0) {
    int fd, argc = 0;
    const int max_nargs = 13;
    char *argv[max_nargs + 1], port_buf[32];

    /* inside the child */

    /* Set up the command-line parameters. */
    my_snprintf(port_buf, sizeof(port_buf), "%d", internal_server_port);
    argv[argc++] = "civserver";
    argv[argc++] = "-p";
    argv[argc++] = port_buf;
    argv[argc++] = "-q";
    argv[argc++] = "1";
    argv[argc++] = "-e";
    argv[argc++] = "--saves";
    argv[argc++] = "~/.freeciv/saves";
    argv[argc++] = "--scenarios";
    argv[argc++] = "~/.freeciv/scenarios";
    if (logfile) {
      argv[argc++] = "--debug";
      argv[argc++] = "3";
      argv[argc++] = "--log";
      argv[argc++] = logfile;
    }
    if (scriptfile) {
      argv[argc++] = "--read";
      argv[argc++] = scriptfile;
    }
    argv[argc] = NULL;
    assert(argc <= max_nargs);

    /* avoid terminal spam, but still make server output available */ 
    fclose(stdout);
    fclose(stderr);

    /* FIXME: include the port to avoid duplication? */
    if (logfile) {
      fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0644);

      if (fd != 1) {
        dup2(fd, 1);
      }
      if (fd != 2) {
        dup2(fd, 2);
      }
      fchmod(1, 0644);
    }

    /* If it's still attatched to our terminal, things get messed up, 
      but civserver needs *something* */ 
    fclose(stdin);
    fd = open("/dev/null", O_RDONLY);
    if (fd != 0) {
      dup2(fd, 0);
    }

    /* these won't return on success */ 
    execvp("./ser", argv);
    execvp("./server/civserver", argv);
    execvp("civserver", argv);
    
    /* This line is only reached if civserver cannot be started, 
     * so we kill the forked process.
     * Calling exit here is dangerous due to X11 problems (async replies) */ 
    _exit(1);
  } 
# else /* HAVE_WORKING_FORK */
#  ifdef WIN32_NATIVE
  if (logfile) {
    loghandle = CreateFile(logfile, GENERIC_WRITE,
                           FILE_SHARE_READ | FILE_SHARE_WRITE,
                           NULL,
			   OPEN_ALWAYS, 0, NULL);
  }

  ZeroMemory(&si, sizeof(si));
  si.cb = sizeof(si);
  si.hStdOutput = loghandle;
  si.hStdInput = INVALID_HANDLE_VALUE;
  si.hStdError = loghandle;
  si.dwFlags = STARTF_USESTDHANDLES;

  /* Set up the command-line parameters. */
  logcmdline[0] = 0;
  scriptcmdline[0] = 0;

  if (logfile) {
    my_snprintf(logcmdline, sizeof(logcmdline), " --debug 3 --log %s",
		logfile);
  }
  if (scriptfile) {
    my_snprintf(scriptcmdline, sizeof(scriptcmdline),  " --read %s",
		scriptfile);
  }

  interpret_tilde(savesdir, sizeof(savesdir), "~/.freeciv/saves");

  my_snprintf(options, sizeof(options), "-p %d -q 1 -e%s%s --saves \"%s\"",
	      internal_server_port, logcmdline, scriptcmdline, savesdir);
  my_snprintf(cmdline1, sizeof(cmdline1), "./ser %s", options);
  my_snprintf(cmdline2, sizeof(cmdline2), "./server/civserver %s", options);
  my_snprintf(cmdline3, sizeof(cmdline3), "civserver %s", options);

  if (!CreateProcess(NULL, cmdline1, NULL, NULL, TRUE,
		     DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
		     NULL, NULL, &si, &pi) 
      && !CreateProcess(NULL, cmdline2, NULL, NULL, TRUE,
			DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
			NULL, NULL, &si, &pi) 
      && !CreateProcess(NULL, cmdline3, NULL, NULL, TRUE,
			DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
			NULL, NULL, &si, &pi)) {
    output_window_append(FTC_CLIENT_INFO, NULL,
                         _("Couldn't start the server."));
    output_window_append(FTC_CLIENT_INFO, NULL,
                         _("You'll have to start one manually. Sorry..."));
    return FALSE;
  }
  
  server_process = pi.hProcess;

#  endif /* WIN32_NATIVE */
# endif /* HAVE_WORKING_FORK */
 
  /* a reasonable number of tries */ 
  while (connect_to_server(user_name, "localhost", internal_server_port, 
                           buf, sizeof(buf)) == -1) {
    myusleep(WAIT_BETWEEN_TRIES);
#ifdef HAVE_WORKING_FORK
#ifndef WIN32_NATIVE
    if (waitpid(server_pid, NULL, WNOHANG) != 0) {
      break;
    }
#endif /* WIN32_NATIVE */
#endif /* HAVE_WORKING_FORK */
    if (connect_tries++ > NUMBER_OF_TRIES) {
      break;
    }
  }

  /* weird, but could happen, if server doesn't support new startup stuff
   * capabilities won't help us here... */ 
  if (!client.conn.used) {
    /* possible that server is still running. kill it */ 
    client_kill_server(TRUE);

    output_window_append(FTC_CLIENT_INFO, NULL,
                         _("Couldn't connect to the server."));
    output_window_append(FTC_CLIENT_INFO, NULL,
                         _("We probably couldn't start it from here."));
    output_window_append(FTC_CLIENT_INFO, NULL,
                         _("You'll have to start one manually. Sorry..."));
    return FALSE;
  }

  /* We set the topology to match the view.
   *
   * When a typical player launches a game, he wants the map orientation to
   * match the tileset orientation.  So if you use an isometric tileset you
   * get an iso-map and for a classic tileset you get a classic map.  In
   * both cases the map wraps in the X direction by default.
   *
   * This works with hex maps too now.  A hex map always has
   * tileset_is_isometric(tileset) return TRUE.  An iso-hex map has
   * tileset_hex_height(tileset) != 0, while a non-iso hex map
   * has tileset_hex_width(tileset) != 0.
   *
   * Setting the option here is a bit of a hack, but so long as the client
   * has sufficient permissions to do so (it doesn't have HACK access yet) it
   * is safe enough.  Note that if you load a savegame the topology will be
   * set but then overwritten during the load. */
  send_chat_printf("/set topology %d",
                   (TF_WRAPX
                    | ((tileset_is_isometric(tileset)
                        && tileset_hex_height(tileset) == 0) ? TF_ISO : 0)
                    | ((tileset_hex_width(tileset) != 0
                        || tileset_hex_height(tileset) != 0) ? TF_HEX : 0)));

  return TRUE;
#endif /* HAVE_WORKING_FORK || WIN32_NATIVE */
}