- Add per-user activity logging to AdminBin: every RESTORE_* action writes to /var/log/gniza/cpanel-<user>.log with action details and gniza command output - New logs.live.cgi CGI with paginated activity list and detail view - WHM logs.cgi now shows cpanel-*.log files with Owner column and structured activity entry viewer with expandable command output - Add Logs nav item to cPanel plugin, update install.sh Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
589 lines
21 KiB
Perl
589 lines
21 KiB
Perl
#!/usr/local/cpanel/3rdparty/bin/perl
|
|
package Cpanel::AdminBin::Script::Call::Gniza::Restore;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use parent 'Cpanel::AdminBin::Script::Call';
|
|
use IPC::Open3;
|
|
use Symbol 'gensym';
|
|
|
|
my $GNIZA_BIN = '/usr/local/bin/gniza';
|
|
my $MAIN_CONFIG = '/etc/gniza/gniza.conf';
|
|
my $REMOTES_DIR = '/etc/gniza/remotes.d';
|
|
|
|
# Argument validation patterns (mirrors GnizaWHM::Runner)
|
|
my %OPT_PATTERNS = (
|
|
remote => qr/^[a-zA-Z0-9_,-]+$/,
|
|
timestamp => qr/^\d{4}-\d{2}-\d{2}T\d{6}$/,
|
|
path => qr/^(?!.*\.\.)[a-zA-Z0-9_.\/@ -]+$/,
|
|
exclude => qr/^[a-zA-Z0-9_.,\/@ *?\[\]-]+$/,
|
|
);
|
|
|
|
my $ACCOUNT_RE = qr/^[a-z][a-z0-9_-]*$/;
|
|
my $REMOTE_RE = qr/^[a-zA-Z0-9_-]+$/;
|
|
my $DBNAME_RE = qr/^[a-zA-Z0-9_]+$/;
|
|
my $EMAIL_RE = qr/^[a-zA-Z0-9._+-]+\@[a-zA-Z0-9._-]+$/;
|
|
my $DOMAIN_RE = qr/^[a-zA-Z0-9._-]+$/;
|
|
my $TS_RE = qr/^\d{4}-\d{2}-\d{2}T\d{6}$/;
|
|
|
|
# ── Allowed remotes for user restore ──────────────────────────
|
|
|
|
sub _get_allowed_remotes {
|
|
my $setting = '';
|
|
if (open my $fh, '<', $MAIN_CONFIG) {
|
|
while (my $line = <$fh>) {
|
|
if ($line =~ /^USER_RESTORE_REMOTES=(?:"([^"]*)"|'([^']*)'|(\S*))$/) {
|
|
$setting = defined $1 ? $1 : (defined $2 ? $2 : ($3 // ''));
|
|
}
|
|
}
|
|
close $fh;
|
|
}
|
|
# Default to "all" if not set
|
|
$setting = 'all' if !defined $setting || $setting eq '';
|
|
|
|
return $setting;
|
|
}
|
|
|
|
sub _list_all_remotes {
|
|
my @remotes;
|
|
if (-d $REMOTES_DIR && opendir my $dh, $REMOTES_DIR) {
|
|
while (my $entry = readdir $dh) {
|
|
if ($entry =~ /^([a-zA-Z0-9_-]+)\.conf$/) {
|
|
push @remotes, $1;
|
|
}
|
|
}
|
|
closedir $dh;
|
|
}
|
|
return sort @remotes;
|
|
}
|
|
|
|
sub _is_remote_allowed {
|
|
my ($remote) = @_;
|
|
my $setting = _get_allowed_remotes();
|
|
return 0 if $setting eq ''; # disabled
|
|
|
|
if ($setting eq 'all') {
|
|
# Check it actually exists
|
|
return -f "$REMOTES_DIR/$remote.conf" ? 1 : 0;
|
|
}
|
|
|
|
my %allowed = map { $_ => 1 } split /,/, $setting;
|
|
return $allowed{$remote} ? 1 : 0;
|
|
}
|
|
|
|
sub _get_filtered_remotes {
|
|
my $setting = _get_allowed_remotes();
|
|
return () if $setting eq '';
|
|
|
|
my @all = _list_all_remotes();
|
|
return @all if $setting eq 'all';
|
|
|
|
my %allowed = map { $_ => 1 } split /,/, $setting;
|
|
return grep { $allowed{$_} } @all;
|
|
}
|
|
|
|
# ── Command execution ─────────────────────────────────────────
|
|
|
|
sub _run_gniza {
|
|
my (@args) = @_;
|
|
|
|
my $err_fh = gensym;
|
|
my ($in, $out);
|
|
my $pid = eval { open3($in, $out, $err_fh, $GNIZA_BIN, @args) };
|
|
unless ($pid) {
|
|
return (0, '', "Failed to execute gniza: $@");
|
|
}
|
|
close $in if $in;
|
|
|
|
my $stdout = do { local $/; <$out> } // '';
|
|
my $stderr = do { local $/; <$err_fh> } // '';
|
|
close $out;
|
|
close $err_fh;
|
|
|
|
waitpid($pid, 0);
|
|
my $exit_code = $? >> 8;
|
|
|
|
return ($exit_code == 0, $stdout, $stderr);
|
|
}
|
|
|
|
# ── Action dispatch ───────────────────────────────────────────
|
|
|
|
# ── Per-user activity logging ─────────────────────────────────
|
|
|
|
my $ACTIVITY_ENTRY_RE = qr/^[0-9]+$/;
|
|
|
|
sub _get_log_dir {
|
|
my $log_dir = '/var/log/gniza';
|
|
if (open my $fh, '<', $MAIN_CONFIG) {
|
|
while (my $line = <$fh>) {
|
|
if ($line =~ /^LOG_DIR=(?:"([^"]*)"|'([^']*)'|(\S*))$/) {
|
|
my $val = defined $1 ? $1 : (defined $2 ? $2 : ($3 // ''));
|
|
$log_dir = $val if $val ne '';
|
|
}
|
|
}
|
|
close $fh;
|
|
}
|
|
return $log_dir;
|
|
}
|
|
|
|
sub _activity_log_path {
|
|
my ($user) = @_;
|
|
my $log_dir = _get_log_dir();
|
|
return "$log_dir/cpanel-$user.log";
|
|
}
|
|
|
|
my %ACTION_LABELS = (
|
|
RESTORE_ACCOUNT => 'Full Account',
|
|
RESTORE_FILES => 'Home Directory',
|
|
RESTORE_DATABASE => 'Database',
|
|
RESTORE_MAILBOX => 'Email',
|
|
RESTORE_CRON => 'Cron Jobs',
|
|
RESTORE_DBUSERS => 'DB Users',
|
|
RESTORE_DOMAINS => 'Domains',
|
|
RESTORE_SSL => 'SSL Certificates',
|
|
);
|
|
|
|
sub _log_activity {
|
|
my ($user, $action, $details, $status, $output) = @_;
|
|
my $log_file = _activity_log_path($user);
|
|
my $log_dir = _get_log_dir();
|
|
mkdir $log_dir, 0700 unless -d $log_dir;
|
|
|
|
my @t = gmtime(time);
|
|
my $ts = sprintf('%04d-%02d-%02d %02d:%02d:%02d',
|
|
$t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
|
|
my $label = $ACTION_LABELS{$action} // $action;
|
|
|
|
if (open my $fh, '>>', $log_file) {
|
|
print $fh "--- ENTRY ---\n";
|
|
print $fh "Date: $ts\n";
|
|
print $fh "Action: $label\n";
|
|
print $fh "Details: $details\n";
|
|
print $fh "Status: $status\n";
|
|
print $fh $output if defined $output && $output ne '';
|
|
print $fh "--- END ---\n";
|
|
close $fh;
|
|
}
|
|
}
|
|
|
|
sub _actions {
|
|
return qw(
|
|
LIST_ALLOWED_REMOTES
|
|
LIST_SNAPSHOTS
|
|
LIST_DATABASES
|
|
LIST_MAILBOXES
|
|
LIST_FILES
|
|
LIST_DBUSERS
|
|
LIST_CRON
|
|
LIST_DNS
|
|
LIST_SSL
|
|
LIST_LOGS
|
|
GET_LOG
|
|
RESTORE_ACCOUNT
|
|
RESTORE_FILES
|
|
RESTORE_DATABASE
|
|
RESTORE_MAILBOX
|
|
RESTORE_CRON
|
|
RESTORE_DBUSERS
|
|
RESTORE_DOMAINS
|
|
RESTORE_SSL
|
|
);
|
|
}
|
|
|
|
sub LIST_LOGS {
|
|
my ($self) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
|
|
my $log_file = _activity_log_path($user);
|
|
return '' unless -f $log_file && !-l $log_file;
|
|
|
|
# Parse entries from the activity log (newest first)
|
|
my @entries;
|
|
if (open my $fh, '<', $log_file) {
|
|
my $in_entry = 0;
|
|
my %cur;
|
|
while (my $line = <$fh>) {
|
|
chomp $line;
|
|
if ($line eq '--- ENTRY ---') {
|
|
$in_entry = 1;
|
|
%cur = ();
|
|
} elsif ($line eq '--- END ---' && $in_entry) {
|
|
push @entries, { %cur } if $cur{date};
|
|
$in_entry = 0;
|
|
} elsif ($in_entry) {
|
|
if ($line =~ /^Date:\s+(.+)$/) { $cur{date} = $1; }
|
|
elsif ($line =~ /^Action:\s+(.+)$/) { $cur{action} = $1; }
|
|
elsif ($line =~ /^Details:\s+(.+)$/) { $cur{details} = $1; }
|
|
elsif ($line =~ /^Status:\s+(.+)$/) { $cur{status} = $1; }
|
|
}
|
|
}
|
|
close $fh;
|
|
}
|
|
|
|
# Return newest first, one line per entry: index\tdate\taction\tdetails\tstatus
|
|
my @lines;
|
|
for my $i (reverse 0 .. $#entries) {
|
|
my $e = $entries[$i];
|
|
push @lines, join("\t", $i, $e->{date} // '', $e->{action} // '',
|
|
$e->{details} // '', $e->{status} // '');
|
|
}
|
|
return join("\n", @lines);
|
|
}
|
|
|
|
sub GET_LOG {
|
|
my ($self, $entry_idx) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid entry" unless defined $entry_idx && $entry_idx =~ $ACTIVITY_ENTRY_RE;
|
|
|
|
my $log_file = _activity_log_path($user);
|
|
return "ERROR: No activity log" unless -f $log_file && !-l $log_file;
|
|
|
|
# Parse the Nth entry
|
|
my $idx = int($entry_idx);
|
|
my @entries;
|
|
if (open my $fh, '<', $log_file) {
|
|
my $in_entry = 0;
|
|
my @cur_lines;
|
|
while (my $line = <$fh>) {
|
|
chomp $line;
|
|
if ($line eq '--- ENTRY ---') {
|
|
$in_entry = 1;
|
|
@cur_lines = ();
|
|
} elsif ($line eq '--- END ---' && $in_entry) {
|
|
push @entries, join("\n", @cur_lines);
|
|
$in_entry = 0;
|
|
} elsif ($in_entry) {
|
|
push @cur_lines, $line;
|
|
}
|
|
}
|
|
close $fh;
|
|
}
|
|
|
|
return "ERROR: Entry not found" if $idx < 0 || $idx > $#entries;
|
|
return $entries[$idx];
|
|
}
|
|
|
|
sub LIST_ALLOWED_REMOTES {
|
|
my ($self) = @_;
|
|
my @remotes = _get_filtered_remotes();
|
|
return join("\n", @remotes);
|
|
}
|
|
|
|
sub LIST_SNAPSHOTS {
|
|
my ($self, $remote) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('list', "--remote=$remote", "--account=$user");
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub LIST_DATABASES {
|
|
my ($self, $remote, $timestamp) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'list-databases', $user,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub LIST_MAILBOXES {
|
|
my ($self, $remote, $timestamp) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'list-mailboxes', $user,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub LIST_FILES {
|
|
my ($self, $remote, $timestamp, $path) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @opts = ("--remote=$remote", "--timestamp=$timestamp");
|
|
if (defined $path && $path ne '') {
|
|
return "ERROR: Invalid path" unless $path =~ $OPT_PATTERNS{path};
|
|
push @opts, "--path=$path";
|
|
}
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'list-files', $user, @opts);
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub LIST_DBUSERS {
|
|
my ($self, $remote, $timestamp) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'list-dbusers', $user,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub LIST_CRON {
|
|
my ($self, $remote, $timestamp) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'list-cron', $user,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub LIST_DNS {
|
|
my ($self, $remote, $timestamp) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'list-dns', $user,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub LIST_SSL {
|
|
my ($self, $remote, $timestamp) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'list-ssl', $user,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
return $ok ? $stdout : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_ACCOUNT {
|
|
my ($self, $remote, $timestamp, $exclude) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @opts = ("--remote=$remote", "--timestamp=$timestamp");
|
|
# NOTE: --terminate is NEVER passed for user restore
|
|
if (defined $exclude && $exclude ne '') {
|
|
return "ERROR: Invalid exclude" unless $exclude =~ $OPT_PATTERNS{exclude};
|
|
push @opts, "--exclude=$exclude";
|
|
}
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
$details .= " exclude=$exclude" if defined $exclude && $exclude ne '';
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'account', $user, @opts);
|
|
_log_activity($user, 'RESTORE_ACCOUNT', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_FILES {
|
|
my ($self, $remote, $timestamp, $path, $exclude) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @opts = ("--remote=$remote", "--timestamp=$timestamp");
|
|
if (defined $path && $path ne '') {
|
|
return "ERROR: Invalid path" unless $path =~ $OPT_PATTERNS{path};
|
|
push @opts, "--path=$path";
|
|
}
|
|
if (defined $exclude && $exclude ne '') {
|
|
return "ERROR: Invalid exclude" unless $exclude =~ $OPT_PATTERNS{exclude};
|
|
push @opts, "--exclude=$exclude";
|
|
}
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
$details .= " path=$path" if defined $path && $path ne '';
|
|
$details .= " exclude=$exclude" if defined $exclude && $exclude ne '';
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'files', $user, @opts);
|
|
_log_activity($user, 'RESTORE_FILES', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_DATABASE {
|
|
my ($self, $remote, $timestamp, $dbname) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @args = ($user);
|
|
if (defined $dbname && $dbname ne '') {
|
|
return "ERROR: Invalid database name" unless $dbname =~ $DBNAME_RE;
|
|
push @args, $dbname;
|
|
}
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
$details .= " database=$dbname" if defined $dbname && $dbname ne '';
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'database', @args,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
_log_activity($user, 'RESTORE_DATABASE', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_MAILBOX {
|
|
my ($self, $remote, $timestamp, $email) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @args = ($user);
|
|
if (defined $email && $email ne '') {
|
|
return "ERROR: Invalid email" unless $email =~ $EMAIL_RE;
|
|
push @args, $email;
|
|
}
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
$details .= " email=$email" if defined $email && $email ne '';
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'mailbox', @args,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
_log_activity($user, 'RESTORE_MAILBOX', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_CRON {
|
|
my ($self, $remote, $timestamp) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'cron', $user,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
_log_activity($user, 'RESTORE_CRON', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_DBUSERS {
|
|
my ($self, $remote, $timestamp, $dbuser) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @args = ($user);
|
|
if (defined $dbuser && $dbuser ne '') {
|
|
return "ERROR: Invalid database user" unless $dbuser =~ $DBNAME_RE;
|
|
push @args, $dbuser;
|
|
}
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
$details .= " dbuser=$dbuser" if defined $dbuser && $dbuser ne '';
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'dbusers', @args,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
_log_activity($user, 'RESTORE_DBUSERS', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_DOMAINS {
|
|
my ($self, $remote, $timestamp, $domain) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @args = ($user);
|
|
if (defined $domain && $domain ne '') {
|
|
return "ERROR: Invalid domain" unless $domain =~ $DOMAIN_RE;
|
|
push @args, $domain;
|
|
}
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
$details .= " domain=$domain" if defined $domain && $domain ne '';
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'domains', @args,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
_log_activity($user, 'RESTORE_DOMAINS', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
sub RESTORE_SSL {
|
|
my ($self, $remote, $timestamp, $domain) = @_;
|
|
my $user = $self->get_caller_username() // '';
|
|
|
|
return "ERROR: Invalid user" unless $user =~ $ACCOUNT_RE;
|
|
return "ERROR: Invalid remote" unless defined $remote && $remote =~ $REMOTE_RE;
|
|
return "ERROR: Invalid timestamp" unless defined $timestamp && $timestamp =~ $TS_RE;
|
|
return "ERROR: Remote not allowed" unless _is_remote_allowed($remote);
|
|
|
|
my @args = ($user);
|
|
if (defined $domain && $domain ne '') {
|
|
return "ERROR: Invalid domain" unless $domain =~ $DOMAIN_RE;
|
|
push @args, $domain;
|
|
}
|
|
|
|
my $details = "remote=$remote snapshot=$timestamp";
|
|
$details .= " domain=$domain" if defined $domain && $domain ne '';
|
|
|
|
my ($ok, $stdout, $stderr) = _run_gniza('restore', 'ssl', @args,
|
|
"--remote=$remote", "--timestamp=$timestamp");
|
|
_log_activity($user, 'RESTORE_SSL', $details,
|
|
$ok ? 'OK' : 'Error', $ok ? $stdout : $stderr);
|
|
return $ok ? "OK\n$stdout" : "ERROR: $stderr";
|
|
}
|
|
|
|
__PACKAGE__->run() if !caller;
|
|
|
|
1;
|