Guest User

ogp_agent.pl

a guest
Oct 25th, 2020
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 50.61 KB | None | 0 0
  1. #!/usr/bin/perl
  2. #
  3. # OGP - Open Game Panel
  4. # Copyright (C) 2008 - 2018 The OGP Development Team
  5. #
  6. # http://www.opengamepanel.org/
  7. #
  8. # This program is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License
  10. # as published by the Free Software Foundation; either version 2
  11. # of the License, or any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. #
  22.  
  23. use warnings;
  24. use strict;
  25.  
  26. use Cwd; # Fast way to get the current directory
  27. use lib getcwd();
  28. use Frontier::Daemon::OGP::Forking; # Forking XML-RPC server
  29. use File::Copy; # Simple file copy functions
  30. use File::Copy::Recursive
  31. qw(fcopy rcopy dircopy fmove rmove dirmove pathempty pathrmdir)
  32. ; # Used to copy whole directories
  33. use File::Basename; # Used to get the file name or the directory name from a given path
  34. use Crypt::XXTEA; # Encryption between webpages and agent.
  35. use Cfg::Config; # Config file
  36. use Cfg::Preferences; # Preferences file
  37. use Fcntl ':flock'; # Import LOCK_* constants for file locking
  38. use LWP::UserAgent; # Used for fetching URLs
  39. use MIME::Base64; # Used to ensure data travelling right through the network.
  40. use Getopt::Long; # Used for command line params.
  41. use Path::Class::File; # Used to handle files and directories.
  42. use File::Path qw(mkpath);
  43. use Archive::Extract; # Used to handle archived files.
  44. use File::Find;
  45. use Schedule::Cron; # Used for scheduling tasks
  46.  
  47. # Compression tools
  48. use IO::Compress::Bzip2 qw(bzip2 $Bzip2Error); # Used to compress files to bz2.
  49. use Compress::Zlib; # Used to compress file download buffers to zlib.
  50. use Archive::Tar; # Used to create tar, tgz or tbz archives.
  51. use Archive::Zip qw( :ERROR_CODES :CONSTANTS ); # Used to create zip archives.
  52.  
  53. # Current location of the agent.
  54. use constant AGENT_RUN_DIR => getcwd();
  55.  
  56. # Load our config file values
  57. use constant AGENT_KEY => $Cfg::Config{key};
  58. use constant AGENT_IP => $Cfg::Config{listen_ip};
  59. use constant AGENT_LOG_FILE => $Cfg::Config{logfile};
  60. use constant AGENT_PORT => $Cfg::Config{listen_port};
  61. use constant AGENT_VERSION => $Cfg::Config{version};
  62. use constant WEB_ADMIN_API_KEY => $Cfg::Config{web_admin_api_key};
  63. use constant WEB_API_URL => $Cfg::Config{web_api_url};
  64. use constant STEAM_DL_LIMIT => $Cfg::Config{steam_dl_limit};
  65. use constant SCREEN_LOG_LOCAL => $Cfg::Preferences{screen_log_local};
  66. use constant DELETE_LOGS_AFTER => $Cfg::Preferences{delete_logs_after};
  67. use constant AGENT_PID_FILE =>
  68. Path::Class::File->new(AGENT_RUN_DIR, 'ogp_agent.pid');
  69. use constant AGENT_RSYNC_GENERIC_LOG =>
  70. Path::Class::File->new(AGENT_RUN_DIR, 'rsync_update_generic.log');
  71. use constant STEAM_LICENSE_OK => "Accept";
  72. use constant STEAM_LICENSE => $Cfg::Config{steam_license};
  73. use constant MANUAL_TMP_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'tmp');
  74. use constant SHARED_GAME_TMP_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'shared');
  75. use constant STEAMCMD_CLIENT_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'steamcmd');
  76. use constant STEAMCMD_CLIENT_BIN =>
  77. Path::Class::File->new(STEAMCMD_CLIENT_DIR, 'steamcmd.sh');
  78. use constant SCREEN_LOGS_DIR =>
  79. Path::Class::Dir->new(AGENT_RUN_DIR, 'screenlogs');
  80. use constant GAME_STARTUP_DIR =>
  81. Path::Class::Dir->new(AGENT_RUN_DIR, 'startups');
  82. use constant SCREENRC_FILE =>
  83. Path::Class::File->new(AGENT_RUN_DIR, 'ogp_screenrc');
  84. use constant SCREENRC_FILE_BK =>
  85. Path::Class::File->new(AGENT_RUN_DIR, 'ogp_screenrc_bk');
  86. use constant SCREENRC_TMP_FILE =>
  87. Path::Class::File->new(AGENT_RUN_DIR, 'ogp_screenrc.tmp');
  88. use constant SCREEN_TYPE_HOME => "HOME";
  89. use constant SCREEN_TYPE_UPDATE => "UPDATE";
  90. use constant FD_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'FastDownload');
  91. use constant FD_ALIASES_DIR => Path::Class::Dir->new(FD_DIR, 'aliases');
  92. use constant FD_PID_FILE => Path::Class::File->new(FD_DIR, 'fd.pid');
  93. use constant SCHED_PID => Path::Class::File->new(AGENT_RUN_DIR, 'scheduler.pid');
  94. use constant SCHED_TASKS => Path::Class::File->new(AGENT_RUN_DIR, 'scheduler.tasks');
  95. use constant SCHED_LOG_FILE => Path::Class::File->new(AGENT_RUN_DIR, 'scheduler.log');
  96.  
  97. $Cfg::Config{sudo_password} =~ s/('+)/'\"$1\"'/g;
  98. our $SUDOPASSWD = $Cfg::Config{sudo_password};
  99. my $no_startups = 0;
  100. my $clear_startups = 0;
  101. our $log_std_out = 0;
  102.  
  103. GetOptions(
  104. 'no-startups' => \$no_startups,
  105. 'clear-startups' => \$clear_startups,
  106. 'log-stdout' => \$log_std_out
  107. );
  108.  
  109. # Starting the agent as root user is not supported anymore.
  110. if ($< == 0)
  111. {
  112. print "ERROR: You are trying to start the agent as root user.";
  113. print "This is not currently supported. If you wish to start the";
  114. print "you need to create a normal user account for it.";
  115. exit 1;
  116. }
  117.  
  118. ### Logger function.
  119. ### @param line the line that is put to the log file.
  120. sub logger
  121. {
  122. my $logcmd = $_[0];
  123. my $also_print = 0;
  124.  
  125. if (@_ == 2)
  126. {
  127. ($also_print) = $_[1];
  128. }
  129.  
  130. $logcmd = localtime() . " $logcmd\n";
  131.  
  132. if ($log_std_out == 1)
  133. {
  134. print "$logcmd";
  135. return;
  136. }
  137. if ($also_print == 1)
  138. {
  139. print "$logcmd";
  140. }
  141.  
  142. open(LOGFILE, '>>', AGENT_LOG_FILE)
  143. or die("Can't open " . AGENT_LOG_FILE . " - $!");
  144. flock(LOGFILE, LOCK_EX) or die("Failed to lock log file.");
  145. seek(LOGFILE, 0, 2) or die("Failed to seek to end of file.");
  146. print LOGFILE "$logcmd" or die("Failed to write to log file.");
  147. flock(LOGFILE, LOCK_UN) or die("Failed to unlock log file.");
  148. close(LOGFILE) or die("Failed to close log file.");
  149. }
  150.  
  151. # Rotate the log file
  152. if (-e AGENT_LOG_FILE)
  153. {
  154. if (-e AGENT_LOG_FILE . ".bak")
  155. {
  156. unlink(AGENT_LOG_FILE . ".bak");
  157. }
  158. logger "Rotating log file";
  159. move(AGENT_LOG_FILE, AGENT_LOG_FILE . ".bak");
  160. logger "New log file created";
  161. }
  162.  
  163. # If for some reason the screenrc file doesn't exist, restore it from the backup copy
  164. # I've seen this happen a few times
  165. if (! -e SCREENRC_FILE)
  166. {
  167. copy(SCREENRC_FILE_BK,SCREENRC_FILE);
  168. }
  169.  
  170. open INPUTFILE, "<", SCREENRC_FILE or die $!;
  171. open OUTPUTFILE, ">", SCREENRC_TMP_FILE or die $!;
  172. my $dest = SCREEN_LOGS_DIR . "/screenlog.%t";
  173. while (<INPUTFILE>)
  174. {
  175. $_ =~ s/logfile.*/logfile $dest/g;
  176. print OUTPUTFILE $_;
  177. }
  178. close INPUTFILE;
  179. close OUTPUTFILE;
  180. unlink SCREENRC_FILE;
  181. move(SCREENRC_TMP_FILE,SCREENRC_FILE);
  182.  
  183. # Check the screen logs folder
  184. if (!-d SCREEN_LOGS_DIR && !mkdir SCREEN_LOGS_DIR)
  185. {
  186. logger "Could not create " . SCREEN_LOGS_DIR . " directory $!.", 1;
  187. exit -1;
  188. }
  189.  
  190. # Check the global shared games folder
  191. if (!-d SHARED_GAME_TMP_DIR && !mkdir SHARED_GAME_TMP_DIR)
  192. {
  193. logger "Could not create " . SHARED_GAME_TMP_DIR . " directory $!.", 1;
  194. exit -1;
  195. }
  196.  
  197. if (check_steam_cmd_client() == -1)
  198. {
  199. print "ERROR: You must download and uncompress the new steamcmd package.";
  200. print "BE SURE TO INSTALL IT IN " . AGENT_RUN_DIR . "/steamcmd directory,";
  201. print "so it can be managed by the agent to install servers.";
  202. exit 1;
  203. }
  204.  
  205. # create the directory for startup flags
  206. if (!-e GAME_STARTUP_DIR)
  207. {
  208. logger "Creating the startups directory " . GAME_STARTUP_DIR . "";
  209. if (!mkdir GAME_STARTUP_DIR)
  210. {
  211. my $message =
  212. "Failed to create the "
  213. . GAME_STARTUP_DIR
  214. . " directory - check permissions. Errno: $!";
  215. logger $message, 1;
  216. exit 1;
  217. }
  218. }
  219. elsif ($clear_startups)
  220. {
  221. opendir(STARTUPDIR, GAME_STARTUP_DIR);
  222. while (my $startup_file = readdir(STARTUPDIR))
  223. {
  224.  
  225. # Skip . and ..
  226. next if $startup_file =~ /^\./;
  227. $startup_file = Path::Class::File->new(GAME_STARTUP_DIR, $startup_file);
  228. logger "Removing " . $startup_file . ".";
  229. unlink($startup_file);
  230. }
  231. closedir(STARTUPDIR);
  232. }
  233. # If the directory already existed check if we need to start some games.
  234. elsif ($no_startups != 1)
  235. {
  236.  
  237. # Loop through all the startup flags, and call universal startup
  238. opendir(STARTUPDIR, GAME_STARTUP_DIR);
  239. logger "Reading startup flags from " . GAME_STARTUP_DIR . "";
  240. while (my $dirlist = readdir(STARTUPDIR))
  241. {
  242.  
  243. # Skip . and ..
  244. next if $dirlist =~ /^\./;
  245. logger "Found $dirlist";
  246. open(STARTFILE, '<', Path::Class::Dir->new(GAME_STARTUP_DIR, $dirlist))
  247. || logger "Error opening start flag $!";
  248. while (<STARTFILE>)
  249. {
  250. my (
  251. $home_id, $home_path, $server_exe,
  252. $run_dir, $startup_cmd, $server_port,
  253. $server_ip, $cpu, $nice, $preStart, $envVars, $game_key, $console_log
  254. ) = split(',', $_);
  255.  
  256. if (is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) ==
  257. 1)
  258. {
  259. logger
  260. "This server ($server_exe on $server_ip : $server_port) is already running (ID: $home_id).";
  261. next;
  262. }
  263.  
  264. logger "Starting server_exe $server_exe from home $home_path.";
  265. universal_start_without_decrypt(
  266. $home_id, $home_path, $server_exe,
  267. $run_dir, $startup_cmd, $server_port,
  268. $server_ip, $cpu, $nice, $preStart, $envVars, $game_key, $console_log
  269. );
  270. }
  271. close(STARTFILE);
  272. }
  273. closedir(STARTUPDIR);
  274. }
  275.  
  276. # Create the pid file
  277. open(PID, '>', AGENT_PID_FILE)
  278. or die("Can't write to pid file - " . AGENT_PID_FILE . "\n");
  279. print PID "$$\n";
  280. close(PID);
  281.  
  282. logger "Open Game Panel - Agent started - "
  283. . AGENT_VERSION
  284. . " - port "
  285. . AGENT_PORT
  286. . " - PID $$", 1;
  287.  
  288. # Stop previous scheduler process if exists
  289. scheduler_stop();
  290. # Create new object with default dispatcher for scheduled tasks
  291. my $cron = new Schedule::Cron( \&scheduler_dispatcher, {
  292. nofork => 1,
  293. loglevel => 0,
  294. log => sub { print $_[1], "\n"; }
  295. } );
  296.  
  297. $cron->add_entry( "* * * * * *", \&scheduler_read_tasks );
  298. # Run scheduler
  299. $cron->run( {detach=>1, pid_file=>SCHED_PID} );
  300.  
  301. if(-e Path::Class::File->new(FD_DIR, 'Settings.pm'))
  302. {
  303. require "FastDownload/Settings.pm"; # Settings for Fast Download Daemon.
  304. if(defined($FastDownload::Settings{autostart_on_agent_startup}) && $FastDownload::Settings{autostart_on_agent_startup} eq "1")
  305. {
  306. start_fastdl();
  307. }
  308. }
  309.  
  310. my $d = Frontier::Daemon::OGP::Forking->new(
  311. methods => {
  312. is_screen_running => \&is_screen_running,
  313. universal_start => \&universal_start,
  314. renice_process => \&renice_process,
  315. cpu_count => \&cpu_count,
  316. rfile_exists => \&rfile_exists,
  317. quick_chk => \&quick_chk,
  318. steam_cmd => \&steam_cmd,
  319. fetch_steam_version => \&fetch_steam_version,
  320. installed_steam_version => \&installed_steam_version,
  321. automatic_steam_update => \&automatic_steam_update,
  322. get_log => \&get_log,
  323. stop_server => \&stop_server,
  324. send_rcon_command => \&send_rcon_command,
  325. dirlist => \&dirlist,
  326. dirlistfm => \&dirlistfm,
  327. readfile => \&readfile,
  328. writefile => \&writefile,
  329. rebootnow => \&rebootnow,
  330. what_os => \&what_os,
  331. start_file_download => \&start_file_download,
  332. lock_additional_files => \&lock_additional_files,
  333. is_file_download_in_progress => \&is_file_download_in_progress,
  334. uncompress_file => \&uncompress_file,
  335. discover_ips => \&discover_ips,
  336. mon_stats => \&mon_stats,
  337. exec => \&exec,
  338. clone_home => \&clone_home,
  339. remove_home => \&remove_home,
  340. start_rsync_install => \&start_rsync_install,
  341. rsync_progress => \&rsync_progress,
  342. restart_server => \&restart_server,
  343. sudo_exec => \&sudo_exec,
  344. master_server_update => \&master_server_update,
  345. secure_path => \&secure_path,
  346. get_chattr => \&get_chattr,
  347. ftp_mgr => \&ftp_mgr,
  348. compress_files => \&compress_files,
  349. stop_fastdl => \&stop_fastdl,
  350. restart_fastdl => \&restart_fastdl,
  351. fastdl_status => \&fastdl_status,
  352. fastdl_get_aliases => \&fastdl_get_aliases,
  353. fastdl_add_alias => \&fastdl_add_alias,
  354. fastdl_del_alias => \&fastdl_del_alias,
  355. fastdl_get_info => \&fastdl_get_info,
  356. fastdl_create_config => \&fastdl_create_config,
  357. agent_restart => \&agent_restart,
  358. scheduler_add_task => \&scheduler_add_task,
  359. scheduler_del_task => \&scheduler_del_task,
  360. scheduler_list_tasks => \&scheduler_list_tasks,
  361. scheduler_edit_task => \&scheduler_edit_task,
  362. get_file_part => \&get_file_part,
  363. stop_update => \&stop_update,
  364. shell_action => \&shell_action,
  365. remote_query => \&remote_query,
  366. send_steam_guard_code => \&send_steam_guard_code,
  367. steam_workshop => \&steam_workshop,
  368. get_workshop_mods_info => \&get_workshop_mods_info
  369. },
  370. debug => 4,
  371. LocalPort => AGENT_PORT,
  372. LocalAddr => AGENT_IP,
  373. ReuseAddr => '1'
  374. ) or die "Couldn't start OGP Agent: $!";
  375.  
  376. sub backup_home_log
  377. {
  378. my ($home_id, $log_file, $console_log_file) = @_;
  379.  
  380. my $home_backup_dir = SCREEN_LOGS_DIR . "/home_id_" . $home_id;
  381.  
  382. if( ! -e $home_backup_dir )
  383. {
  384. if( ! mkdir $home_backup_dir )
  385. {
  386. logger "Can not create a backup directory at $home_backup_dir.";
  387. return 1;
  388. }
  389. }
  390.  
  391. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  392.  
  393. my $backup_file_name = $mday . $mon . $year . '_' . $hour . 'h' . $min . 'm' . $sec . "s.log";
  394.  
  395. my $output_path = $home_backup_dir . "/" . $backup_file_name;
  396.  
  397. # Used for deleting log files older than DELETE_LOGS_AFTER
  398. my @file_list;
  399. my @find_dirs; # directories to search
  400. my $now = time(); # get current time
  401. my $days;
  402. if((DELETE_LOGS_AFTER =~ /^[+-]?\d+$/) && (DELETE_LOGS_AFTER > 0)){
  403. $days = DELETE_LOGS_AFTER; # how many days old
  404. }else{
  405. $days = 30; # how many days old
  406. }
  407. my $seconds_per_day = 60*60*24; # seconds in a day
  408. my $AGE = $days*$seconds_per_day; # age in seconds
  409. push (@find_dirs, $home_backup_dir);
  410.  
  411. # Create local copy of log file backup in the log_backups folder and current user home directory if SCREEN_LOG_LOCAL = 1
  412. if(SCREEN_LOG_LOCAL == 1)
  413. {
  414. # Create local backups folder
  415. my $local_log_folder = Path::Class::Dir->new("logs_backup");
  416.  
  417. if(!-e $local_log_folder){
  418. mkdir($local_log_folder);
  419. }
  420.  
  421. # Add full path to @find_dirs so that log files older than DELETE_LOGS_AFTER are deleted
  422. my $fullpath_to_local_logs = Path::Class::Dir->new(getcwd(), "logs_backup");
  423. push (@find_dirs, $fullpath_to_local_logs);
  424.  
  425. my $log_local = $local_log_folder . "/" . $backup_file_name;
  426.  
  427. # Delete the local log file if it already exists
  428. if(-e $log_local){
  429. unlink $log_local;
  430. }
  431.  
  432. # If the log file contains UPDATE in the filename, do not allow users to see it since it will contain steam credentials
  433. # Will return -1 for not existing
  434. my $isUpdate = index($log_file,SCREEN_TYPE_UPDATE);
  435.  
  436. if($isUpdate == -1){
  437. copy($log_file,$log_local);
  438. }
  439. }
  440.  
  441. # Delete all files in @find_dirs older than DELETE_LOGS_AFTER days
  442. find ( sub {
  443. my $file = $File::Find::name;
  444. if ( -f $file ) {
  445. push (@file_list, $file);
  446. }
  447. }, @find_dirs);
  448.  
  449. # Include the custom console path - and also do a size check on it
  450. if(defined $console_log_file && $console_log_file ne ""){
  451. my $path_to_console_file = $console_log_file;
  452. if( -f $path_to_console_file){
  453. push (@file_list, $path_to_console_file);
  454.  
  455. # Backup and delete this specific file as well if it's over 200MB
  456. my @stats = stat($path_to_console_file);
  457. if($stats[7] >= 209715200){
  458. if(SCREEN_LOG_LOCAL == 1){
  459. # Copy it to local log folder as well
  460. my $local_log_folder = Path::Class::Dir->new("logs_backup");
  461. my $log_local = $local_log_folder . "/" . $backup_file_name . "_console_log";
  462. copy($path_to_console_file, $log_local);
  463. }
  464. # Copy it to the main log folder as well
  465. move($path_to_console_file,$output_path . "_console_log");
  466. }
  467. }
  468. }
  469.  
  470. for my $file (@file_list) {
  471. if( -f $file ){
  472. my @stats = stat($file);
  473. if ($now-$stats[9] > $AGE) {
  474. unlink $file;
  475. }
  476. }
  477. }
  478.  
  479. move($log_file,$output_path);
  480.  
  481. return 0;
  482. }
  483.  
  484. sub get_home_pids
  485. {
  486. my ($home_id) = @_;
  487. my $screen_id = create_screen_id(SCREEN_TYPE_HOME, $home_id);
  488. my ($pid, @pids);
  489. ($pid) = split(/\./, `screen -ls | grep -E -o "[0-9]+\.$screen_id"`, 2);
  490. if(defined $pid)
  491. {
  492. chomp($pid);
  493. while ($pid =~ /^[0-9]+$/)
  494. {
  495. push(@pids,$pid);
  496. $pid = `pgrep -P $pid`;
  497. chomp($pid);
  498. }
  499. }
  500. return @pids;
  501. }
  502.  
  503. sub create_screen_id
  504. {
  505. my ($screen_type, $home_id) = @_;
  506. return sprintf("OGP_%s_%09d", $screen_type, $home_id);
  507. }
  508.  
  509. sub create_screen_cmd
  510. {
  511. my ($screen_id, $exec_cmd) = @_;
  512. $exec_cmd = replace_OGP_Env_Vars($screen_id, "", "", $exec_cmd);
  513. return
  514. sprintf('export WINEDEBUG="fixme-all" && export DISPLAY=:1 && screen -d -m -t "%1$s" -c ' . SCREENRC_FILE . ' -S %1$s %2$s',
  515. $screen_id, $exec_cmd);
  516.  
  517. }
  518.  
  519. sub create_screen_cmd_loop
  520. {
  521. my ($screen_id, $exec_cmd, $envVars, $skipLoop) = @_;
  522. my $server_start_bashfile = $screen_id . "_startup_scr.sh";
  523.  
  524. $exec_cmd = replace_OGP_Env_Vars($screen_id, "", "", $exec_cmd);
  525.  
  526. # Allow file to be overwritten
  527. if(-e $server_start_bashfile){
  528. secure_path_without_decrypt('chattr-i', $server_start_bashfile);
  529. }
  530.  
  531. # Create bash file that screen will run which spawns the server
  532. # If it crashes without user intervention, it will restart
  533. open (SERV_START_SCRIPT, '>', $server_start_bashfile);
  534.  
  535. my $respawn_server_command = "#!/bin/bash" . "\n";
  536.  
  537. if(!$skipLoop){
  538. $respawn_server_command .= "function startServer(){" . "\n";
  539. }
  540.  
  541. if(defined $envVars && $envVars ne ""){
  542. $respawn_server_command .= $envVars;
  543. }
  544.  
  545. if(!$skipLoop){
  546. $respawn_server_command .= "NUMSECONDS=`expr \$(date +%s)`" . "\n"
  547. . "until " . $exec_cmd . "; do" . "\n"
  548. . "let DIFF=(`date +%s` - \"\$NUMSECONDS\")" . "\n"
  549. . "if [ \"\$DIFF\" -gt 15 ]; then" . "\n"
  550. . "NUMSECONDS=`expr \$(date +%s)`" . "\n"
  551. . "echo \"Server '" . $exec_cmd . "' crashed with exit code \$?. Respawning...\" >&2 " . "\n"
  552. . "fi" . "\n"
  553. . "sleep 3" . "\n"
  554. . "done" . "\n"
  555. . "let DIFF=(`date +%s` - \"\$NUMSECONDS\")" . "\n"
  556.  
  557. . "if [ ! -e \"SERVER_STOPPED\" ] && [ \"\$DIFF\" -gt 15 ]; then" . "\n"
  558. . "startServer" . "\n"
  559. . "fi" . "\n"
  560. . "}" . "\n"
  561. . "startServer" . "\n";
  562. }else{
  563. $respawn_server_command .= $exec_cmd . "\n";
  564. }
  565.  
  566. print SERV_START_SCRIPT $respawn_server_command;
  567. close (SERV_START_SCRIPT);
  568.  
  569. # Secure file
  570. secure_path_without_decrypt('chattr+i', $server_start_bashfile);
  571.  
  572. my $screen_exec_script = "bash " . $server_start_bashfile;
  573.  
  574. return
  575. sprintf('export WINEDEBUG="fixme-all" && export DISPLAY=:1 && screen -d -m -t "%1$s" -c ' . SCREENRC_FILE . ' -S %1$s %2$s',
  576. $screen_id, $screen_exec_script);
  577.  
  578. }
  579.  
  580. sub handle_lock_command_line{
  581. my ($command) = @_;
  582. if(defined $command && $command ne ""){
  583. if ($command =~ m/{OGP_LOCK_FILE}/) {
  584. $command =~ s/{OGP_LOCK_FILE}\s*//g;
  585. return secure_path_without_decrypt("chattr+i", $command);
  586. }
  587. }
  588.  
  589. return 0;
  590. }
  591.  
  592. sub replace_OGP_Env_Vars{
  593. # This function replaces constants from environment variables set in the XML
  594. my ($screen_id, $homeid, $homepath, $exec_cmd, $game_key) = @_;
  595.  
  596. # Handle steam specific replacements
  597. if(defined $screen_id && $screen_id ne ""){
  598. my $screen_id_for_txt_update = substr ($screen_id, rindex($screen_id, '_') + 1);
  599. my $steamInsFile = $screen_id_for_txt_update . "_install.txt";
  600. my $steamCMDPath = STEAMCMD_CLIENT_DIR;
  601. my $fullPath = Path::Class::File->new($steamCMDPath, $steamInsFile);
  602.  
  603. # If the install file exists, the game can be auto updated, else it will be ignored by the game for improper syntax
  604. # To generate the install file, the "Install/Update via Steam" button must be clicked on at least once!
  605. if(-e $fullPath){
  606. $exec_cmd =~ s/{OGP_STEAM_CMD_DIR}/$steamCMDPath/g;
  607. $exec_cmd =~ s/{STEAMCMD_INSTALL_FILE}/$steamInsFile/g;
  608. }
  609. }
  610.  
  611.  
  612. # Handle home directory replacement
  613. if(defined $homepath && $homepath ne ""){
  614. $exec_cmd =~ s/{OGP_HOME_DIR}/$homepath/g;
  615. }
  616.  
  617. # Handle global game shared directory replacement
  618. if(defined $game_key && $game_key ne ""){
  619. my $readable_game_key = lc(substr($game_key, 0, rindex($game_key,"_")));
  620. my $shared_path = Path::Class::Dir->new(SHARED_GAME_TMP_DIR, $readable_game_key);
  621. # Create the folder if it doesn't exist
  622. if (!-d $shared_path && !mkdir $shared_path)
  623. {
  624. logger "Could not create " . $shared_path . " directory $!.", 1;
  625. }
  626. $exec_cmd =~ s/{OGP_GAME_SHARED_DIR}/$shared_path/g;
  627. }
  628.  
  629. return $exec_cmd;
  630. }
  631.  
  632. sub encode_list
  633. {
  634. my $encoded_content = '';
  635. if(@_)
  636. {
  637. foreach my $line (@_)
  638. {
  639. $encoded_content .= encode_base64($line, "") . '\n';
  640. }
  641. }
  642. return $encoded_content;
  643. }
  644.  
  645. sub decrypt_param
  646. {
  647. my ($param) = @_;
  648. $param = decode_base64($param);
  649. $param = Crypt::XXTEA::decrypt($param, AGENT_KEY);
  650. $param = decode_base64($param);
  651. return $param;
  652. }
  653.  
  654. sub decrypt_params
  655. {
  656. my @params;
  657. foreach my $param (@_)
  658. {
  659. $param = &decrypt_param($param);
  660. push(@params, $param);
  661. }
  662. return @params;
  663. }
  664.  
  665. sub check_steam_cmd_client
  666. {
  667. if (STEAM_LICENSE ne STEAM_LICENSE_OK)
  668. {
  669. logger "Steam license not accepted, stopping Steam client check.";
  670. return 0;
  671. }
  672. if (!-d STEAMCMD_CLIENT_DIR && !mkdir STEAMCMD_CLIENT_DIR)
  673. {
  674. logger "Could not create " . STEAMCMD_CLIENT_DIR . " directory $!.", 1;
  675. exit -1;
  676. }
  677. if (!-w STEAMCMD_CLIENT_DIR)
  678. {
  679. logger "Steam client dir '"
  680. . STEAMCMD_CLIENT_DIR
  681. . "' not writable. Unable to get Steam client.";
  682. return -1;
  683. }
  684. if (!-f STEAMCMD_CLIENT_BIN)
  685. {
  686. logger "The Steam client, steamcmd, does not exist yet, installing...";
  687. my $steam_client_file = 'steamcmd_linux.tar.gz';
  688. my $steam_client_path = Path::Class::File->new(STEAMCMD_CLIENT_DIR, $steam_client_file);
  689. my $steam_client_url =
  690. "http://media.steampowered.com/client/" . $steam_client_file;
  691. logger "Downloading the Steam client from $steam_client_url to '"
  692. . $steam_client_path . "'.";
  693.  
  694. my $ua = LWP::UserAgent->new;
  695. $ua->agent('Mozilla/5.0');
  696. my $response = $ua->get($steam_client_url, ':content_file' => "$steam_client_path");
  697.  
  698. unless ($response->is_success)
  699. {
  700. logger "Failed to download steam installer from "
  701. . $steam_client_url
  702. . ".", 1;
  703. return -1;
  704. }
  705. if (-f $steam_client_path)
  706. {
  707. logger "Uncompressing $steam_client_path";
  708. if ( uncompress_file_without_decrypt($steam_client_path, STEAMCMD_CLIENT_DIR) != 1 )
  709. {
  710. unlink($steam_client_path);
  711. logger "Unable to uncompress $steam_client_path, the file has been removed.";
  712. return -1;
  713. }
  714. unlink($steam_client_path);
  715. }
  716. }
  717. if (!-x STEAMCMD_CLIENT_BIN)
  718. {
  719. if ( ! chmod 0755, STEAMCMD_CLIENT_BIN )
  720. {
  721. logger "Unable to apply execution permission to ".STEAMCMD_CLIENT_BIN.".";
  722. }
  723. }
  724. return 1;
  725. }
  726.  
  727. sub is_screen_running
  728. {
  729. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  730. my ($screen_type, $home_id) = decrypt_params(@_);
  731. return is_screen_running_without_decrypt($screen_type, $home_id);
  732. }
  733.  
  734. sub is_screen_running_without_decrypt
  735. {
  736. my ($screen_type, $home_id) = @_;
  737.  
  738. my $screen_id = create_screen_id($screen_type, $home_id);
  739.  
  740. my $is_running = `screen -list | grep $screen_id`;
  741.  
  742. if ($is_running =~ /^\s*$/)
  743. {
  744. return 0;
  745. }
  746. else
  747. {
  748. return 1;
  749. }
  750. }
  751.  
  752. # Delete Server Stopped Status File:
  753. sub deleteStoppedStatFile
  754. {
  755. my ($home_path) = @_;
  756. my $server_stop_status_file = Path::Class::File->new($home_path, "SERVER_STOPPED");
  757. if(-e $server_stop_status_file)
  758. {
  759. unlink $server_stop_status_file;
  760. }
  761. }
  762.  
  763. # Universal startup function
  764. sub universal_start
  765. {
  766. chomp(@_);
  767. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  768. return universal_start_without_decrypt(decrypt_params(@_));
  769. }
  770.  
  771. # Split to two parts because of internal calls.
  772. sub universal_start_without_decrypt
  773. {
  774. my (
  775. $home_id, $home_path, $server_exe, $run_dir,
  776. $startup_cmd, $server_port, $server_ip, $cpu, $nice, $preStart, $envVars, $game_key, $console_log
  777. ) = @_;
  778.  
  779. if (is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 1)
  780. {
  781. logger "This server is already running (ID: $home_id).";
  782. return -14;
  783. }
  784.  
  785. if (!-e $home_path)
  786. {
  787. logger "Can't find server's install path [ $home_path ].";
  788. return -10;
  789. }
  790.  
  791. my $uid = `id -u`;
  792. chomp $uid;
  793. my $gid = `id -g`;
  794. chomp $gid;
  795. my $path = $home_path;
  796. $path =~ s/('+)/'\"$1\"'/g;
  797. sudo_exec_without_decrypt('chown -Rf '.$uid.':'.$gid.' \''.$path.'\'');
  798.  
  799. # Some game require that we are in the directory where the binary is.
  800. my $game_binary_dir = Path::Class::Dir->new($home_path, $run_dir);
  801. if ( -e $game_binary_dir && !chdir $game_binary_dir)
  802. {
  803. logger "Could not change to server binary directory $game_binary_dir.";
  804. return -12;
  805. }
  806.  
  807. secure_path_without_decrypt('chattr-i', $server_exe);
  808.  
  809. if (!-x $server_exe)
  810. {
  811. if (!chmod 0755, $server_exe)
  812. {
  813. logger "The $server_exe file is not executable.";
  814. return -13;
  815. }
  816. }
  817.  
  818. if(defined $preStart && $preStart ne ""){
  819. # Get it in the format that the startup file can use
  820. $preStart = multiline_to_startup_comma_format($preStart);
  821. }else{
  822. $preStart = "";
  823. }
  824.  
  825. if(defined $envVars && $envVars ne ""){
  826. # Replace variables in the envvars if they exist
  827. my @prestartenvvars = split /[\r\n]+/, $envVars;
  828. my $envVarStr = "";
  829. foreach my $line (@prestartenvvars) {
  830. $line = replace_OGP_Env_Vars("", $home_id, $home_path, $line, $game_key);
  831. if($line ne ""){
  832. logger "Configuring environment variable: $line";
  833. $envVarStr .= "$line\n";
  834. }
  835. }
  836.  
  837. if(defined $envVarStr && $envVarStr ne ""){
  838. $envVars = $envVarStr;
  839. }
  840.  
  841. # Get it in the format that the startup file can use
  842. $envVars = multiline_to_startup_comma_format($envVars);
  843. }else{
  844. $envVars = "";
  845. }
  846.  
  847. secure_path_without_decrypt('chattr+i', $server_exe);
  848.  
  849. # Create startup file for the server.
  850. my $startup_file =
  851. Path::Class::File->new(GAME_STARTUP_DIR, "$server_ip-$server_port");
  852. if (open(STARTUP, '>', $startup_file))
  853. {
  854. print STARTUP
  855. "$home_id,$home_path,$server_exe,$run_dir,$startup_cmd,$server_port,$server_ip,$cpu,$nice,$preStart,$envVars,$game_key,$console_log";
  856. logger "Created startup flag for $server_ip-$server_port";
  857. close(STARTUP);
  858. }
  859. else
  860. {
  861. logger "Cannot create file in " . $startup_file . " : $!";
  862. }
  863.  
  864. if(defined $preStart && $preStart ne ""){
  865. # Get it in the format that the startup file can use
  866. $preStart = startup_comma_format_to_multiline($preStart);
  867. }else{
  868. $preStart = "";
  869. }
  870.  
  871. if(defined $envVars && $envVars ne ""){
  872. # Get it in the format that the startup file can use
  873. $envVars = startup_comma_format_to_multiline($envVars);
  874. }else{
  875. $envVars = "";
  876. }
  877.  
  878. # Create the startup string.
  879. my $screen_id = create_screen_id(SCREEN_TYPE_HOME, $home_id);
  880. my $file_extension = substr $server_exe, -4;
  881. my $cli_bin;
  882. my $command;
  883. my $run_before_start;
  884.  
  885. # Replace any OGP variables found in the command line
  886. $startup_cmd = replace_OGP_Env_Vars($screen_id, $home_id, $home_path, $startup_cmd, $game_key);
  887.  
  888. if($file_extension eq ".exe" or $file_extension eq ".bat")
  889. {
  890. $command = "wine $server_exe $startup_cmd";
  891.  
  892. if ($cpu ne 'NA')
  893. {
  894. $command = "taskset -c $cpu wine $server_exe $startup_cmd";
  895. }
  896.  
  897. if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
  898. deleteStoppedStatFile($home_path);
  899. $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars);
  900. }else{
  901. $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1);
  902. }
  903. }
  904. elsif($file_extension eq ".jar")
  905. {
  906. $command = "$startup_cmd";
  907.  
  908. if ($cpu ne 'NA')
  909. {
  910. $command = "taskset -c $cpu $startup_cmd";
  911. }
  912.  
  913. if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
  914. deleteStoppedStatFile($home_path);
  915. $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars);
  916. }else{
  917. $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1);
  918. }
  919. }
  920. else
  921. {
  922. $command = "./$server_exe $startup_cmd";
  923.  
  924. if ($cpu ne 'NA')
  925. {
  926. $command = "taskset -c $cpu ./$server_exe $startup_cmd";
  927. }
  928.  
  929. if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
  930. deleteStoppedStatFile($home_path);
  931. $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars);
  932. }else{
  933. $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1);
  934. }
  935. }
  936.  
  937. my $log_file = Path::Class::File->new(SCREEN_LOGS_DIR, "screenlog.$screen_id");
  938. backup_home_log( $home_id, $log_file, $home_path . "/" . $console_log );
  939.  
  940. logger
  941. "Startup command [ $cli_bin ] will be executed in dir $game_binary_dir.";
  942.  
  943. # Run before start script
  944. $run_before_start = run_before_start_commands($home_id, $home_path, $preStart);
  945.  
  946. system($cli_bin);
  947.  
  948. sleep(1);
  949.  
  950. renice_process_without_decrypt($home_id, $nice);
  951.  
  952. chdir AGENT_RUN_DIR;
  953. return 1;
  954. }
  955.  
  956. # This is used to change the priority of process
  957. # @return 1 if successfully set prosess priority
  958. # @return -1 in case of an error.
  959. sub renice_process
  960. {
  961. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  962. return renice_process_without_decrypt(decrypt_params(@_));
  963. }
  964.  
  965. sub renice_process_without_decrypt
  966. {
  967. my ($home_id, $nice) = @_;
  968. if ($nice != 0)
  969. {
  970. my @pids = get_home_pids($home_id);
  971. logger
  972. "Renicing pids [ @pids ] from home_id $home_id with nice value $nice.";
  973. foreach my $pid (@pids)
  974. {
  975. my $rpid = kill 0, $pid;
  976. if ($rpid == 1)
  977. {
  978. my $ret = sudo_exec_without_decrypt('/usr/bin/renice '.$nice.' '.$pid);
  979. ($ret) = split(/;/, $ret, 2);
  980. if($ret != 1)
  981. {
  982. logger "Unable to renice process, probably bad sudo password or not in sudoers list.";
  983. return -1
  984. }
  985. }
  986. }
  987. }
  988. return 1;
  989. }
  990.  
  991. # This is used to force a process to run on a particular CPU
  992. sub force_cpu
  993. {
  994. return force_cpu_without_decrypt(decrypt_params(@_));
  995. }
  996.  
  997. sub force_cpu_without_decrypt
  998. {
  999. my ($home_id, $cpu) = @_;
  1000. if ($cpu ne 'NA')
  1001. {
  1002. my @pids = get_home_pids($home_id);
  1003. logger
  1004. "Setting server from home_id $home_id with pids @pids to run on CPU $cpu.";
  1005. foreach my $pid (@pids)
  1006. {
  1007. my $rpid = kill 0, $pid;
  1008. if ($rpid == 1)
  1009. {
  1010. my $ret = sudo_exec_without_decrypt('/usr/bin/taskset -pc '.$cpu.' '.$pid);
  1011. ($ret) = split(/;/, $ret, 2);
  1012. if($ret != 1)
  1013. {
  1014. logger "Unable to set cpu, probably a bad sudo password or not in sudoers list.";
  1015. return -1
  1016. }
  1017. }
  1018. }
  1019. }
  1020. return 1;
  1021. }
  1022.  
  1023. # Returns the number of CPUs available.
  1024. sub cpu_count
  1025. {
  1026. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1027. if (!-e "/proc/cpuinfo")
  1028. {
  1029. return "ERROR - Missing /proc/cpuinfo";
  1030. }
  1031.  
  1032. open(CPUINFO, '<', "/proc/cpuinfo")
  1033. or return "ERROR - Cannot open /proc/cpuinfo";
  1034.  
  1035. my $cpu_count = 0;
  1036.  
  1037. while (<CPUINFO>)
  1038. {
  1039. chomp;
  1040. next if $_ !~ /^processor/;
  1041. $cpu_count++;
  1042. }
  1043. close(CPUINFO);
  1044. return "$cpu_count";
  1045. }
  1046.  
  1047. ### File exists check ####
  1048. # Simple a way to check if a file exists using the remote agent
  1049. #
  1050. # @return 0 when file exists.
  1051. # @return 1 when file does not exist.
  1052. sub rfile_exists
  1053. {
  1054. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1055. chdir AGENT_RUN_DIR;
  1056. my $checkFile = decrypt_param(@_);
  1057.  
  1058. if (-e $checkFile)
  1059. {
  1060. return 0;
  1061. }
  1062. else
  1063. {
  1064. return 1;
  1065. }
  1066. }
  1067.  
  1068. #### Quick check to verify agent is up and running
  1069. # Used to quickly see if the agent is online, and if the keys match.
  1070. # The message that is sent to the agent must be hello, if not then
  1071. # it is intrepret as encryption key missmatch.
  1072. #
  1073. # @return 1 when encrypted message is not 'hello'
  1074. # @return 0 when check is ok.
  1075. sub quick_chk
  1076. {
  1077. my $dec_check = &decrypt_param(@_);
  1078. if ($dec_check ne 'hello')
  1079. {
  1080. logger "ERROR - Encryption key mismatch! Returning 1 to asker.";
  1081. return 1;
  1082. }
  1083. return 0;
  1084. }
  1085.  
  1086. ### Return -10 If home path is not found.
  1087. ### Return -9 If log type was invalid.
  1088. ### Return -8 If log file was not found.
  1089. ### 0 reserved for connection problems.
  1090. ### Return 1;content If log found and screen running.
  1091. ### Return 2;content If log found but screen is not running.
  1092. sub get_log
  1093. {
  1094. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1095. my ($screen_type, $home_id, $home_path, $nb_of_lines, $log_file) = decrypt_params(@_);
  1096.  
  1097. if (!chdir $home_path)
  1098. {
  1099. logger "Can't change to server's install path [ $home_path ].";
  1100. return -10;
  1101. }
  1102.  
  1103. if ( ($screen_type eq SCREEN_TYPE_UPDATE)
  1104. && ($screen_type eq SCREEN_TYPE_HOME))
  1105. {
  1106. logger "Invalid screen type '$screen_type'.";
  1107. return -9;
  1108. }
  1109.  
  1110. if(!$log_file)
  1111. {
  1112. my $screen_id = create_screen_id($screen_type, $home_id);
  1113. $log_file = Path::Class::File->new(SCREEN_LOGS_DIR, "screenlog.$screen_id");
  1114. }
  1115. else
  1116. {
  1117. $log_file = Path::Class::File->new($home_path, $log_file);
  1118. }
  1119.  
  1120. chmod 0644, $log_file;
  1121.  
  1122. # Create local copy of current log file if SCREEN_LOG_LOCAL = 1
  1123. if(SCREEN_LOG_LOCAL == 1)
  1124. {
  1125. my $log_local = Path::Class::File->new($home_path, "LOG_$screen_type.txt");
  1126. if ( -e $log_local )
  1127. {
  1128. unlink $log_local;
  1129. }
  1130.  
  1131. # Copy log file only if it's not an UPDATE type as it may contain steam credentials
  1132. if($screen_type eq SCREEN_TYPE_HOME){
  1133. copy($log_file, $log_local);
  1134. }
  1135. }
  1136.  
  1137. # Regenerate the log file if it doesn't exist
  1138. unless ( -e $log_file )
  1139. {
  1140. if (open(NEWLOG, '>', $log_file))
  1141. {
  1142. logger "Log file missing, regenerating: " . $log_file;
  1143. print NEWLOG "Log file missing, started new log\n";
  1144. close(NEWLOG);
  1145. }
  1146. else
  1147. {
  1148. logger "Cannot regenerate log file in " . $log_file . " : $!";
  1149. return -8;
  1150. }
  1151. }
  1152.  
  1153. # Return a few lines of output to the web browser
  1154. my(@modedlines) = `tail -n $nb_of_lines $log_file`;
  1155.  
  1156. my $linecount = 0;
  1157.  
  1158. foreach my $line (@modedlines) {
  1159. #Text replacements to remove the Steam user login from steamcmd logs for security reasons.
  1160. $line =~ s/login .*//g;
  1161. $line =~ s/Logging .*//g;
  1162. $line =~ s/set_steam_guard_code.*//g;
  1163. $line =~ s/force_install_dir.*//g;
  1164. #Text replacements to remove empty lines.
  1165. $line =~ s/^ +//g;
  1166. $line =~ s/^\t+//g;
  1167. $line =~ s/^\e+//g;
  1168. #Remove � from console output when master server update is running.
  1169. $line =~ s/�//g;
  1170. $modedlines[$linecount]=$line;
  1171. $linecount++;
  1172. }
  1173.  
  1174. my $encoded_content = encode_list(@modedlines);
  1175. chdir AGENT_RUN_DIR;
  1176. if(is_screen_running_without_decrypt($screen_type, $home_id) == 1)
  1177. {
  1178. return "1;" . $encoded_content;
  1179. }
  1180. else
  1181. {
  1182. return "2;" . $encoded_content;
  1183. }
  1184. }
  1185.  
  1186. # stop server function
  1187. sub stop_server
  1188. {
  1189. chomp(@_);
  1190. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1191. return stop_server_without_decrypt(decrypt_params(@_));
  1192. }
  1193.  
  1194. ##### Stop server without decrypt
  1195. ### Return 1 when error occurred on decryption.
  1196. ### Return 0 on success
  1197. sub stop_server_without_decrypt
  1198. {
  1199. my ($home_id, $server_ip, $server_port, $control_protocol,
  1200. $control_password, $control_type, $home_path) = @_;
  1201.  
  1202. my $usedProtocolToStop = 0;
  1203.  
  1204. my $startup_file = Path::Class::File->new(GAME_STARTUP_DIR, "$server_ip-$server_port");
  1205.  
  1206. if (-e $startup_file)
  1207. {
  1208. logger "Removing startup flag " . $startup_file . "";
  1209. unlink($startup_file)
  1210. or logger "Cannot remove the startup flag file $startup_file $!";
  1211. }
  1212.  
  1213. # Create file indicator that the game server has been stopped if defined
  1214. if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
  1215.  
  1216. # Get current directory and chdir into the game's home dir
  1217. my $curDir = getcwd();
  1218. chdir $home_path;
  1219.  
  1220. # Create stopped indicator file used by autorestart of OGP if server crashes
  1221. open(STOPFILE, '>', "SERVER_STOPPED");
  1222. close(STOPFILE);
  1223.  
  1224. # Return to original directory
  1225. chdir $curDir;
  1226. }
  1227.  
  1228. # Some validation checks for the variables.
  1229. if ($server_ip =~ /^\s*$/ || $server_port < 0 || $server_port > 65535)
  1230. {
  1231. logger("Invalid IP:Port given $server_ip:$server_port.");
  1232. return 1;
  1233. }
  1234.  
  1235. if ($control_password !~ /^\s*$/ and $control_protocol ne "")
  1236. {
  1237. if ($control_protocol eq "rcon")
  1238. {
  1239. use KKrcon::KKrcon;
  1240. my $rcon = new KKrcon(
  1241. Password => $control_password,
  1242. Host => $server_ip,
  1243. Port => $server_port,
  1244. Type => $control_type
  1245. );
  1246.  
  1247. my $rconCommand = "quit";
  1248. $rcon->execute($rconCommand);
  1249. $usedProtocolToStop = 1;
  1250. }
  1251. elsif ($control_protocol eq "rcon2")
  1252. {
  1253. use KKrcon::HL2;
  1254. my $rcon2 = new HL2(
  1255. hostname => $server_ip,
  1256. port => $server_port,
  1257. password => $control_password,
  1258. timeout => 2
  1259. );
  1260.  
  1261. my $rconCommand = "quit";
  1262. $rcon2->run($rconCommand);
  1263. $usedProtocolToStop = 1;
  1264. }
  1265. elsif ($control_protocol eq "armabe")
  1266. {
  1267. use ArmaBE::ArmaBE;
  1268. my $armabe = new ArmaBE(
  1269. hostname => $server_ip,
  1270. port => $server_port, # Uses server port for now (Arma 2), Arma 3 BE uses a different, user definable port
  1271. password => $control_password,
  1272. timeout => 2
  1273. );
  1274.  
  1275. my $rconCommand = "#shutdown";
  1276. my $armabe_result = $armabe->run($rconCommand);
  1277. if ($armabe_result) {
  1278. logger "ArmaBE Shutdown command sent successfully";
  1279. $usedProtocolToStop = 1;
  1280. }
  1281. }
  1282.  
  1283. my @server_pids;
  1284.  
  1285. # Gives the server time to shutdown with rcon in case it takes a while for the server to shutdown (arma for example) before we forcefully kill it
  1286. if ($usedProtocolToStop == 1 && is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 1){
  1287. @server_pids = get_home_pids($home_id);
  1288. my $timeWaited = 0;
  1289. my $pidSize = @server_pids;
  1290. my $maxWaitTime = 5;
  1291.  
  1292. # Maximum time to wait can now be configured as a preference
  1293. if(defined($Cfg::Preferences{protocol_shutdown_waittime}) && $Cfg::Preferences{protocol_shutdown_waittime} =~ /^\d+?$/){
  1294. $maxWaitTime = $Cfg::Preferences{protocol_shutdown_waittime};
  1295. }
  1296.  
  1297. while ($pidSize > 0 && $timeWaited < $maxWaitTime && is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 1) {
  1298. select(undef, undef, undef, 0.25); # Sleeps for 250ms
  1299.  
  1300. # Add to time waited
  1301. $timeWaited += 0.25;
  1302.  
  1303. # Recheck server home PIDs
  1304. @server_pids = get_home_pids($home_id);
  1305. $pidSize = @server_pids;
  1306. }
  1307. }
  1308.  
  1309. if (is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 0)
  1310. {
  1311. logger "Stopped server $server_ip:$server_port with rcon quit.";
  1312. return 0;
  1313. }
  1314. else
  1315. {
  1316. logger "Failed to send rcon quit. Stopping server with kill command.";
  1317. }
  1318.  
  1319. @server_pids = get_home_pids($home_id);
  1320.  
  1321. my $cnt;
  1322. foreach my $pid (@server_pids)
  1323. {
  1324. chomp($pid);
  1325. $cnt = kill 15, $pid;
  1326.  
  1327. if ($cnt != 1)
  1328. {
  1329. $cnt = kill 9, $pid;
  1330. if ($cnt == 1)
  1331. {
  1332. logger "Stopped process with pid $pid successfully using kill 9.";
  1333. }
  1334. else
  1335. {
  1336. logger "Process $pid can not be stopped.";
  1337. }
  1338. }
  1339. else
  1340. {
  1341. logger "Stopped process with pid $pid successfully using kill 15.";
  1342. }
  1343. }
  1344. system('screen -wipe > /dev/null 2>&1');
  1345. return 0;
  1346. }
  1347. else
  1348. {
  1349. logger "Remote control protocol not available or PASSWORD NOT SET. Using kill signal instead.";
  1350. my @server_pids = get_home_pids($home_id);
  1351.  
  1352. my $cnt;
  1353. foreach my $pid (@server_pids)
  1354. {
  1355. chomp($pid);
  1356. $cnt = kill 15, $pid;
  1357.  
  1358. if ($cnt != 1)
  1359. {
  1360. $cnt = kill 9, $pid;
  1361. if ($cnt == 1)
  1362. {
  1363. logger "Stopped process with pid $pid successfully using kill 9.";
  1364. }
  1365. else
  1366. {
  1367. logger "Process $pid can not be stopped.";
  1368. }
  1369. }
  1370. else
  1371. {
  1372. logger "Stopped process with pid $pid successfully using kill 15.";
  1373. }
  1374. }
  1375. system('screen -wipe > /dev/null 2>&1');
  1376. return 0;
  1377. }
  1378. }
  1379.  
  1380. ##### Send RCON command
  1381. ### Return 0 when error occurred on decryption.
  1382. ### Return 1 on success
  1383. sub send_rcon_command
  1384. {
  1385. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1386. my ($home_id, $server_ip, $server_port, $control_protocol,
  1387. $control_password, $control_type, $rconCommand) = decrypt_params(@_);
  1388.  
  1389. # legacy console
  1390. if ($control_protocol eq "lcon")
  1391. {
  1392. my $screen_id = create_screen_id(SCREEN_TYPE_HOME, $home_id);
  1393. system('screen -S '.$screen_id.' -p 0 -X stuff "'.$rconCommand.'$(printf \\\\r)"');
  1394. logger "Sending legacy console command to ".$screen_id.": \n$rconCommand \n .";
  1395. if ($? == -1)
  1396. {
  1397. my(@modedlines) = "$rconCommand";
  1398. my $encoded_content = encode_list(@modedlines);
  1399. return "1;" . $encoded_content;
  1400. }
  1401. return 0;
  1402. }
  1403.  
  1404. # Some validation checks for the variables.
  1405. if ($server_ip =~ /^\s*$/ || $server_port < 0 || $server_port > 65535)
  1406. {
  1407. logger("Invalid IP:Port given $server_ip:$server_port.");
  1408. return 0;
  1409. }
  1410.  
  1411. if ($control_password !~ /^\s*$/)
  1412. {
  1413. if ($control_protocol eq "rcon")
  1414. {
  1415. use KKrcon::KKrcon;
  1416. my $rcon = new KKrcon(
  1417. Password => $control_password,
  1418. Host => $server_ip,
  1419. Port => $server_port,
  1420. Type => $control_type
  1421. );
  1422.  
  1423. logger "Sending RCON command to $server_ip:$server_port: \n$rconCommand \n .";
  1424.  
  1425. my(@modedlines) = $rcon->execute($rconCommand);
  1426. my $encoded_content = encode_list(@modedlines);
  1427. return "1;" . $encoded_content;
  1428. }
  1429. elsif ($control_protocol eq "rcon2")
  1430. {
  1431. use KKrcon::HL2;
  1432. my $rcon2 = new HL2(
  1433. hostname => $server_ip,
  1434. port => $server_port,
  1435. password => $control_password,
  1436. timeout => 2
  1437. );
  1438.  
  1439. logger "Sending RCON command to $server_ip:$server_port: \n $rconCommand \n .";
  1440.  
  1441. my(@modedlines) = $rcon2->run($rconCommand);
  1442. my $encoded_content = encode_list(@modedlines);
  1443. return "1;" . $encoded_content;
  1444. }
  1445. elsif ($control_protocol eq "armabe")
  1446. {
  1447. use ArmaBE::ArmaBE;
  1448. my $armabe = new ArmaBE(
  1449. hostname => $server_ip,
  1450. port => $server_port, # Uses server port for now (Arma 2), Arma 3 BE uses a different, user definable port
  1451. password => $control_password,
  1452. timeout => 2
  1453. );
  1454.  
  1455. logger "Sending RCON command to $server_ip:$server_port: \n $rconCommand \n .";
  1456.  
  1457. my(@modedlines) = $armabe->run($rconCommand);
  1458. my $encoded_content = encode_list(@modedlines);
  1459. return "1;" . $encoded_content;
  1460. }
  1461. }
  1462. else
  1463. {
  1464. logger "Control protocol PASSWORD NOT SET.";
  1465. return -10;
  1466. }
  1467. }
  1468.  
  1469. ##### Returns a directory listing
  1470. ### @return List of directories if everything OK.
  1471. ### @return 0 If the directory is not found.
  1472. ### @return -1 If cannot open the directory.
  1473. sub dirlist
  1474. {
  1475. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1476. my ($datadir) = &decrypt_param(@_);
  1477. logger "Asked for dirlist of $datadir directory.";
  1478. if (!-d $datadir)
  1479. {
  1480. logger "ERROR - Directory [ $datadir ] not found!";
  1481. return -1;
  1482. }
  1483. if (!opendir(DIR, $datadir))
  1484. {
  1485. logger "ERROR - Can't open $datadir: $!";
  1486. return -2;
  1487. }
  1488. my @dirlist = readdir(DIR);
  1489. closedir(DIR);
  1490. return join(";", @dirlist);
  1491. }
  1492.  
  1493. ##### Returns a directory listing with extra info the filemanager
  1494. ### @return List of directories if everything OK.
  1495. ### @return 1 If the directory is empty.
  1496. ### @return -1 If the directory is not found.
  1497. ### @return -2 If cannot open the directory.
  1498. sub dirlistfm
  1499. {
  1500. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1501. my $datadir = &decrypt_param(@_);
  1502.  
  1503. logger "Asked for dirlist of $datadir directory.";
  1504.  
  1505. if (!-d $datadir)
  1506. {
  1507. logger "ERROR - Directory [ $datadir ] not found!";
  1508. return -1;
  1509. }
  1510.  
  1511. if (!opendir(DIR, $datadir))
  1512. {
  1513. logger "ERROR - Can't open $datadir: $!";
  1514. return -2;
  1515. }
  1516.  
  1517. my $dir = $datadir;
  1518. $dir =~ s/('+)/'"$1"'/g;
  1519. my $lsattr = `lsattr '$dir' 2>/dev/null`;
  1520.  
  1521. my @attr_all = split /\n+/, $lsattr;
  1522.  
  1523. my %attr = ();
  1524.  
  1525. my ($a, $p, @f);
  1526.  
  1527. foreach (@attr_all)
  1528. {
  1529. ($a, $p) = split(/\s/, $_, 2);
  1530. @f = split /\//, $p;
  1531. $attr{$f[-1]} = $a;
  1532. }
  1533.  
  1534. my %dirfiles = ();
  1535.  
  1536. my (
  1537. $dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
  1538. $size, $atime, $mtime, $ctime, $blksize, $blocks
  1539. );
  1540.  
  1541. my $count = 0;
  1542.  
  1543. chdir($datadir);
  1544.  
  1545. while (my $item = readdir(DIR))
  1546. {
  1547. #skip the . and .. special dirs
  1548. next if $item eq '.';
  1549. next if $item eq '..';
  1550. #print "Dir list is" . $item."\n";
  1551. #Stat the file to get ownership and size
  1552. (
  1553. $dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
  1554. $size, $atime, $mtime, $ctime, $blksize, $blocks
  1555. ) = stat($item);
  1556.  
  1557. if(defined $uid)
  1558. {
  1559. $uid = getpwuid($uid);
  1560. }
  1561. else
  1562. {
  1563. $uid = '';
  1564. }
  1565.  
  1566. if(defined $gid)
  1567. {
  1568. $gid = getgrgid($gid);
  1569. }
  1570. else
  1571. {
  1572. $gid = '';
  1573. }
  1574.  
  1575. #This if else logic determines what it is, File, Directory, other
  1576. if (-T $item)
  1577. {
  1578. # print "File\n";
  1579. $dirfiles{'files'}{$count}{'filename'} = encode_base64($item);
  1580. $dirfiles{'files'}{$count}{'size'} = $size;
  1581. $dirfiles{'files'}{$count}{'user'} = $uid;
  1582. $dirfiles{'files'}{$count}{'group'} = $gid;
  1583. $dirfiles{'files'}{$count}{'attr'} = $attr{$item};
  1584. }
  1585. elsif (-d $item)
  1586. {
  1587. # print "Dir\n";
  1588. $dirfiles{'directorys'}{$count}{'filename'} = encode_base64($item);
  1589. $dirfiles{'directorys'}{$count}{'size'} = $size;
  1590. $dirfiles{'directorys'}{$count}{'user'} = $uid;
  1591. $dirfiles{'directorys'}{$count}{'group'} = $gid;
  1592. }
  1593. elsif (-B $item)
  1594. {
  1595. #print "File\n";
  1596. $dirfiles{'binarys'}{$count}{'filename'} = encode_base64($item);
  1597. $dirfiles{'binarys'}{$count}{'size'} = $size;
  1598. $dirfiles{'binarys'}{$count}{'user'} = $uid;
  1599. $dirfiles{'binarys'}{$count}{'group'} = $gid;
  1600. $dirfiles{'binarys'}{$count}{'attr'} = $attr{$item};
  1601. }
  1602. else
  1603. {
  1604. #print "Unknown\n"
  1605. #will be listed as common files;
  1606. $dirfiles{'files'}{$count}{'filename'} = encode_base64($item);
  1607. $dirfiles{'files'}{$count}{'size'} = $size;
  1608. $dirfiles{'files'}{$count}{'user'} = $uid;
  1609. $dirfiles{'files'}{$count}{'group'} = $gid;
  1610. $dirfiles{'files'}{$count}{'attr'} = $attr{$item};
  1611. }
  1612. $count++;
  1613. }
  1614. closedir(DIR);
  1615.  
  1616. if ($count eq 0)
  1617. {
  1618. logger "Empty directory $datadir.";
  1619. return 1;
  1620. }
  1621.  
  1622. chdir AGENT_RUN_DIR;
  1623. #Now we return it to the webpage, as array
  1624. return {%dirfiles};
  1625. }
  1626.  
  1627. ###### Returns the contents of a text file
  1628. sub readfile
  1629. {
  1630. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1631. chdir AGENT_RUN_DIR;
  1632. my $userfile = &decrypt_param(@_);
  1633.  
  1634. unless ( -e $userfile )
  1635. {
  1636. if (open(BLANK, '>', $userfile))
  1637. {
  1638. close(BLANK);
  1639. }
  1640. }
  1641.  
  1642. if (!open(USERFILE, '<', $userfile))
  1643. {
  1644. logger "ERROR - Can't open file $userfile for reading.";
  1645. return -1;
  1646. }
  1647.  
  1648. my ($wholefile, $buf);
  1649.  
  1650. while (read(USERFILE, $buf, 60 * 57))
  1651. {
  1652. $wholefile .= encode_base64($buf);
  1653. }
  1654. close(USERFILE);
  1655.  
  1656. if(!defined $wholefile)
  1657. {
  1658. return "1; ";
  1659. }
  1660.  
  1661. return "1;" . $wholefile;
  1662. }
  1663.  
  1664. ###### Backs up file, then writes data to new file
  1665. ### @return 1 On success
  1666. ### @return 0 In case of a failure
  1667. sub writefile
  1668. {
  1669. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1670. chdir AGENT_RUN_DIR;
  1671. # $writefile = file we're editing, $filedata = the contents were writing to it
  1672. my ($writefile, $filedata) = &decrypt_params(@_);
  1673. if (!-e $writefile)
  1674. {
  1675. open FILE, ">", $writefile;
  1676. }
  1677. else
  1678. {
  1679. # backup the existing file
  1680. logger
  1681. "Backing up file $writefile to $writefile.bak before writing new data.";
  1682. if (!copy("$writefile", "$writefile.bak"))
  1683. {
  1684. logger
  1685. "ERROR - Failed to backup $writefile to $writefile.bak. Error: $!";
  1686. return 0;
  1687. }
  1688. }
  1689. if (!-w $writefile)
  1690. {
  1691. logger "ERROR - File [ $writefile ] is not writeable!";
  1692. return 0;
  1693. }
  1694. if (!open(WRITER, '>', $writefile))
  1695. {
  1696. logger "ERROR - Failed to open $writefile for writing.";
  1697. return 0;
  1698. }
  1699. $filedata = decode_base64($filedata);
  1700. $filedata =~ s/\r//g;
  1701. print WRITER "$filedata";
  1702. close(WRITER);
  1703. logger "Wrote $writefile successfully!";
  1704. return 1;
  1705. }
  1706.  
  1707. ###### Reboots the server remotely through panel
  1708. ### @return 1 On success
  1709. ### @return 0 In case of a failure
  1710. sub rebootnow
  1711. {
  1712. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1713. sudo_exec_without_decrypt('sleep 10s; shutdown -r now');
  1714. logger "Scheduled system reboot to occur in 10 seconds successfully!";
  1715. return 1;
  1716. }
  1717.  
  1718. # Determine the os of the agent machine.
  1719. sub what_os
  1720. {
  1721. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1722. logger "Asking for OS type";
  1723. my $which_uname = `which uname`;
  1724. chomp $which_uname;
  1725. if ($which_uname ne "")
  1726. {
  1727. my $os;
  1728. my $os_name;
  1729. my $os_arch;
  1730. my $wine_ver = "";
  1731. $os_name = `$which_uname`;
  1732. chomp $os_name;
  1733. $os_arch = `$which_uname -m`;
  1734. chomp $os_arch;
  1735. my $which_wine = `which wine`;
  1736. chomp $which_wine;
  1737. if ($which_wine ne "")
  1738. {
  1739. $wine_ver = `$which_wine --version`;
  1740. chomp $wine_ver;
  1741. $wine_ver = "|".$wine_ver;
  1742. }
  1743. $os = $os_name." ".$os_arch.$wine_ver;
  1744. logger "OS is $os";
  1745. return "$os";
  1746. }
  1747. else
  1748. {
  1749. logger "Cannot determine OS..that is odd";
  1750. return "Unknown";
  1751. }
  1752. }
  1753.  
  1754. ### @return PID of the download process if started succesfully.
  1755. ### @return -1 If could not create temporary download directory.
  1756. ### @return -2 If could not create destination directory.
  1757. ### @return -3 If resources unavailable.
  1758. sub start_file_download
  1759. {
  1760. return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
  1761. my ($url, $destination, $filename, $action, $post_script) = &decrypt_params(@_);
  1762. logger
  1763. "Starting to download URL $url. Destination: $destination - Filename: $filename";
  1764.  
  1765. if (!-e $destination)
  1766. {
  1767. logger "Creating destination directory.";
  1768. if (!mkpath $destination )
  1769. {
  1770. logger "Could not create destination '$destination' directory : $!";
  1771. return -2;
  1772. }
  1773. }
  1774.  
  1775. my $download_file_path = Path::Class::File->new($destination, "$filename");
  1776.  
  1777. my $pid = fork();
  1778. if (not defined $pid)
  1779. {
  1780. logger "Could not allocate resources for download.";
  1781. return -3;
  1782. }
  1783.  
  1784. # Only the forked child goes here.
  1785. elsif ($pid == 0)
  1786. {
  1787. my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0,
  1788. SSL_verify_mode => 0x00 } );
  1789. $ua->agent('Mozilla/5.0');
  1790. my $response = $ua->get($url, ':content_file' => "$download_file_path");
  1791.  
  1792. if ($response->is_success)
  1793. {
  1794. logger "Successfully fetched $url and stored it to $download_file_path. Retval: ".$response->status_line;
Add Comment
Please, Sign In to add comment