X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=shogi-server;h=adc18bc0fb733db0fa3481ce36d8baa9aa7c3442;hb=b1bcf75aff75fef3f3aef613323d7028a7791a40;hp=07e42a63adbac4653eff2ffc8dd48c614984ff08;hpb=6150d6fd590bf23c1a2e46be19820de0d3ed5857;p=shogi-server%2Fshogi-server.git diff --git a/shogi-server b/shogi-server index 07e42a6..adc18bc 100755 --- a/shogi-server +++ b/shogi-server @@ -1,26 +1,38 @@ #! /usr/bin/env ruby -## $Id$ - -## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch) -## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org) -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -TOP_DIR = File.expand_path(File.dirname(__FILE__)) +# $Id$ +# +# Author:: NABEYA Kenichi, Daigo Moriwaki +# Homepage:: http://sourceforge.jp/projects/shogi-server/ +# +#-- +# Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch) +# Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#++ +# +# + +$topdir = nil +$league = nil +$logger = nil +$config = nil $:.unshift File.dirname(__FILE__) require 'shogi_server' +require 'shogi_server/config' +require 'tempfile' ################################################# # MAIN @@ -58,7 +70,7 @@ OPTIONS --player-log-dir dir log network messages for each player. Log files will be put in the dir. - --floodgate_history + --floodgate-history file name to record Floodgate game history default: './floodgate_history.yaml' @@ -72,6 +84,7 @@ RELEASE REVISION #{ShogiServer::Revision} + EOM end @@ -83,6 +96,9 @@ end def log_message(str) $logger.info(str) end +def log_info(str) + log_message(str) +end def log_warning(str) $logger.warn(str) @@ -93,12 +109,17 @@ def log_error(str) end +# Parse command line options. Return a hash containing the option strings +# where a key is the option name without the first two slashes. For example, +# {"pid-file" => "foo.pid"}. +# def parse_command_line options = Hash::new parser = GetoptLong.new( - ["--daemon", GetoptLong::REQUIRED_ARGUMENT], - ["--pid-file", GetoptLong::REQUIRED_ARGUMENT], - ["--player-log-dir", GetoptLong::REQUIRED_ARGUMENT]) + ["--daemon", GetoptLong::REQUIRED_ARGUMENT], + ["--pid-file", GetoptLong::REQUIRED_ARGUMENT], + ["--player-log-dir", GetoptLong::REQUIRED_ARGUMENT], + ["--floodgate-history", GetoptLong::REQUIRED_ARGUMENT]) parser.quiet = true begin parser.each_option do |name, arg| @@ -112,6 +133,96 @@ def parse_command_line return options end +# Check command line options. +# If any of them is invalid, exit the process. +# +def check_command_line + if (ARGV.length != 2) + usage + exit 2 + end + + if $options["daemon"] + $options["daemon"] = File.expand_path($options["daemon"], File.dirname(__FILE__)) + unless is_writable_dir? $options["daemon"] + usage + $stderr.puts "Can not create a file in the daemon directory: %s" % [$options["daemon"]] + exit 5 + end + end + + $topdir = $options["daemon"] || File.expand_path(File.dirname(__FILE__)) + + if $options["player-log-dir"] + $options["player-log-dir"] = File.expand_path($options["player-log-dir"], $topdir) + unless is_writable_dir?($options["player-log-dir"]) + usage + $stderr.puts "Can not write a file in the player log dir: %s" % [$options["player-log-dir"]] + exit 3 + end + end + + if $options["pid-file"] + $options["pid-file"] = File.expand_path($options["pid-file"], $topdir) + unless is_writable_file? $options["pid-file"] + usage + $stderr.puts "Can not create the pid file: %s" % [$options["pid-file"]] + exit 4 + end + end + + $options["floodgate-history"] ||= File.join($topdir, "floodgate_history.yaml") + $options["floodgate-history"] = File.expand_path($options["floodgate-history"], $topdir) + unless is_writable_file? $options["floodgate-history"] + usage + $stderr.puts "Can not create the floodgate history file: %s" % [$options["floodgate-history"]] + exit 6 + end +end + +# See if the file is writable. The file will be created if it does not exist +# yet. +# Return true if the file is writable, otherwise false. +# +def is_writable_file?(file) + if File.exist?(file) + if FileTest.file?(file) + return FileTest.writable_real?(file) + else + return false + end + end + + begin + open(file, "w") {|fh| } + FileUtils.rm file + rescue + return false + end + + return true +end + +# See if a file can be created in the directory. +# Return true if a file is writable in the directory, otherwise false. +# +def is_writable_dir?(dir) + unless File.directory? dir + return false + end + + result = true + + begin + temp_file = Tempfile.new("dummy-shogi-server", dir) + temp_file.close true + rescue + result = false + end + + return result +end + def write_pid_file(file) open(file, "w") do |fh| fh.puts "#{$$}" @@ -142,14 +253,14 @@ def login_loop(client) while r = select([client], nil, nil, ShogiServer::Login_Time) do break unless str = r[0].first.gets - $mutex.lock # guards LEAGUE + $mutex.lock # guards $league begin str =~ /([\r\n]*)$/ eol = $1 if (ShogiServer::Login::good_login?(str)) player = ShogiServer::Player::new(str, client, eol) login = ShogiServer::Login::factory(str, player) - if (current_player = LEAGUE.find(player.name)) + if (current_player = $league.find(player.name)) if (current_player.password == player.password && current_player.status != "game") log_message(sprintf("user %s login forcely", player.name)) @@ -160,7 +271,7 @@ def login_loop(client) break end end - LEAGUE.add(player) + $league.add(player) break else client.write("LOGIN:incorrect" + eol) @@ -174,7 +285,7 @@ def login_loop(client) end def setup_logger(log_file) - logger = Logger.new(log_file, 'daily') + logger = ShogiServer::Logger.new(log_file, 'daily') logger.formatter = ShogiServer::Formatter.new logger.level = $DEBUG ? Logger::DEBUG : Logger::INFO logger.datetime_format = "%Y-%m-%d %H:%M:%S" @@ -192,7 +303,7 @@ end def setup_floodgate return Thread.start do Thread.pass - floodgate = ShogiServer::League::Floodgate.new(LEAGUE) + floodgate = ShogiServer::League::Floodgate.new($league) log_message("Flooddgate reloaded. The next match will start at %s." % [floodgate.next_time]) @@ -203,7 +314,7 @@ def setup_floodgate sleep(diff/2) next end - LEAGUE.reload + $league.reload floodgate.match_game floodgate.charge next_time = floodgate.next_time @@ -211,8 +322,8 @@ def setup_floodgate log_message("Reloading source...") ShogiServer.reload end - floodgate = ShogiServer::League::Floodgate.new(LEAGUE, next_time) - log_message("Floodgate will start the next match at %s." % + floodgate = ShogiServer::League::Floodgate.new($league, next_time) + log_message("Floodgate: The next match will start at %s." % [floodgate.next_time]) rescue Exception => ex # ignore errors @@ -225,37 +336,18 @@ end def main $options = parse_command_line - if (ARGV.length != 2) - usage - exit 2 - end - if $options["player-log-dir"] - $options["player-log-dir"] = File.expand_path($options["player-log-dir"]) - end - if $options["player-log-dir"] && - !File.directory?($options["player-log-dir"]) - usage - exit 3 - end - if $options["pid-file"] - $options["pid-file"] = File.expand_path($options["pid-file"]) - end - $options["floodgate-history"] ||= File.join(File.dirname(__FILE__), "floodgate_history.yaml") - $options["floodgate-history"] = File.expand_path($options["floodgate-history"]) + check_command_line + $config = ShogiServer::Config.new $options - LEAGUE.event = ARGV.shift - port = ARGV.shift + $league = ShogiServer::League.new($topdir) - dir = $options["daemon"] - dir = File.expand_path(dir) if dir - if dir && ! File.exist?(dir) - FileUtils.mkdir(dir) - end + $league.event = ARGV.shift + port = ARGV.shift - log_file = dir ? File.join(dir, "shogi-server.log") : STDOUT + log_file = $options["daemon"] ? File.join($options["daemon"], "shogi-server.log") : STDOUT $logger = setup_logger(log_file) - LEAGUE.dir = dir || TOP_DIR + $league.dir = $topdir config = {} config[:Port] = port @@ -270,7 +362,7 @@ def main write_pid_file($options["pid-file"]) end setup_watchdog_for_giant_lock - LEAGUE.setup_players_database + $league.setup_players_database fg_thread = setup_floodgate end @@ -280,6 +372,7 @@ def main end end + srand server = WEBrick::GenericServer.new(config) ["INT", "TERM"].each do |signal| trap(signal) do @@ -310,7 +403,7 @@ def main player.game.kill(player) end player.finish # socket has been closed - LEAGUE.delete(player) + $league.delete(player) log_message(sprintf("user %s logout", player.name)) ensure $mutex.unlock @@ -326,9 +419,12 @@ if ($0 == __FILE__) Thread.abort_on_exception = $DEBUG ? true : false begin - LEAGUE = ShogiServer::League.new(TOP_DIR) main rescue Exception => ex - log_error("main: #{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}") + if $logger + log_error("main: #{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}") + else + $stderr.puts "main: #{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" + end end end