update dix

This commit is contained in:
Frank Harris 2026-06-06 14:21:58 -05:00
parent e921a49d5b
commit 11691a5876
11 changed files with 1108 additions and 54 deletions

View file

@ -408,6 +408,7 @@ my $d = Frontier::Daemon::OGP::Forking->new(
fastdl_get_info => \&fastdl_get_info,
fastdl_create_config => \&fastdl_create_config,
agent_restart => \&agent_restart,
component_update => \&component_update,
scheduler_add_task => \&scheduler_add_task,
scheduler_del_task => \&scheduler_del_task,
scheduler_list_tasks => \&scheduler_list_tasks,
@ -4673,6 +4674,165 @@ sub fastdl_create_config
return 1;
}
sub component_update_shell_quote
{
my ($value) = @_;
$value = '' unless defined $value;
$value =~ s/'/'"'"'/g;
return "'" . $value . "'";
}
sub component_update_response
{
my (%data) = @_;
my %encoded = ();
foreach my $key (keys %data)
{
$encoded{$key} = encode_base64(defined $data{$key} ? $data{$key} : '', '');
}
return \%encoded;
}
sub component_update_parse_payload
{
my ($payload) = @_;
my %data = ();
foreach my $line (split(/\r?\n/, $payload || ''))
{
next if $line !~ /^([A-Za-z0-9_]+)=(.*)$/;
$data{$1} = decode_base64($2);
}
return %data;
}
sub component_update
{
return component_update_response(success => 0, status => 'bad_encryption', message => 'Bad Encryption Key')
unless(decrypt_param(pop(@_)) eq "Encryption checking OK");
my ($payload) = decrypt_params(@_);
my %cfg = component_update_parse_payload($payload);
my $component = $cfg{component} || 'linux_agent';
if ($component ne 'linux_agent' && $component ne 'windows_agent')
{
return component_update_response(success => 0, status => 'invalid_component', message => 'Invalid component.');
}
my $repo_url = $cfg{repo_url} || '';
my $branch = $cfg{branch} || '';
my $source_path = $cfg{source_path} || '';
my $install_path = $cfg{install_path} || AGENT_RUN_DIR;
my $git_path = $cfg{git_path} || 'git';
my $backup_path = $cfg{backup_path} || Path::Class::Dir->new(AGENT_RUN_DIR, 'component_backups')->stringify;
my $post_update_command = $cfg{post_update_command} || '';
if ($repo_url !~ m{^(https?://|ssh://|git@|/)} || $repo_url =~ /[\r\n\0]/)
{
return component_update_response(success => 0, status => 'invalid_repo', message => 'Invalid repository URL or path.');
}
if ($branch !~ /^[A-Za-z0-9._\/-]{1,128}$/)
{
return component_update_response(success => 0, status => 'invalid_branch', message => 'Invalid branch.');
}
if ($source_path eq '' || $source_path =~ /\.\./ || $source_path =~ /[\r\n\0]/ || $source_path !~ /^[A-Za-z0-9._\/-]+$/)
{
return component_update_response(success => 0, status => 'invalid_source_path', message => 'Invalid source path.');
}
if ($install_path !~ m{^/} || $install_path =~ /\.\./ || $install_path =~ /[\r\n\0]/)
{
return component_update_response(success => 0, status => 'invalid_install_path', message => 'Invalid install path.');
}
if ($post_update_command =~ /[\r\n\0]/)
{
return component_update_response(success => 0, status => 'invalid_post_update', message => 'Post-update command must be a single line.');
}
my $ts = time();
my $script_path = Path::Class::File->new(AGENT_RUN_DIR, "gsp_component_update_$ts.sh")->stringify;
my $log_path = Path::Class::File->new(AGENT_RUN_DIR, 'gsp_component_update.log')->stringify;
my $screenrc = SCREENRC_FILE;
my $repo_q = component_update_shell_quote($repo_url);
my $branch_q = component_update_shell_quote($branch);
my $source_q = component_update_shell_quote($source_path);
my $install_q = component_update_shell_quote($install_path);
my $git_q = component_update_shell_quote($git_path);
my $backup_q = component_update_shell_quote($backup_path);
my $post_q = component_update_shell_quote($post_update_command);
my $log_q = component_update_shell_quote($log_path);
my $screenrc_q = component_update_shell_quote($screenrc);
my $script = <<"EOS";
#!/usr/bin/env bash
set -u
LOG=$log_q
log(){ printf '[%s] %s\\n' "\$(date '+%Y-%m-%d %H:%M:%S')" "\$*" >> "\$LOG"; }
fail(){ log "FAILED: \$*"; exit 1; }
REPO=$repo_q
BRANCH=$branch_q
SOURCE_REL=$source_q
DEST=$install_q
GIT_BIN=$git_q
BACKUP_BASE=$backup_q
POST_UPDATE=$post_q
TMP="\${TMPDIR:-/tmp}/gsp_agent_update_\$(date +%s)_\$\$"
mkdir -p "\$TMP" "\$BACKUP_BASE" || fail "Cannot create staging or backup directories"
log "queued component=$component repo=\$REPO branch=\$BRANCH source=\$SOURCE_REL dest=\$DEST"
if ! command -v "\$GIT_BIN" >/dev/null 2>&1 && [ ! -x "\$GIT_BIN" ]; then
fail "Git executable not found: \$GIT_BIN"
fi
"\$GIT_BIN" clone --depth 1 --branch "\$BRANCH" "\$REPO" "\$TMP/repo" >> "\$LOG" 2>&1 || fail "git clone failed"
SRC="\$TMP/repo/\$SOURCE_REL"
[ -d "\$SRC" ] || fail "Source component folder missing: \$SRC"
case "\$DEST" in /*) ;; *) fail "Destination must be absolute: \$DEST" ;; esac
mkdir -p "\$DEST" || fail "Cannot create destination: \$DEST"
ARCHIVE="\$BACKUP_BASE/${component}_\$(date +%Y%m%d_%H%M%S).tar.gz"
tar --exclude='./Cfg' --exclude='./ServerFiles' --exclude='./Schedule' --exclude='./logs' --exclude='./screenlogs' --exclude='./steamcmd' --exclude='./startups' --exclude='./tmp' --exclude='./shared' --exclude='./component_backups' --exclude='./*.pid' -czf "\$ARCHIVE" -C "\$DEST" . >> "\$LOG" 2>&1 || fail "Backup failed"
if command -v rsync >/dev/null 2>&1; then
rsync -a --exclude='Cfg/' --exclude='ServerFiles/' --exclude='Schedule/' --exclude='logs/' --exclude='screenlogs/' --exclude='steamcmd/' --exclude='startups/' --exclude='tmp/' --exclude='shared/' --exclude='component_backups/' --exclude='*.pid' "\$SRC"/ "\$DEST"/ >> "\$LOG" 2>&1 || fail "rsync copy failed"
else
(cd "\$SRC" && tar --exclude='./Cfg' --exclude='./ServerFiles' --exclude='./Schedule' --exclude='./logs' --exclude='./screenlogs' --exclude='./steamcmd' --exclude='./startups' --exclude='./tmp' --exclude='./shared' --exclude='./component_backups' --exclude='./*.pid' -cf - .) | (cd "\$DEST" && tar -xf -) >> "\$LOG" 2>&1 || fail "tar copy failed"
fi
if [ -f "\$DEST/ogp_agent.pl" ]; then
perl -c "\$DEST/ogp_agent.pl" >> "\$LOG" 2>&1 || fail "Updated ogp_agent.pl failed syntax validation"
fi
if [ -n "\$POST_UPDATE" ]; then
(cd "\$DEST" && bash -lc "\$POST_UPDATE") >> "\$LOG" 2>&1 || fail "post-update command failed"
fi
log "copy complete archive=\$ARCHIVE"
sleep 2
if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files 2>/dev/null | grep -q '^ogp_agent\\.service'; then
systemctl restart ogp_agent.service >> "\$LOG" 2>&1 && exit 0
fi
cd "\$DEST" || exit 0
if [ -f ogp_agent_run.pid ]; then kill "\$(cat ogp_agent_run.pid)" >/dev/null 2>&1 || true; fi
if [ -f ogp_agent.pid ]; then kill "\$(cat ogp_agent.pid)" >/dev/null 2>&1 || true; fi
sleep 2
if [ -f ogp_agent_run ]; then
screen -d -m -t "ogp_agent" -c $screenrc_q -S ogp_agent bash ogp_agent_run -pidfile ogp_agent_run.pid >> "\$LOG" 2>&1 || true
else
screen -d -m -t "ogp_agent" -c $screenrc_q -S ogp_agent perl ogp_agent.pl >> "\$LOG" 2>&1 || true
fi
log "restart attempted"
rm -rf "\$TMP"
exit 0
EOS
my $fh;
if (!open($fh, '>', $script_path))
{
return component_update_response(success => 0, status => 'write_failed', message => "Cannot write updater script: $!");
}
print $fh $script;
close($fh);
chmod 0700, $script_path;
system('screen -d -m -t "component_update" -c "' . SCREENRC_FILE . '" -S component_update bash ' . component_update_shell_quote($script_path));
if ($? != 0)
{
system('bash ' . component_update_shell_quote($script_path) . ' >/dev/null 2>&1 &');
}
logger "Component update queued for $component from $repo_url branch $branch.";
return component_update_response(success => 1, status => 'queued', message => 'Component update queued on agent.', log_path => $log_path, script_path => $script_path);
}
sub agent_restart
{
return "Bad Encryption Key" unless(decrypt_param(pop(@_)) eq "Encryption checking OK");