Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/perl
- #
- # OGP - Open Game Panel
- # Copyright (C) 2008 - 2018 The OGP Development Team
- #
- # http://www.opengamepanel.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 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.
- #
- use warnings;
- use strict;
- use Cwd; # Fast way to get the current directory
- use lib getcwd();
- use Frontier::Daemon::OGP::Forking; # Forking XML-RPC server
- use File::Copy; # Simple file copy functions
- use File::Copy::Recursive
- qw(fcopy rcopy dircopy fmove rmove dirmove pathempty pathrmdir)
- ; # Used to copy whole directories
- use File::Basename; # Used to get the file name or the directory name from a given path
- use Crypt::XXTEA; # Encryption between webpages and agent.
- use Cfg::Config; # Config file
- use Cfg::Preferences; # Preferences file
- use Fcntl ':flock'; # Import LOCK_* constants for file locking
- use LWP::UserAgent; # Used for fetching URLs
- use MIME::Base64; # Used to ensure data travelling right through the network.
- use Getopt::Long; # Used for command line params.
- use Path::Class::File; # Used to handle files and directories.
- use File::Path qw(mkpath);
- use Archive::Extract; # Used to handle archived files.
- use File::Find;
- use Schedule::Cron; # Used for scheduling tasks
- # Compression tools
- use IO::Compress::Bzip2 qw(bzip2 $Bzip2Error); # Used to compress files to bz2.
- use Compress::Zlib; # Used to compress file download buffers to zlib.
- use Archive::Tar; # Used to create tar, tgz or tbz archives.
- use Archive::Zip qw( :ERROR_CODES :CONSTANTS ); # Used to create zip archives.
- # Current location of the agent.
- use constant AGENT_RUN_DIR => getcwd();
- # Load our config file values
- use constant AGENT_KEY => $Cfg::Config{key};
- use constant AGENT_IP => $Cfg::Config{listen_ip};
- use constant AGENT_LOG_FILE => $Cfg::Config{logfile};
- use constant AGENT_PORT => $Cfg::Config{listen_port};
- use constant AGENT_VERSION => $Cfg::Config{version};
- use constant WEB_ADMIN_API_KEY => $Cfg::Config{web_admin_api_key};
- use constant WEB_API_URL => $Cfg::Config{web_api_url};
- use constant STEAM_DL_LIMIT => $Cfg::Config{steam_dl_limit};
- use constant SCREEN_LOG_LOCAL => $Cfg::Preferences{screen_log_local};
- use constant DELETE_LOGS_AFTER => $Cfg::Preferences{delete_logs_after};
- use constant AGENT_PID_FILE =>
- Path::Class::File->new(AGENT_RUN_DIR, 'ogp_agent.pid');
- use constant AGENT_RSYNC_GENERIC_LOG =>
- Path::Class::File->new(AGENT_RUN_DIR, 'rsync_update_generic.log');
- use constant STEAM_LICENSE_OK => "Accept";
- use constant STEAM_LICENSE => $Cfg::Config{steam_license};
- use constant MANUAL_TMP_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'tmp');
- use constant SHARED_GAME_TMP_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'shared');
- use constant STEAMCMD_CLIENT_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'steamcmd');
- use constant STEAMCMD_CLIENT_BIN =>
- Path::Class::File->new(STEAMCMD_CLIENT_DIR, 'steamcmd.sh');
- use constant SCREEN_LOGS_DIR =>
- Path::Class::Dir->new(AGENT_RUN_DIR, 'screenlogs');
- use constant GAME_STARTUP_DIR =>
- Path::Class::Dir->new(AGENT_RUN_DIR, 'startups');
- use constant SCREENRC_FILE =>
- Path::Class::File->new(AGENT_RUN_DIR, 'ogp_screenrc');
- use constant SCREENRC_FILE_BK =>
- Path::Class::File->new(AGENT_RUN_DIR, 'ogp_screenrc_bk');
- use constant SCREENRC_TMP_FILE =>
- Path::Class::File->new(AGENT_RUN_DIR, 'ogp_screenrc.tmp');
- use constant SCREEN_TYPE_HOME => "HOME";
- use constant SCREEN_TYPE_UPDATE => "UPDATE";
- use constant FD_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'FastDownload');
- use constant FD_ALIASES_DIR => Path::Class::Dir->new(FD_DIR, 'aliases');
- use constant FD_PID_FILE => Path::Class::File->new(FD_DIR, 'fd.pid');
- use constant SCHED_PID => Path::Class::File->new(AGENT_RUN_DIR, 'scheduler.pid');
- use constant SCHED_TASKS => Path::Class::File->new(AGENT_RUN_DIR, 'scheduler.tasks');
- use constant SCHED_LOG_FILE => Path::Class::File->new(AGENT_RUN_DIR, 'scheduler.log');
- $Cfg::Config{sudo_password} =~ s/('+)/'\"$1\"'/g;
- our $SUDOPASSWD = $Cfg::Config{sudo_password};
- my $no_startups = 0;
- my $clear_startups = 0;
- our $log_std_out = 0;
- GetOptions(
- 'no-startups' => \$no_startups,
- 'clear-startups' => \$clear_startups,
- 'log-stdout' => \$log_std_out
- );
- # Starting the agent as root user is not supported anymore.
- if ($< == 0)
- {
- print "ERROR: You are trying to start the agent as root user.";
- print "This is not currently supported. If you wish to start the";
- print "you need to create a normal user account for it.";
- exit 1;
- }
- ### Logger function.
- ### @param line the line that is put to the log file.
- sub logger
- {
- my $logcmd = $_[0];
- my $also_print = 0;
- if (@_ == 2)
- {
- ($also_print) = $_[1];
- }
- $logcmd = localtime() . " $logcmd\n";
- if ($log_std_out == 1)
- {
- print "$logcmd";
- return;
- }
- if ($also_print == 1)
- {
- print "$logcmd";
- }
- open(LOGFILE, '>>', AGENT_LOG_FILE)
- or die("Can't open " . AGENT_LOG_FILE . " - $!");
- flock(LOGFILE, LOCK_EX) or die("Failed to lock log file.");
- seek(LOGFILE, 0, 2) or die("Failed to seek to end of file.");
- print LOGFILE "$logcmd" or die("Failed to write to log file.");
- flock(LOGFILE, LOCK_UN) or die("Failed to unlock log file.");
- close(LOGFILE) or die("Failed to close log file.");
- }
- # Rotate the log file
- if (-e AGENT_LOG_FILE)
- {
- if (-e AGENT_LOG_FILE . ".bak")
- {
- unlink(AGENT_LOG_FILE . ".bak");
- }
- logger "Rotating log file";
- move(AGENT_LOG_FILE, AGENT_LOG_FILE . ".bak");
- logger "New log file created";
- }
- # If for some reason the screenrc file doesn't exist, restore it from the backup copy
- # I've seen this happen a few times
- if (! -e SCREENRC_FILE)
- {
- copy(SCREENRC_FILE_BK,SCREENRC_FILE);
- }
- open INPUTFILE, "<", SCREENRC_FILE or die $!;
- open OUTPUTFILE, ">", SCREENRC_TMP_FILE or die $!;
- my $dest = SCREEN_LOGS_DIR . "/screenlog.%t";
- while (<INPUTFILE>)
- {
- $_ =~ s/logfile.*/logfile $dest/g;
- print OUTPUTFILE $_;
- }
- close INPUTFILE;
- close OUTPUTFILE;
- unlink SCREENRC_FILE;
- move(SCREENRC_TMP_FILE,SCREENRC_FILE);
- # Check the screen logs folder
- if (!-d SCREEN_LOGS_DIR && !mkdir SCREEN_LOGS_DIR)
- {
- logger "Could not create " . SCREEN_LOGS_DIR . " directory $!.", 1;
- exit -1;
- }
- # Check the global shared games folder
- if (!-d SHARED_GAME_TMP_DIR && !mkdir SHARED_GAME_TMP_DIR)
- {
- logger "Could not create " . SHARED_GAME_TMP_DIR . " directory $!.", 1;
- exit -1;
- }
- if (check_steam_cmd_client() == -1)
- {
- print "ERROR: You must download and uncompress the new steamcmd package.";
- print "BE SURE TO INSTALL IT IN " . AGENT_RUN_DIR . "/steamcmd directory,";
- print "so it can be managed by the agent to install servers.";
- exit 1;
- }
- # create the directory for startup flags
- if (!-e GAME_STARTUP_DIR)
- {
- logger "Creating the startups directory " . GAME_STARTUP_DIR . "";
- if (!mkdir GAME_STARTUP_DIR)
- {
- my $message =
- "Failed to create the "
- . GAME_STARTUP_DIR
- . " directory - check permissions. Errno: $!";
- logger $message, 1;
- exit 1;
- }
- }
- elsif ($clear_startups)
- {
- opendir(STARTUPDIR, GAME_STARTUP_DIR);
- while (my $startup_file = readdir(STARTUPDIR))
- {
- # Skip . and ..
- next if $startup_file =~ /^\./;
- $startup_file = Path::Class::File->new(GAME_STARTUP_DIR, $startup_file);
- logger "Removing " . $startup_file . ".";
- unlink($startup_file);
- }
- closedir(STARTUPDIR);
- }
- # If the directory already existed check if we need to start some games.
- elsif ($no_startups != 1)
- {
- # Loop through all the startup flags, and call universal startup
- opendir(STARTUPDIR, GAME_STARTUP_DIR);
- logger "Reading startup flags from " . GAME_STARTUP_DIR . "";
- while (my $dirlist = readdir(STARTUPDIR))
- {
- # Skip . and ..
- next if $dirlist =~ /^\./;
- logger "Found $dirlist";
- open(STARTFILE, '<', Path::Class::Dir->new(GAME_STARTUP_DIR, $dirlist))
- || logger "Error opening start flag $!";
- while (<STARTFILE>)
- {
- my (
- $home_id, $home_path, $server_exe,
- $run_dir, $startup_cmd, $server_port,
- $server_ip, $cpu, $nice, $preStart, $envVars, $game_key, $console_log
- ) = split(',', $_);
- if (is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) ==
- 1)
- {
- logger
- "This server ($server_exe on $server_ip : $server_port) is already running (ID: $home_id).";
- next;
- }
- logger "Starting server_exe $server_exe from home $home_path.";
- universal_start_without_decrypt(
- $home_id, $home_path, $server_exe,
- $run_dir, $startup_cmd, $server_port,
- $server_ip, $cpu, $nice, $preStart, $envVars, $game_key, $console_log
- );
- }
- close(STARTFILE);
- }
- closedir(STARTUPDIR);
- }
- # Create the pid file
- open(PID, '>', AGENT_PID_FILE)
- or die("Can't write to pid file - " . AGENT_PID_FILE . "\n");
- print PID "$$\n";
- close(PID);
- logger "Open Game Panel - Agent started - "
- . AGENT_VERSION
- . " - port "
- . AGENT_PORT
- . " - PID $$", 1;
- # Stop previous scheduler process if exists
- scheduler_stop();
- # Create new object with default dispatcher for scheduled tasks
- my $cron = new Schedule::Cron( \&scheduler_dispatcher, {
- nofork => 1,
- loglevel => 0,
- log => sub { print $_[1], "\n"; }
- } );
- $cron->add_entry( "* * * * * *", \&scheduler_read_tasks );
- # Run scheduler
- $cron->run( {detach=>1, pid_file=>SCHED_PID} );
- if(-e Path::Class::File->new(FD_DIR, 'Settings.pm'))
- {
- require "FastDownload/Settings.pm"; # Settings for Fast Download Daemon.
- if(defined($FastDownload::Settings{autostart_on_agent_startup}) && $FastDownload::Settings{autostart_on_agent_startup} eq "1")
- {
- start_fastdl();
- }
- }
- my $d = Frontier::Daemon::OGP::Forking->new(
- methods => {
- is_screen_running => \&is_screen_running,
- universal_start => \&universal_start,
- renice_process => \&renice_process,
- cpu_count => \&cpu_count,
- rfile_exists => \&rfile_exists,
- quick_chk => \&quick_chk,
- steam_cmd => \&steam_cmd,
- fetch_steam_version => \&fetch_steam_version,
- installed_steam_version => \&installed_steam_version,
- automatic_steam_update => \&automatic_steam_update,
- get_log => \&get_log,
- stop_server => \&stop_server,
- send_rcon_command => \&send_rcon_command,
- dirlist => \&dirlist,
- dirlistfm => \&dirlistfm,
- readfile => \&readfile,
- writefile => \&writefile,
- rebootnow => \&rebootnow,
- what_os => \&what_os,
- start_file_download => \&start_file_download,
- lock_additional_files => \&lock_additional_files,
- is_file_download_in_progress => \&is_file_download_in_progress,
- uncompress_file => \&uncompress_file,
- discover_ips => \&discover_ips,
- mon_stats => \&mon_stats,
- exec => \&exec,
- clone_home => \&clone_home,
- remove_home => \&remove_home,
- start_rsync_install => \&start_rsync_install,
- rsync_progress => \&rsync_progress,
- restart_server => \&restart_server,
- sudo_exec => \&sudo_exec,
- master_server_update => \&master_server_update,
- secure_path => \&secure_path,
- get_chattr => \&get_chattr,
- ftp_mgr => \&ftp_mgr,
- compress_files => \&compress_files,
- stop_fastdl => \&stop_fastdl,
- restart_fastdl => \&restart_fastdl,
- fastdl_status => \&fastdl_status,
- fastdl_get_aliases => \&fastdl_get_aliases,
- fastdl_add_alias => \&fastdl_add_alias,
- fastdl_del_alias => \&fastdl_del_alias,
- fastdl_get_info => \&fastdl_get_info,
- fastdl_create_config => \&fastdl_create_config,
- agent_restart => \&agent_restart,
- scheduler_add_task => \&scheduler_add_task,
- scheduler_del_task => \&scheduler_del_task,
- scheduler_list_tasks => \&scheduler_list_tasks,
- scheduler_edit_task => \&scheduler_edit_task,
- get_file_part => \&get_file_part,
- stop_update => \&stop_update,
- shell_action => \&shell_action,
- remote_query => \&remote_query,
- send_steam_guard_code => \&send_steam_guard_code,
- steam_workshop => \&steam_workshop,
- get_workshop_mods_info => \&get_workshop_mods_info
- },
- debug => 4,
- LocalPort => AGENT_PORT,
- LocalAddr => AGENT_IP,
- ReuseAddr => '1'
- ) or die "Couldn't start OGP Agent: $!";
- sub backup_home_log
- {
- my ($home_id, $log_file, $console_log_file) = @_;
- my $home_backup_dir = SCREEN_LOGS_DIR . "/home_id_" . $home_id;
- if( ! -e $home_backup_dir )
- {
- if( ! mkdir $home_backup_dir )
- {
- logger "Can not create a backup directory at $home_backup_dir.";
- return 1;
- }
- }
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
- my $backup_file_name = $mday . $mon . $year . '_' . $hour . 'h' . $min . 'm' . $sec . "s.log";
- my $output_path = $home_backup_dir . "/" . $backup_file_name;
- # Used for deleting log files older than DELETE_LOGS_AFTER
- my @file_list;
- my @find_dirs; # directories to search
- my $now = time(); # get current time
- my $days;
- if((DELETE_LOGS_AFTER =~ /^[+-]?\d+$/) && (DELETE_LOGS_AFTER > 0)){
- $days = DELETE_LOGS_AFTER; # how many days old
- }else{
- $days = 30; # how many days old
- }
- my $seconds_per_day = 60*60*24; # seconds in a day
- my $AGE = $days*$seconds_per_day; # age in seconds
- push (@find_dirs, $home_backup_dir);
- # Create local copy of log file backup in the log_backups folder and current user home directory if SCREEN_LOG_LOCAL = 1
- if(SCREEN_LOG_LOCAL == 1)
- {
- # Create local backups folder
- my $local_log_folder = Path::Class::Dir->new("logs_backup");
- if(!-e $local_log_folder){
- mkdir($local_log_folder);
- }
- # Add full path to @find_dirs so that log files older than DELETE_LOGS_AFTER are deleted
- my $fullpath_to_local_logs = Path::Class::Dir->new(getcwd(), "logs_backup");
- push (@find_dirs, $fullpath_to_local_logs);
- my $log_local = $local_log_folder . "/" . $backup_file_name;
- # Delete the local log file if it already exists
- if(-e $log_local){
- unlink $log_local;
- }
- # If the log file contains UPDATE in the filename, do not allow users to see it since it will contain steam credentials
- # Will return -1 for not existing
- my $isUpdate = index($log_file,SCREEN_TYPE_UPDATE);
- if($isUpdate == -1){
- copy($log_file,$log_local);
- }
- }
- # Delete all files in @find_dirs older than DELETE_LOGS_AFTER days
- find ( sub {
- my $file = $File::Find::name;
- if ( -f $file ) {
- push (@file_list, $file);
- }
- }, @find_dirs);
- # Include the custom console path - and also do a size check on it
- if(defined $console_log_file && $console_log_file ne ""){
- my $path_to_console_file = $console_log_file;
- if( -f $path_to_console_file){
- push (@file_list, $path_to_console_file);
- # Backup and delete this specific file as well if it's over 200MB
- my @stats = stat($path_to_console_file);
- if($stats[7] >= 209715200){
- if(SCREEN_LOG_LOCAL == 1){
- # Copy it to local log folder as well
- my $local_log_folder = Path::Class::Dir->new("logs_backup");
- my $log_local = $local_log_folder . "/" . $backup_file_name . "_console_log";
- copy($path_to_console_file, $log_local);
- }
- # Copy it to the main log folder as well
- move($path_to_console_file,$output_path . "_console_log");
- }
- }
- }
- for my $file (@file_list) {
- if( -f $file ){
- my @stats = stat($file);
- if ($now-$stats[9] > $AGE) {
- unlink $file;
- }
- }
- }
- move($log_file,$output_path);
- return 0;
- }
- sub get_home_pids
- {
- my ($home_id) = @_;
- my $screen_id = create_screen_id(SCREEN_TYPE_HOME, $home_id);
- my ($pid, @pids);
- ($pid) = split(/\./, `screen -ls | grep -E -o "[0-9]+\.$screen_id"`, 2);
- if(defined $pid)
- {
- chomp($pid);
- while ($pid =~ /^[0-9]+$/)
- {
- push(@pids,$pid);
- $pid = `pgrep -P $pid`;
- chomp($pid);
- }
- }
- return @pids;
- }
- sub create_screen_id
- {
- my ($screen_type, $home_id) = @_;
- return sprintf("OGP_%s_%09d", $screen_type, $home_id);
- }
- sub create_screen_cmd
- {
- my ($screen_id, $exec_cmd) = @_;
- $exec_cmd = replace_OGP_Env_Vars($screen_id, "", "", $exec_cmd);
- return
- sprintf('export WINEDEBUG="fixme-all" && export DISPLAY=:1 && screen -d -m -t "%1$s" -c ' . SCREENRC_FILE . ' -S %1$s %2$s',
- $screen_id, $exec_cmd);
- }
- sub create_screen_cmd_loop
- {
- my ($screen_id, $exec_cmd, $envVars, $skipLoop) = @_;
- my $server_start_bashfile = $screen_id . "_startup_scr.sh";
- $exec_cmd = replace_OGP_Env_Vars($screen_id, "", "", $exec_cmd);
- # Allow file to be overwritten
- if(-e $server_start_bashfile){
- secure_path_without_decrypt('chattr-i', $server_start_bashfile);
- }
- # Create bash file that screen will run which spawns the server
- # If it crashes without user intervention, it will restart
- open (SERV_START_SCRIPT, '>', $server_start_bashfile);
- my $respawn_server_command = "#!/bin/bash" . "\n";
- if(!$skipLoop){
- $respawn_server_command .= "function startServer(){" . "\n";
- }
- if(defined $envVars && $envVars ne ""){
- $respawn_server_command .= $envVars;
- }
- if(!$skipLoop){
- $respawn_server_command .= "NUMSECONDS=`expr \$(date +%s)`" . "\n"
- . "until " . $exec_cmd . "; do" . "\n"
- . "let DIFF=(`date +%s` - \"\$NUMSECONDS\")" . "\n"
- . "if [ \"\$DIFF\" -gt 15 ]; then" . "\n"
- . "NUMSECONDS=`expr \$(date +%s)`" . "\n"
- . "echo \"Server '" . $exec_cmd . "' crashed with exit code \$?. Respawning...\" >&2 " . "\n"
- . "fi" . "\n"
- . "sleep 3" . "\n"
- . "done" . "\n"
- . "let DIFF=(`date +%s` - \"\$NUMSECONDS\")" . "\n"
- . "if [ ! -e \"SERVER_STOPPED\" ] && [ \"\$DIFF\" -gt 15 ]; then" . "\n"
- . "startServer" . "\n"
- . "fi" . "\n"
- . "}" . "\n"
- . "startServer" . "\n";
- }else{
- $respawn_server_command .= $exec_cmd . "\n";
- }
- print SERV_START_SCRIPT $respawn_server_command;
- close (SERV_START_SCRIPT);
- # Secure file
- secure_path_without_decrypt('chattr+i', $server_start_bashfile);
- my $screen_exec_script = "bash " . $server_start_bashfile;
- return
- sprintf('export WINEDEBUG="fixme-all" && export DISPLAY=:1 && screen -d -m -t "%1$s" -c ' . SCREENRC_FILE . ' -S %1$s %2$s',
- $screen_id, $screen_exec_script);
- }
- sub handle_lock_command_line{
- my ($command) = @_;
- if(defined $command && $command ne ""){
- if ($command =~ m/{OGP_LOCK_FILE}/) {
- $command =~ s/{OGP_LOCK_FILE}\s*//g;
- return secure_path_without_decrypt("chattr+i", $command);
- }
- }
- return 0;
- }
- sub replace_OGP_Env_Vars{
- # This function replaces constants from environment variables set in the XML
- my ($screen_id, $homeid, $homepath, $exec_cmd, $game_key) = @_;
- # Handle steam specific replacements
- if(defined $screen_id && $screen_id ne ""){
- my $screen_id_for_txt_update = substr ($screen_id, rindex($screen_id, '_') + 1);
- my $steamInsFile = $screen_id_for_txt_update . "_install.txt";
- my $steamCMDPath = STEAMCMD_CLIENT_DIR;
- my $fullPath = Path::Class::File->new($steamCMDPath, $steamInsFile);
- # If the install file exists, the game can be auto updated, else it will be ignored by the game for improper syntax
- # To generate the install file, the "Install/Update via Steam" button must be clicked on at least once!
- if(-e $fullPath){
- $exec_cmd =~ s/{OGP_STEAM_CMD_DIR}/$steamCMDPath/g;
- $exec_cmd =~ s/{STEAMCMD_INSTALL_FILE}/$steamInsFile/g;
- }
- }
- # Handle home directory replacement
- if(defined $homepath && $homepath ne ""){
- $exec_cmd =~ s/{OGP_HOME_DIR}/$homepath/g;
- }
- # Handle global game shared directory replacement
- if(defined $game_key && $game_key ne ""){
- my $readable_game_key = lc(substr($game_key, 0, rindex($game_key,"_")));
- my $shared_path = Path::Class::Dir->new(SHARED_GAME_TMP_DIR, $readable_game_key);
- # Create the folder if it doesn't exist
- if (!-d $shared_path && !mkdir $shared_path)
- {
- logger "Could not create " . $shared_path . " directory $!.", 1;
- }
- $exec_cmd =~ s/{OGP_GAME_SHARED_DIR}/$shared_path/g;
- }
- return $exec_cmd;
- }
- sub encode_list
- {
- my $encoded_content = '';
- if(@_)
- {
- foreach my $line (@_)
- {
- $encoded_content .= encode_base64($line, "") . '\n';
- }
- }
- return $encoded_content;
- }
- sub decrypt_param
- {
- my ($param) = @_;
- $param = decode_base64($param);
- $param = Crypt::XXTEA::decrypt($param, AGENT_KEY);
- $param = decode_base64($param);
- return $param;
- }
- sub decrypt_params
- {
- my @params;
- foreach my $param (@_)
- {
- $param = &decrypt_param($param);
- push(@params, $param);
- }
- return @params;
- }
- sub check_steam_cmd_client
- {
- if (STEAM_LICENSE ne STEAM_LICENSE_OK)
- {
- logger "Steam license not accepted, stopping Steam client check.";
- return 0;
- }
- if (!-d STEAMCMD_CLIENT_DIR && !mkdir STEAMCMD_CLIENT_DIR)
- {
- logger "Could not create " . STEAMCMD_CLIENT_DIR . " directory $!.", 1;
- exit -1;
- }
- if (!-w STEAMCMD_CLIENT_DIR)
- {
- logger "Steam client dir '"
- . STEAMCMD_CLIENT_DIR
- . "' not writable. Unable to get Steam client.";
- return -1;
- }
- if (!-f STEAMCMD_CLIENT_BIN)
- {
- logger "The Steam client, steamcmd, does not exist yet, installing...";
- my $steam_client_file = 'steamcmd_linux.tar.gz';
- my $steam_client_path = Path::Class::File->new(STEAMCMD_CLIENT_DIR, $steam_client_file);
- my $steam_client_url =
- "http://media.steampowered.com/client/" . $steam_client_file;
- logger "Downloading the Steam client from $steam_client_url to '"
- . $steam_client_path . "'.";
- my $ua = LWP::UserAgent->new;
- $ua->agent('Mozilla/5.0');
- my $response = $ua->get($steam_client_url, ':content_file' => "$steam_client_path");
- unless ($response->is_success)
- {
- logger "Failed to download steam installer from "
- . $steam_client_url
- . ".", 1;
- return -1;
- }
- if (-f $steam_client_path)
- {
- logger "Uncompressing $steam_client_path";
- if ( uncompress_file_without_decrypt($steam_client_path, STEAMCMD_CLIENT_DIR) != 1 )
- {
- unlink($steam_client_path);
- logger "Unable to uncompress $steam_client_path, the file has been removed.";
- return -1;
- }
- unlink($steam_client_path);
- }
- }
- if (!-x STEAMCMD_CLIENT_BIN)
- {
- if ( ! chmod 0755, STEAMCMD_CLIENT_BIN )
- {
- logger "Unable to apply execution permission to ".STEAMCMD_CLIENT_BIN.".";
- }
- }
- return 1;
- }
- sub is_screen_running
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- my ($screen_type, $home_id) = decrypt_params(@_);
- return is_screen_running_without_decrypt($screen_type, $home_id);
- }
- sub is_screen_running_without_decrypt
- {
- my ($screen_type, $home_id) = @_;
- my $screen_id = create_screen_id($screen_type, $home_id);
- my $is_running = `screen -list | grep $screen_id`;
- if ($is_running =~ /^\s*$/)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- # Delete Server Stopped Status File:
- sub deleteStoppedStatFile
- {
- my ($home_path) = @_;
- my $server_stop_status_file = Path::Class::File->new($home_path, "SERVER_STOPPED");
- if(-e $server_stop_status_file)
- {
- unlink $server_stop_status_file;
- }
- }
- # Universal startup function
- sub universal_start
- {
- chomp(@_);
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- return universal_start_without_decrypt(decrypt_params(@_));
- }
- # Split to two parts because of internal calls.
- sub universal_start_without_decrypt
- {
- my (
- $home_id, $home_path, $server_exe, $run_dir,
- $startup_cmd, $server_port, $server_ip, $cpu, $nice, $preStart, $envVars, $game_key, $console_log
- ) = @_;
- if (is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 1)
- {
- logger "This server is already running (ID: $home_id).";
- return -14;
- }
- if (!-e $home_path)
- {
- logger "Can't find server's install path [ $home_path ].";
- return -10;
- }
- my $uid = `id -u`;
- chomp $uid;
- my $gid = `id -g`;
- chomp $gid;
- my $path = $home_path;
- $path =~ s/('+)/'\"$1\"'/g;
- sudo_exec_without_decrypt('chown -Rf '.$uid.':'.$gid.' \''.$path.'\'');
- # Some game require that we are in the directory where the binary is.
- my $game_binary_dir = Path::Class::Dir->new($home_path, $run_dir);
- if ( -e $game_binary_dir && !chdir $game_binary_dir)
- {
- logger "Could not change to server binary directory $game_binary_dir.";
- return -12;
- }
- secure_path_without_decrypt('chattr-i', $server_exe);
- if (!-x $server_exe)
- {
- if (!chmod 0755, $server_exe)
- {
- logger "The $server_exe file is not executable.";
- return -13;
- }
- }
- if(defined $preStart && $preStart ne ""){
- # Get it in the format that the startup file can use
- $preStart = multiline_to_startup_comma_format($preStart);
- }else{
- $preStart = "";
- }
- if(defined $envVars && $envVars ne ""){
- # Replace variables in the envvars if they exist
- my @prestartenvvars = split /[\r\n]+/, $envVars;
- my $envVarStr = "";
- foreach my $line (@prestartenvvars) {
- $line = replace_OGP_Env_Vars("", $home_id, $home_path, $line, $game_key);
- if($line ne ""){
- logger "Configuring environment variable: $line";
- $envVarStr .= "$line\n";
- }
- }
- if(defined $envVarStr && $envVarStr ne ""){
- $envVars = $envVarStr;
- }
- # Get it in the format that the startup file can use
- $envVars = multiline_to_startup_comma_format($envVars);
- }else{
- $envVars = "";
- }
- secure_path_without_decrypt('chattr+i', $server_exe);
- # Create startup file for the server.
- my $startup_file =
- Path::Class::File->new(GAME_STARTUP_DIR, "$server_ip-$server_port");
- if (open(STARTUP, '>', $startup_file))
- {
- print STARTUP
- "$home_id,$home_path,$server_exe,$run_dir,$startup_cmd,$server_port,$server_ip,$cpu,$nice,$preStart,$envVars,$game_key,$console_log";
- logger "Created startup flag for $server_ip-$server_port";
- close(STARTUP);
- }
- else
- {
- logger "Cannot create file in " . $startup_file . " : $!";
- }
- if(defined $preStart && $preStart ne ""){
- # Get it in the format that the startup file can use
- $preStart = startup_comma_format_to_multiline($preStart);
- }else{
- $preStart = "";
- }
- if(defined $envVars && $envVars ne ""){
- # Get it in the format that the startup file can use
- $envVars = startup_comma_format_to_multiline($envVars);
- }else{
- $envVars = "";
- }
- # Create the startup string.
- my $screen_id = create_screen_id(SCREEN_TYPE_HOME, $home_id);
- my $file_extension = substr $server_exe, -4;
- my $cli_bin;
- my $command;
- my $run_before_start;
- # Replace any OGP variables found in the command line
- $startup_cmd = replace_OGP_Env_Vars($screen_id, $home_id, $home_path, $startup_cmd, $game_key);
- if($file_extension eq ".exe" or $file_extension eq ".bat")
- {
- $command = "wine $server_exe $startup_cmd";
- if ($cpu ne 'NA')
- {
- $command = "taskset -c $cpu wine $server_exe $startup_cmd";
- }
- if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
- deleteStoppedStatFile($home_path);
- $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars);
- }else{
- $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1);
- }
- }
- elsif($file_extension eq ".jar")
- {
- $command = "$startup_cmd";
- if ($cpu ne 'NA')
- {
- $command = "taskset -c $cpu $startup_cmd";
- }
- if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
- deleteStoppedStatFile($home_path);
- $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars);
- }else{
- $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1);
- }
- }
- else
- {
- $command = "./$server_exe $startup_cmd";
- if ($cpu ne 'NA')
- {
- $command = "taskset -c $cpu ./$server_exe $startup_cmd";
- }
- if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
- deleteStoppedStatFile($home_path);
- $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars);
- }else{
- $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1);
- }
- }
- my $log_file = Path::Class::File->new(SCREEN_LOGS_DIR, "screenlog.$screen_id");
- backup_home_log( $home_id, $log_file, $home_path . "/" . $console_log );
- logger
- "Startup command [ $cli_bin ] will be executed in dir $game_binary_dir.";
- # Run before start script
- $run_before_start = run_before_start_commands($home_id, $home_path, $preStart);
- system($cli_bin);
- sleep(1);
- renice_process_without_decrypt($home_id, $nice);
- chdir AGENT_RUN_DIR;
- return 1;
- }
- # This is used to change the priority of process
- # @return 1 if successfully set prosess priority
- # @return -1 in case of an error.
- sub renice_process
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- return renice_process_without_decrypt(decrypt_params(@_));
- }
- sub renice_process_without_decrypt
- {
- my ($home_id, $nice) = @_;
- if ($nice != 0)
- {
- my @pids = get_home_pids($home_id);
- logger
- "Renicing pids [ @pids ] from home_id $home_id with nice value $nice.";
- foreach my $pid (@pids)
- {
- my $rpid = kill 0, $pid;
- if ($rpid == 1)
- {
- my $ret = sudo_exec_without_decrypt('/usr/bin/renice '.$nice.' '.$pid);
- ($ret) = split(/;/, $ret, 2);
- if($ret != 1)
- {
- logger "Unable to renice process, probably bad sudo password or not in sudoers list.";
- return -1
- }
- }
- }
- }
- return 1;
- }
- # This is used to force a process to run on a particular CPU
- sub force_cpu
- {
- return force_cpu_without_decrypt(decrypt_params(@_));
- }
- sub force_cpu_without_decrypt
- {
- my ($home_id, $cpu) = @_;
- if ($cpu ne 'NA')
- {
- my @pids = get_home_pids($home_id);
- logger
- "Setting server from home_id $home_id with pids @pids to run on CPU $cpu.";
- foreach my $pid (@pids)
- {
- my $rpid = kill 0, $pid;
- if ($rpid == 1)
- {
- my $ret = sudo_exec_without_decrypt('/usr/bin/taskset -pc '.$cpu.' '.$pid);
- ($ret) = split(/;/, $ret, 2);
- if($ret != 1)
- {
- logger "Unable to set cpu, probably a bad sudo password or not in sudoers list.";
- return -1
- }
- }
- }
- }
- return 1;
- }
- # Returns the number of CPUs available.
- sub cpu_count
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- if (!-e "/proc/cpuinfo")
- {
- return "ERROR - Missing /proc/cpuinfo";
- }
- open(CPUINFO, '<', "/proc/cpuinfo")
- or return "ERROR - Cannot open /proc/cpuinfo";
- my $cpu_count = 0;
- while (<CPUINFO>)
- {
- chomp;
- next if $_ !~ /^processor/;
- $cpu_count++;
- }
- close(CPUINFO);
- return "$cpu_count";
- }
- ### File exists check ####
- # Simple a way to check if a file exists using the remote agent
- #
- # @return 0 when file exists.
- # @return 1 when file does not exist.
- sub rfile_exists
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- chdir AGENT_RUN_DIR;
- my $checkFile = decrypt_param(@_);
- if (-e $checkFile)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- #### Quick check to verify agent is up and running
- # Used to quickly see if the agent is online, and if the keys match.
- # The message that is sent to the agent must be hello, if not then
- # it is intrepret as encryption key missmatch.
- #
- # @return 1 when encrypted message is not 'hello'
- # @return 0 when check is ok.
- sub quick_chk
- {
- my $dec_check = &decrypt_param(@_);
- if ($dec_check ne 'hello')
- {
- logger "ERROR - Encryption key mismatch! Returning 1 to asker.";
- return 1;
- }
- return 0;
- }
- ### Return -10 If home path is not found.
- ### Return -9 If log type was invalid.
- ### Return -8 If log file was not found.
- ### 0 reserved for connection problems.
- ### Return 1;content If log found and screen running.
- ### Return 2;content If log found but screen is not running.
- sub get_log
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- my ($screen_type, $home_id, $home_path, $nb_of_lines, $log_file) = decrypt_params(@_);
- if (!chdir $home_path)
- {
- logger "Can't change to server's install path [ $home_path ].";
- return -10;
- }
- if ( ($screen_type eq SCREEN_TYPE_UPDATE)
- && ($screen_type eq SCREEN_TYPE_HOME))
- {
- logger "Invalid screen type '$screen_type'.";
- return -9;
- }
- if(!$log_file)
- {
- my $screen_id = create_screen_id($screen_type, $home_id);
- $log_file = Path::Class::File->new(SCREEN_LOGS_DIR, "screenlog.$screen_id");
- }
- else
- {
- $log_file = Path::Class::File->new($home_path, $log_file);
- }
- chmod 0644, $log_file;
- # Create local copy of current log file if SCREEN_LOG_LOCAL = 1
- if(SCREEN_LOG_LOCAL == 1)
- {
- my $log_local = Path::Class::File->new($home_path, "LOG_$screen_type.txt");
- if ( -e $log_local )
- {
- unlink $log_local;
- }
- # Copy log file only if it's not an UPDATE type as it may contain steam credentials
- if($screen_type eq SCREEN_TYPE_HOME){
- copy($log_file, $log_local);
- }
- }
- # Regenerate the log file if it doesn't exist
- unless ( -e $log_file )
- {
- if (open(NEWLOG, '>', $log_file))
- {
- logger "Log file missing, regenerating: " . $log_file;
- print NEWLOG "Log file missing, started new log\n";
- close(NEWLOG);
- }
- else
- {
- logger "Cannot regenerate log file in " . $log_file . " : $!";
- return -8;
- }
- }
- # Return a few lines of output to the web browser
- my(@modedlines) = `tail -n $nb_of_lines $log_file`;
- my $linecount = 0;
- foreach my $line (@modedlines) {
- #Text replacements to remove the Steam user login from steamcmd logs for security reasons.
- $line =~ s/login .*//g;
- $line =~ s/Logging .*//g;
- $line =~ s/set_steam_guard_code.*//g;
- $line =~ s/force_install_dir.*//g;
- #Text replacements to remove empty lines.
- $line =~ s/^ +//g;
- $line =~ s/^\t+//g;
- $line =~ s/^\e+//g;
- #Remove � from console output when master server update is running.
- $line =~ s/�//g;
- $modedlines[$linecount]=$line;
- $linecount++;
- }
- my $encoded_content = encode_list(@modedlines);
- chdir AGENT_RUN_DIR;
- if(is_screen_running_without_decrypt($screen_type, $home_id) == 1)
- {
- return "1;" . $encoded_content;
- }
- else
- {
- return "2;" . $encoded_content;
- }
- }
- # stop server function
- sub stop_server
- {
- chomp(@_);
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- return stop_server_without_decrypt(decrypt_params(@_));
- }
- ##### Stop server without decrypt
- ### Return 1 when error occurred on decryption.
- ### Return 0 on success
- sub stop_server_without_decrypt
- {
- my ($home_id, $server_ip, $server_port, $control_protocol,
- $control_password, $control_type, $home_path) = @_;
- my $usedProtocolToStop = 0;
- my $startup_file = Path::Class::File->new(GAME_STARTUP_DIR, "$server_ip-$server_port");
- if (-e $startup_file)
- {
- logger "Removing startup flag " . $startup_file . "";
- unlink($startup_file)
- or logger "Cannot remove the startup flag file $startup_file $!";
- }
- # Create file indicator that the game server has been stopped if defined
- if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){
- # Get current directory and chdir into the game's home dir
- my $curDir = getcwd();
- chdir $home_path;
- # Create stopped indicator file used by autorestart of OGP if server crashes
- open(STOPFILE, '>', "SERVER_STOPPED");
- close(STOPFILE);
- # Return to original directory
- chdir $curDir;
- }
- # Some validation checks for the variables.
- if ($server_ip =~ /^\s*$/ || $server_port < 0 || $server_port > 65535)
- {
- logger("Invalid IP:Port given $server_ip:$server_port.");
- return 1;
- }
- if ($control_password !~ /^\s*$/ and $control_protocol ne "")
- {
- if ($control_protocol eq "rcon")
- {
- use KKrcon::KKrcon;
- my $rcon = new KKrcon(
- Password => $control_password,
- Host => $server_ip,
- Port => $server_port,
- Type => $control_type
- );
- my $rconCommand = "quit";
- $rcon->execute($rconCommand);
- $usedProtocolToStop = 1;
- }
- elsif ($control_protocol eq "rcon2")
- {
- use KKrcon::HL2;
- my $rcon2 = new HL2(
- hostname => $server_ip,
- port => $server_port,
- password => $control_password,
- timeout => 2
- );
- my $rconCommand = "quit";
- $rcon2->run($rconCommand);
- $usedProtocolToStop = 1;
- }
- elsif ($control_protocol eq "armabe")
- {
- use ArmaBE::ArmaBE;
- my $armabe = new ArmaBE(
- hostname => $server_ip,
- port => $server_port, # Uses server port for now (Arma 2), Arma 3 BE uses a different, user definable port
- password => $control_password,
- timeout => 2
- );
- my $rconCommand = "#shutdown";
- my $armabe_result = $armabe->run($rconCommand);
- if ($armabe_result) {
- logger "ArmaBE Shutdown command sent successfully";
- $usedProtocolToStop = 1;
- }
- }
- my @server_pids;
- # 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
- if ($usedProtocolToStop == 1 && is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 1){
- @server_pids = get_home_pids($home_id);
- my $timeWaited = 0;
- my $pidSize = @server_pids;
- my $maxWaitTime = 5;
- # Maximum time to wait can now be configured as a preference
- if(defined($Cfg::Preferences{protocol_shutdown_waittime}) && $Cfg::Preferences{protocol_shutdown_waittime} =~ /^\d+?$/){
- $maxWaitTime = $Cfg::Preferences{protocol_shutdown_waittime};
- }
- while ($pidSize > 0 && $timeWaited < $maxWaitTime && is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 1) {
- select(undef, undef, undef, 0.25); # Sleeps for 250ms
- # Add to time waited
- $timeWaited += 0.25;
- # Recheck server home PIDs
- @server_pids = get_home_pids($home_id);
- $pidSize = @server_pids;
- }
- }
- if (is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 0)
- {
- logger "Stopped server $server_ip:$server_port with rcon quit.";
- return 0;
- }
- else
- {
- logger "Failed to send rcon quit. Stopping server with kill command.";
- }
- @server_pids = get_home_pids($home_id);
- my $cnt;
- foreach my $pid (@server_pids)
- {
- chomp($pid);
- $cnt = kill 15, $pid;
- if ($cnt != 1)
- {
- $cnt = kill 9, $pid;
- if ($cnt == 1)
- {
- logger "Stopped process with pid $pid successfully using kill 9.";
- }
- else
- {
- logger "Process $pid can not be stopped.";
- }
- }
- else
- {
- logger "Stopped process with pid $pid successfully using kill 15.";
- }
- }
- system('screen -wipe > /dev/null 2>&1');
- return 0;
- }
- else
- {
- logger "Remote control protocol not available or PASSWORD NOT SET. Using kill signal instead.";
- my @server_pids = get_home_pids($home_id);
- my $cnt;
- foreach my $pid (@server_pids)
- {
- chomp($pid);
- $cnt = kill 15, $pid;
- if ($cnt != 1)
- {
- $cnt = kill 9, $pid;
- if ($cnt == 1)
- {
- logger "Stopped process with pid $pid successfully using kill 9.";
- }
- else
- {
- logger "Process $pid can not be stopped.";
- }
- }
- else
- {
- logger "Stopped process with pid $pid successfully using kill 15.";
- }
- }
- system('screen -wipe > /dev/null 2>&1');
- return 0;
- }
- }
- ##### Send RCON command
- ### Return 0 when error occurred on decryption.
- ### Return 1 on success
- sub send_rcon_command
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- my ($home_id, $server_ip, $server_port, $control_protocol,
- $control_password, $control_type, $rconCommand) = decrypt_params(@_);
- # legacy console
- if ($control_protocol eq "lcon")
- {
- my $screen_id = create_screen_id(SCREEN_TYPE_HOME, $home_id);
- system('screen -S '.$screen_id.' -p 0 -X stuff "'.$rconCommand.'$(printf \\\\r)"');
- logger "Sending legacy console command to ".$screen_id.": \n$rconCommand \n .";
- if ($? == -1)
- {
- my(@modedlines) = "$rconCommand";
- my $encoded_content = encode_list(@modedlines);
- return "1;" . $encoded_content;
- }
- return 0;
- }
- # Some validation checks for the variables.
- if ($server_ip =~ /^\s*$/ || $server_port < 0 || $server_port > 65535)
- {
- logger("Invalid IP:Port given $server_ip:$server_port.");
- return 0;
- }
- if ($control_password !~ /^\s*$/)
- {
- if ($control_protocol eq "rcon")
- {
- use KKrcon::KKrcon;
- my $rcon = new KKrcon(
- Password => $control_password,
- Host => $server_ip,
- Port => $server_port,
- Type => $control_type
- );
- logger "Sending RCON command to $server_ip:$server_port: \n$rconCommand \n .";
- my(@modedlines) = $rcon->execute($rconCommand);
- my $encoded_content = encode_list(@modedlines);
- return "1;" . $encoded_content;
- }
- elsif ($control_protocol eq "rcon2")
- {
- use KKrcon::HL2;
- my $rcon2 = new HL2(
- hostname => $server_ip,
- port => $server_port,
- password => $control_password,
- timeout => 2
- );
- logger "Sending RCON command to $server_ip:$server_port: \n $rconCommand \n .";
- my(@modedlines) = $rcon2->run($rconCommand);
- my $encoded_content = encode_list(@modedlines);
- return "1;" . $encoded_content;
- }
- elsif ($control_protocol eq "armabe")
- {
- use ArmaBE::ArmaBE;
- my $armabe = new ArmaBE(
- hostname => $server_ip,
- port => $server_port, # Uses server port for now (Arma 2), Arma 3 BE uses a different, user definable port
- password => $control_password,
- timeout => 2
- );
- logger "Sending RCON command to $server_ip:$server_port: \n $rconCommand \n .";
- my(@modedlines) = $armabe->run($rconCommand);
- my $encoded_content = encode_list(@modedlines);
- return "1;" . $encoded_content;
- }
- }
- else
- {
- logger "Control protocol PASSWORD NOT SET.";
- return -10;
- }
- }
- ##### Returns a directory listing
- ### @return List of directories if everything OK.
- ### @return 0 If the directory is not found.
- ### @return -1 If cannot open the directory.
- sub dirlist
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- my ($datadir) = &decrypt_param(@_);
- logger "Asked for dirlist of $datadir directory.";
- if (!-d $datadir)
- {
- logger "ERROR - Directory [ $datadir ] not found!";
- return -1;
- }
- if (!opendir(DIR, $datadir))
- {
- logger "ERROR - Can't open $datadir: $!";
- return -2;
- }
- my @dirlist = readdir(DIR);
- closedir(DIR);
- return join(";", @dirlist);
- }
- ##### Returns a directory listing with extra info the filemanager
- ### @return List of directories if everything OK.
- ### @return 1 If the directory is empty.
- ### @return -1 If the directory is not found.
- ### @return -2 If cannot open the directory.
- sub dirlistfm
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- my $datadir = &decrypt_param(@_);
- logger "Asked for dirlist of $datadir directory.";
- if (!-d $datadir)
- {
- logger "ERROR - Directory [ $datadir ] not found!";
- return -1;
- }
- if (!opendir(DIR, $datadir))
- {
- logger "ERROR - Can't open $datadir: $!";
- return -2;
- }
- my $dir = $datadir;
- $dir =~ s/('+)/'"$1"'/g;
- my $lsattr = `lsattr '$dir' 2>/dev/null`;
- my @attr_all = split /\n+/, $lsattr;
- my %attr = ();
- my ($a, $p, @f);
- foreach (@attr_all)
- {
- ($a, $p) = split(/\s/, $_, 2);
- @f = split /\//, $p;
- $attr{$f[-1]} = $a;
- }
- my %dirfiles = ();
- my (
- $dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
- $size, $atime, $mtime, $ctime, $blksize, $blocks
- );
- my $count = 0;
- chdir($datadir);
- while (my $item = readdir(DIR))
- {
- #skip the . and .. special dirs
- next if $item eq '.';
- next if $item eq '..';
- #print "Dir list is" . $item."\n";
- #Stat the file to get ownership and size
- (
- $dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
- $size, $atime, $mtime, $ctime, $blksize, $blocks
- ) = stat($item);
- if(defined $uid)
- {
- $uid = getpwuid($uid);
- }
- else
- {
- $uid = '';
- }
- if(defined $gid)
- {
- $gid = getgrgid($gid);
- }
- else
- {
- $gid = '';
- }
- #This if else logic determines what it is, File, Directory, other
- if (-T $item)
- {
- # print "File\n";
- $dirfiles{'files'}{$count}{'filename'} = encode_base64($item);
- $dirfiles{'files'}{$count}{'size'} = $size;
- $dirfiles{'files'}{$count}{'user'} = $uid;
- $dirfiles{'files'}{$count}{'group'} = $gid;
- $dirfiles{'files'}{$count}{'attr'} = $attr{$item};
- }
- elsif (-d $item)
- {
- # print "Dir\n";
- $dirfiles{'directorys'}{$count}{'filename'} = encode_base64($item);
- $dirfiles{'directorys'}{$count}{'size'} = $size;
- $dirfiles{'directorys'}{$count}{'user'} = $uid;
- $dirfiles{'directorys'}{$count}{'group'} = $gid;
- }
- elsif (-B $item)
- {
- #print "File\n";
- $dirfiles{'binarys'}{$count}{'filename'} = encode_base64($item);
- $dirfiles{'binarys'}{$count}{'size'} = $size;
- $dirfiles{'binarys'}{$count}{'user'} = $uid;
- $dirfiles{'binarys'}{$count}{'group'} = $gid;
- $dirfiles{'binarys'}{$count}{'attr'} = $attr{$item};
- }
- else
- {
- #print "Unknown\n"
- #will be listed as common files;
- $dirfiles{'files'}{$count}{'filename'} = encode_base64($item);
- $dirfiles{'files'}{$count}{'size'} = $size;
- $dirfiles{'files'}{$count}{'user'} = $uid;
- $dirfiles{'files'}{$count}{'group'} = $gid;
- $dirfiles{'files'}{$count}{'attr'} = $attr{$item};
- }
- $count++;
- }
- closedir(DIR);
- if ($count eq 0)
- {
- logger "Empty directory $datadir.";
- return 1;
- }
- chdir AGENT_RUN_DIR;
- #Now we return it to the webpage, as array
- return {%dirfiles};
- }
- ###### Returns the contents of a text file
- sub readfile
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- chdir AGENT_RUN_DIR;
- my $userfile = &decrypt_param(@_);
- unless ( -e $userfile )
- {
- if (open(BLANK, '>', $userfile))
- {
- close(BLANK);
- }
- }
- if (!open(USERFILE, '<', $userfile))
- {
- logger "ERROR - Can't open file $userfile for reading.";
- return -1;
- }
- my ($wholefile, $buf);
- while (read(USERFILE, $buf, 60 * 57))
- {
- $wholefile .= encode_base64($buf);
- }
- close(USERFILE);
- if(!defined $wholefile)
- {
- return "1; ";
- }
- return "1;" . $wholefile;
- }
- ###### Backs up file, then writes data to new file
- ### @return 1 On success
- ### @return 0 In case of a failure
- sub writefile
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- chdir AGENT_RUN_DIR;
- # $writefile = file we're editing, $filedata = the contents were writing to it
- my ($writefile, $filedata) = &decrypt_params(@_);
- if (!-e $writefile)
- {
- open FILE, ">", $writefile;
- }
- else
- {
- # backup the existing file
- logger
- "Backing up file $writefile to $writefile.bak before writing new data.";
- if (!copy("$writefile", "$writefile.bak"))
- {
- logger
- "ERROR - Failed to backup $writefile to $writefile.bak. Error: $!";
- return 0;
- }
- }
- if (!-w $writefile)
- {
- logger "ERROR - File [ $writefile ] is not writeable!";
- return 0;
- }
- if (!open(WRITER, '>', $writefile))
- {
- logger "ERROR - Failed to open $writefile for writing.";
- return 0;
- }
- $filedata = decode_base64($filedata);
- $filedata =~ s/\r//g;
- print WRITER "$filedata";
- close(WRITER);
- logger "Wrote $writefile successfully!";
- return 1;
- }
- ###### Reboots the server remotely through panel
- ### @return 1 On success
- ### @return 0 In case of a failure
- sub rebootnow
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- sudo_exec_without_decrypt('sleep 10s; shutdown -r now');
- logger "Scheduled system reboot to occur in 10 seconds successfully!";
- return 1;
- }
- # Determine the os of the agent machine.
- sub what_os
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- logger "Asking for OS type";
- my $which_uname = `which uname`;
- chomp $which_uname;
- if ($which_uname ne "")
- {
- my $os;
- my $os_name;
- my $os_arch;
- my $wine_ver = "";
- $os_name = `$which_uname`;
- chomp $os_name;
- $os_arch = `$which_uname -m`;
- chomp $os_arch;
- my $which_wine = `which wine`;
- chomp $which_wine;
- if ($which_wine ne "")
- {
- $wine_ver = `$which_wine --version`;
- chomp $wine_ver;
- $wine_ver = "|".$wine_ver;
- }
- $os = $os_name." ".$os_arch.$wine_ver;
- logger "OS is $os";
- return "$os";
- }
- else
- {
- logger "Cannot determine OS..that is odd";
- return "Unknown";
- }
- }
- ### @return PID of the download process if started succesfully.
- ### @return -1 If could not create temporary download directory.
- ### @return -2 If could not create destination directory.
- ### @return -3 If resources unavailable.
- sub start_file_download
- {
- return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
- my ($url, $destination, $filename, $action, $post_script) = &decrypt_params(@_);
- logger
- "Starting to download URL $url. Destination: $destination - Filename: $filename";
- if (!-e $destination)
- {
- logger "Creating destination directory.";
- if (!mkpath $destination )
- {
- logger "Could not create destination '$destination' directory : $!";
- return -2;
- }
- }
- my $download_file_path = Path::Class::File->new($destination, "$filename");
- my $pid = fork();
- if (not defined $pid)
- {
- logger "Could not allocate resources for download.";
- return -3;
- }
- # Only the forked child goes here.
- elsif ($pid == 0)
- {
- my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0,
- SSL_verify_mode => 0x00 } );
- $ua->agent('Mozilla/5.0');
- my $response = $ua->get($url, ':content_file' => "$download_file_path");
- if ($response->is_success)
- {
- logger "Successfully fetched $url and stored it to $download_file_path. Retval: ".$response->status_line;
Add Comment
Please, Sign In to add comment