ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/utils/metaserver.pl.in
Revision: 1.1
Committed: Fri Feb 3 07:14:50 2006 UTC (18 years, 3 months ago) by root
Branch point for: UPSTREAM, MAIN
Log Message:
Initial revision

File Contents

# User Rev Content
1 root 1.1 #!/usr/bin/perl
2     # $Id: metaserver.pl.in,v 1.3 2005/07/08 23:48:59 mwedel Exp $
3    
4     # Copyright 2000 by Mark Wedel.
5     # This script follows the same license as crossfire (GPL).
6    
7     use Socket;
8     use English;
9    
10     # We periodically generate a nice HTML file that people can't put their
11     # web browser at. This is the location of that file.
12     $HTML_FILE="/var/apache/htdocs/metaserver.html";
13    
14     # Cache file to keep data we ahve collected. This is used so that if
15     # the metaserver program crashes/dies, it still has some old data.
16     # You may want to set this to a location that will survive across system
17     # reboots.
18     $CACHE_FILE="/var/tmp/meta_xfire.cache";
19    
20     # We remove a server after $REMOVE_SERVER number of seconds of no updates.
21     # 600 is 10 minutes - if we haven't gotten an update that fast, the server
22     # is almost certainly gone/not available. This reduces congestion when
23     # someone on a dhcp connection keeps running a server and it fills
24     # up a bunch of the slots.
25     $REMOVE_SERVER=600;
26    
27     # UPDATE_SYNC determines how often we update the HTML_FILE and CACHE_FILE.
28     $UPDATE_SYNC=300;
29    
30     # IP_INTERVAL is how often (in seconds) same IP can request metaserver
31     # info. This is to prevent DOS attacks.
32    
33     $IP_INTERVAL=5;
34    
35     # For gathering some anonymous statistics. You probably want to use
36     # MRTG/RRDTOOL for generating statistics from the file.
37     # -- Heikki Hokkanen, 2005-03-26
38     my $STATS_FILE="/var/tmp/meta_xfire.stats";
39     my $stats_updatehits = 0; # COUNTER
40     my $stats_requesthits = 0; # COUNTER
41     my $stats_totalplayers = 0; # GAUGE
42     my $stats_totalservers = 0; # GAUGE
43    
44     socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) ||
45     die("$0: can not open udp socket: $OS_ERROR\n");
46     bind(SOCKET, sockaddr_in(13326, INADDR_ANY)) ||
47     die("$0: Can not bind to socket: $OS_ERROR\n");
48    
49     # Socket1 is used for incoming requests - if we get a connection on this,
50     # we dump out our data to that socket in an easily parsible form.
51     socket(SOCKET1, PF_INET, SOCK_STREAM, getprotobyname("tcp")) ||
52     die("$0: can not open tcp socket: $OS_ERROR\n");
53    
54     # errors on this not that critical
55     setsockopt(SOCKET1, SOL_SOCKET, SO_REUSEADDR, 1);
56    
57     bind(SOCKET1, sockaddr_in(13326, INADDR_ANY)) ||
58     die("$0: Can not bind to socket: $OS_ERROR\n");
59     listen(SOCKET1, 10) ||
60     die("$0: Can not listen on socket: $OS_ERROR\n");
61    
62     vec($rin, fileno(SOCKET), 1)=1;
63     vec($rin, fileno(SOCKET1), 1)=1;
64    
65     if (open(CACHE,"<$CACHE_FILE")) {
66     while (<CACHE>) {
67     chomp;
68     ($ip, $rest) = split /\|/, $_, 2;
69     $data{$ip} = $_;
70     }
71     }
72     close(CACHE);
73    
74     $last_sync=time;
75    
76     while (1) {
77     $nfound=select($rout=$rin, undef, undef, 60);
78     $cur_time=time;
79     if ($nfound) {
80     if (vec($rout, fileno(SOCKET),1)) {
81     $ipaddr = recv(SOCKET, $data, 256, 0) ||
82     print STDERR "$0: error on recv call: $OS_ERROR\n";
83     ($port, $ip) = sockaddr_in($ipaddr);
84     $host = inet_ntoa $ip;
85     ($name, $rest) = split /\|/, $data;
86     if ($name ne "put.your.hostname.here") {
87     $data{$host} = "$host|$cur_time|$data";
88     $stats_updatehits++;
89     }
90     }
91     if (vec($rout, fileno(SOCKET1),1)) {
92     # This is overly basic - if there are enough servers
93     # where the output won't fit in the outgoing socket,
94     # this will block. However, if we fork, we open
95     # ourselves up to someone intentionally designing something
96     # that causes these to block, and then have us fork a huge
97     # number of process potentially filling up our proc table.
98     if ($ipaddr=accept NEWSOCKET, SOCKET1) {
99     ($port, $ip ) = sockaddr_in( $ipaddr );
100     $dq = join('.',unpack('C4', $ip));
101     if ($ip_times{$dq} > time) {
102     close(NEWSOCKET);
103     } else {
104     $ip_times{$dq} = time + $IP_INTERVAL;
105     foreach $i (keys %data) {
106     # Report idle time to client, and not last update
107     # as we received in seconds since epoch.
108     ($ip, $time, $rest) = split /\|/, $data{$i}, 3;
109     $newtime = $cur_time - $time;
110     print NEWSOCKET "$ip|$newtime|$rest\n";
111     }
112     close(NEWSOCKET);
113     $stats_requesthits++;
114     }
115     }
116     }
117     }
118    
119     # Need to generate some files. This is also where we remove outdated
120     # hosts.
121     if ($last_sync+$UPDATE_SYNC < $cur_time) {
122     $last_sync = $cur_time;
123     open(CACHE,">$CACHE_FILE");
124     open(HTML,">$HTML_FILE");
125    
126     print HTML
127     '<title>Crossfire Server List</title>
128     <h1 align=center>Crossfire Server List</h1><p>
129     <table border=1 align=center cellpadding=5>
130     <tr>
131     <th>IP Address</th><th>Last Update Date/Time</th><th>Last Update Minutes Elapsed</th>
132     <th>Hostname</th><th>Number of Players</th><th>Version</th><th>Comment</th>
133     </tr>
134     ';
135    
136     $stats_totalplayers = 0;
137     $stats_totalservers = 0;
138     foreach $i (keys %data) {
139     $stats_totalservers++;
140     ($ip, $time, @rest) = split /\|/, $data{$i};
141     if ($time+$REMOVE_SERVER<$cur_time) {
142     delete $data{$i};
143     } else {
144     print CACHE "$data{$i}\n";
145     $elapsed = int(($cur_time - $time)/60);
146     $gmtime = gmtime($time);
147     print HTML "<tr><td>$i</td><td>$gmtime</td><td>$elapsed</td>";
148     print HTML "<td>$rest[0]</td><td>$rest[1]</td><td>$rest[2]</td><td>$rest[3]</td></tr>\n";
149     $stats_totalplayers += int($rest[2]);
150     }
151     }
152     $gmtime = gmtime($cur_time);
153     print HTML "
154     </table><p>
155     The server name is reported by the server, while the ip address is determined by
156     the incoming data packet. These values may not resolve to the same thing in the
157     case of multi homed hosts or multi ip hosts.<p>
158    
159     All times are in GMT.<p>
160    
161     <font size=-2>Last Updated: $gmtime<font size=+2><p>";
162     close(HTML);
163     close(CACHE);
164    
165     open(STATS,">$STATS_FILE");
166     print STATS "$stats_updatehits:$stats_requesthits:$stats_totalservers:$stats_totalplayers\n";
167     close(STATS);
168    
169     }
170     }