PageRenderTime 29ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/CIPHER Game Server Source/monitor.pl

https://gitlab.com/bithinalangot/gameserver
Perl | 245 lines | 144 code | 27 blank | 74 comment | 16 complexity | b98daadfed95369fb2937cec6abfce39 MD5 | raw file
  1. #!/usr/bin/perl -w
  2. # ********************************************************************
  3. #
  4. # This program is free software; you can redistribute it and/or modify it
  5. # under the terms of the GNU General Public License as published by the Free
  6. # Software Foundation; either version 2 of the License, or (at your option)
  7. # any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful, but WITHOUT
  10. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12. # more details.
  13. #
  14. # You should have received a copy of the GNU General Public License along with
  15. # this program; if not, write to the Free Software Foundation, Inc., 59
  16. # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. #
  18. # The full GNU General Public License is included in this distribution in the
  19. # file called LICENSE.
  20. #
  21. # Contact Information:
  22. # Lexi Pimenidis, <i4@pimenidis.org>
  23. #
  24. #*******************************************************************************
  25. #
  26. # CREATED AND DESIGNED BY
  27. # Lexi Pimenidis, <i4@pimenidis.org>
  28. #
  29. #
  30. use strict;
  31. #***********************************************************************
  32. #
  33. # Main Thread of the Game Server
  34. #
  35. # (Note: A former version of the game server trusted all data it received
  36. # from the database! This was resolved, but maybe we missed something)
  37. # (Note for me: read DBI::taint for more informations about this)
  38. #
  39. #***********************************************************************
  40. use common;
  41. use mysql;
  42. common->init();
  43. my $hostname = get_hostname();
  44. #-------------------------------------------------------------- DRONES
  45. sub check_drones {
  46. my @drones = mysql->query('SELECT id,UNIX_TIMESTAMP(heartbeat),host,pid,status FROM drone');
  47. unless(@drones) {
  48. mylog('there are currently no drones connected, according to the database',0,0,0,$log_warning);
  49. return;
  50. }
  51. #mylog('check drones...',0,0,0,$log_operation);
  52. my $stale_drone = time() - get_config(0,'stale_drone',120);
  53. foreach my $drone (@drones) {
  54. my ($did,$heartbeat,$drone_hostname,$pid,$status) = @$drone;
  55. # check if drone is still existing (only on this machine!)
  56. if ($drone_hostname eq $hostname) {
  57. if (kill(0,$pid)) {
  58. # TODO: check if process's name is corelating to 'drone.pl'?
  59. } else {
  60. mylog("drone $did, pid=$pid seems to be gone - removing from DB",0,0,0,$log_error);
  61. mysql->query('UPDATE service_status SET fi_drone=NULL WHERE fi_drone=?',$did);
  62. mysql->query('DELETE FROM drone WHERE id=?',$did);
  63. }
  64. }
  65. # check if drone is hanging
  66. if ($heartbeat < $stale_drone) {
  67. mylog("drone $did\@$drone_hostname, pid=$pid seems to be hanging",0,0,0,$log_warning);
  68. }
  69. }
  70. }
  71. # check if system seems to have too much or too less drones?
  72. sub check_load {
  73. # # simple counting of slots
  74. # my $total_slots = mysql->single_value('SELECT count(*) FROM service_status');
  75. # my $free_slots = mysql->single_value('SELECT count(*) FROM service_status WHERE fi_drone IS NULL');
  76. # mylog("LOAD: $free_slots out of $total_slots slots are free",0,0,0,$log_operation);
  77. # if ($free_slots > 0 ) {
  78. # # TODO: possible underload situation?
  79. # } else {
  80. # # TODO: possible overload situation?
  81. # }
  82. # Better: check time of next event to be done
  83. my @slot = mysql->single_row('SELECT service_status.fi_game, service_status.fi_service, service_status.fi_team, service_status.ip,
  84. UNIX_TIMESTAMP(service_status.last_change)+`game_x_service`.flags_interval AS next
  85. FROM service_status,`game_x_service`
  86. WHERE (service_status.fi_drone IS NULL)AND(service_status.fi_service=`game_x_service`.fi_service)
  87. ORDER BY NEXT
  88. LIMIT 1;');
  89. if (@slot) {
  90. # check delay of work
  91. my $future = $slot[4]-time();
  92. # OK, we're WAY ahead of time
  93. if ($future>=60) {
  94. mylog("LOAD: we're way too bloated - $future seconds until next event",0,0,0,$log_operation);
  95. return;
  96. };
  97. if ($future>=-2) {
  98. mylog("LOAD: we're fine - still $future seconds until next event",0,0,0,$log_verbose);
  99. return;
  100. };
  101. $future=abs($future);
  102. if ($future<=10) {
  103. mylog("LOAD: we're close - just $future seconds behind!",0,0,0,$log_warning);
  104. return;
  105. };
  106. if ($future<=30) {
  107. mylog("LOAD: we're getting late - already $future seconds behind!",0,0,0,$log_warning);
  108. return;
  109. };
  110. mylog("LOAD: SEVERE WARNING - we're $future seconds late!",0,0,0,$log_error);
  111. } else {
  112. mylog("LOAD: can't get data from service_status",0,0,0,$log_warning);
  113. }
  114. }
  115. # check if there are zombie-processes
  116. my $ps_banner_once;
  117. sub check_zombies_with_ps {
  118. my ($ps_bin) = @_;
  119. unless($ps_banner_once) {
  120. mylog("ZOMBIES: check for zombies with help of OS-tool '$ps_bin'",0,0,0,$log_operation);
  121. $ps_banner_once = 1;
  122. }
  123. # get list of processes
  124. unless(open(PS,"$ps_bin ux |")) {
  125. mylog("getting list of processes with '$ps_bin': $!",0,0,0,$log_error);
  126. return;
  127. }
  128. my @lines = <PS>;
  129. close(PS);
  130. if(scalar(@lines)<3) {
  131. mylog("strange result from $ps_bin: not enough lines (only ".scalar(@lines)." lines)",0,0,0,$log_warning);
  132. return;
  133. }
  134. # parse header line
  135. my %name_to_col;
  136. my @header = split(/\s+/,$lines[0]);
  137. foreach(my $i=0;$i<@header;++$i) {
  138. $name_to_col{$header[$i]} = $i;
  139. }
  140. my $stat_column = $name_to_col{'STAT'};
  141. my $user_column = $name_to_col{'USER'};
  142. my $pid_column = $name_to_col{'PID'};
  143. my $cmd_column = $name_to_col{'COMMAND'};
  144. unless(defined $stat_column) {
  145. mylog("could not find column 'STAT' in header line '$lines[0]' from $ps_bin",0,0,0,$log_warning);
  146. return;
  147. }
  148. unless(defined $user_column) {
  149. mylog("could not find column 'USER' in header line '$lines[0]' from $ps_bin",0,0,0,$log_warning);
  150. return;
  151. }
  152. unless(defined $pid_column) {
  153. mylog("could not find column 'PID' in header line '$lines[0]' from $ps_bin",0,0,0,$log_warning);
  154. return;
  155. }
  156. unless(defined $cmd_column) {
  157. mylog("could not find column 'COMMAND' in header line '$lines[0]' from $ps_bin",0,0,0,$log_warning);
  158. return;
  159. }
  160. # parse output to find zombies
  161. foreach(@lines) {
  162. my @cols = split(/\s+/);
  163. if ($cols[$stat_column] eq 'Z') {
  164. # foud a zombie!
  165. my ($user,$pid) = ($cols[$user_column],$cols[$pid_column]);
  166. # make output
  167. splice(@cols,0,$cmd_column);
  168. my $cmd = join(' ',@cols);
  169. # check how we should react
  170. if (get_config(0,'kill_zombies',0)) {
  171. mylog("ZOMBIE: KILLING process $pid from $user = '$cmd'",0,0,0,$log_error);
  172. kill(-9,$pid);
  173. } else {
  174. mylog("ZOMBIE: process $pid from $user = '$cmd'",0,0,0,$log_error);
  175. }
  176. }
  177. }
  178. }
  179. my $ps_bin;
  180. sub check_zombies {
  181. # TODO: check on zombies using /proc-filesystem
  182. if (-d '/proc') {
  183. }
  184. # check with the help of 'ps-binary'
  185. # get ps-binary, if not already found
  186. unless($ps_bin) {
  187. $ps_bin=`which ps`;
  188. unless($ps_bin) {
  189. mylog("can not find 'ps'-binary for zombie detection",0,0,0,$log_error);
  190. $ps_bin='IGNORE';
  191. } else {
  192. chomp($ps_bin);
  193. }
  194. }
  195. check_zombies_with_ps($ps_bin) if($ps_bin ne 'IGNORE');
  196. }
  197. #--------------------------------------------------------------
  198. # exit handler: quit on Ctrl-C
  199. my $quit = 0;
  200. $SIG{INT} = sub { print "quitting..\n"; ++$quit; };
  201. $SIG{HUP} = sub { common->read_config(); };
  202. srand(time());
  203. mylog("Monitor started",0,0,0,$log_critical);
  204. my $clear = `clear`;
  205. # -------------------------- main loop
  206. while(! $quit) {
  207. # check drones
  208. check_drones();
  209. # check if system seems to have too much or too less drones?
  210. check_load();
  211. # check for zombie processes on the system
  212. check_zombies();
  213. # sleep
  214. sleep(1);
  215. };
  216. # terminate
  217. mylog("Finishing Monitor...cleaning up",0,0,0,$log_critical);
  218. common->done();