#!/usr/local/bin/perl
# install.cgi
# Download and install webmin module or theme on multiple hosts
require './cluster-webmin-lib.pl';
if ($ENV{REQUEST_METHOD} eq "POST") { &ReadParseMime(); }
else { &ReadParse(); $no_upload = 1; }
&error_setup($text{'install_err'});
if ($in{source} == 2) {
&ui_print_unbuffered_header(undef, $text{'install_title'}, "");
}
else {
&ui_print_header(undef, $text{'install_title'}, "");
}
if ($in{source} == 0) {
# installing from local file (or maybe directory)
if (!$in{'local'})
{ &download_error($text{'install_elocal'}); }
if (!-r $in{'local'})
{ &download_error(&text('install_elocal2', $in{'local'})); }
$source = $in{'local'};
$pfile = $in{'local'};
$need_unlink = 0;
}
elsif ($in{source} == 1) {
# installing from upload .. store file in temp location
if ($no_upload) {
&download_error($text{'install_eupload'});
}
$in{'upload_filename'} =~ /([^\/\\]+$)/;
$pfile = &tempname("$1");
&open_tempfile(PFILE, "> $pfile");
&print_tempfile(PFILE, $in{'upload'});
&close_tempfile(PFILE);
$source = $in{'upload_filename'};
$need_unlink = 1;
}
elsif ($in{source} == 2) {
# installing from URL.. store downloaded file in temp location
$in{'url'} =~ /\/([^\/]+)\/*$/;
$pfile = &tempname("$1");
$progress_callback_url = $in{'url'};
if ($in{'url'} =~ /^(http|https):\/\/([^\/]+)(\/.*)$/) {
# Make a HTTP request
$ssl = $1 eq 'https';
$host = $2; $page = $3; $port = $ssl ? 443 : 80;
if ($host =~ /^(.*):(\d+)$/) { $host = $1; $port = $2; }
&http_download($host, $port, $page, $pfile, \$error,
\&progress_callback, $ssl);
}
elsif ($in{'url'} =~ /^ftp:\/\/([^\/]+)(:21)?(\/.*)$/) {
$host = $1; $file = $3;
&ftp_download($host, $file, $pfile, \$error,
\&progress_callback);
}
else { &download_error(&text('install_eurl', $in{'url'})); }
&download_error($error) if ($error);
$source = $in{'url'};
$need_unlink = 1;
}
$grant = $in{'grant'} ? undef : [ split(/\s+/, $in{'grantto'}) ];
# Check validity
open(MFILE, $pfile);
read(MFILE, $two, 2);
close(MFILE);
if ($two eq "\037\235") {
# Unix compressed
&has_command("uncompress") ||
&download_error(&text('install_ecomp', "uncompress"));
$cmd = "uncompress -c '$pfile' | tar tf -";
}
elsif ($two eq "\037\213") {
# Gzipped
&has_command("gunzip") ||
&download_error(&text('install_egzip', "gunzip"));
$cmd = "gunzip -c '$pfile' | tar tf -";
}
else {
# Just a tar file
$cmd = "tar tf '$pfile'";
}
$tar = `$cmd 2>&1`;
$? && &download_error(&text('install_ecmd', "$tar"));
foreach $f (split(/\n/, $tar)) {
if ($f =~ /^\.\/([^\/]+)\/(.*)$/ || $f =~ /^([^\/]+)\/(.*)$/) {
$mods{$1}++;
$hasfile{$1,$2}++;
}
}
foreach $m (keys %mods) {
$hasfile{$m,"module.info"} || $hasfile{$m,"theme.info"} ||
&download_error(&text('install_einfo', "$m"));
}
if (!%mods) {
&download_error($text{'install_enone'});
}
# Get the version numbers
$tempdir = &transname();
mkdir($tempdir, 0755);
foreach $m (keys %mods) {
local $xcmd = $cmd;
$xcmd =~ s/tf/xf/g;
system("cd $tempdir ; $xcmd $m/module.info ./$m/module.info >/dev/null 2>&1");
local %minfo;
&read_file("$tempdir/$m/module.info", \%minfo);
$modver{$m} = $minfo{'version'};
}
# Setup error handler for down hosts
sub inst_error
{
$inst_error_msg = join("", @_);
}
&remote_error_setup(\&inst_error);
# Work out which hosts have the module, and which to install on
@hosts = &list_webmin_hosts();
@servers = &list_servers();
foreach $h (@hosts) {
local $gotall = 1;
foreach $m (keys %mods) {
local ($alr) = grep { $_->{'dir'} eq $m }
(@{$h->{'modules'}}, @{$h->{'themes'}});
$gotall = 0 if (!$alr ||
(defined($modver{$m}) &&
$modver{$m} > $alr->{'version'}));
}
push(@gothosts, $h) if ($gotall);
}
@hosts = &create_on_parse("install_header", \@gothosts,
join(" ", keys %mods));
# Run the install
$p = 0;
foreach $h (@hosts) {
local ($s) = grep { $_->{'id'} == $h->{'id'} } @servers;
local ($rh = "READ$p", $wh = "WRITE$p");
pipe($rh, $wh);
select($wh); $| = 1; select(STDOUT);
if (!fork()) {
# Do the install in a subprocess
close($rh);
&remote_foreign_require($s->{'host'}, "webmin",
"webmin-lib.pl");
if ($inst_error_msg) {
# Failed to contact host ..
print $wh &serialise_variable($inst_error_msg);
exit;
}
&remote_foreign_require($s->{'host'}, "acl", "acl-lib.pl");
local $rfile;
local $host_need_unlink = 1;
if (!$s->{'id'}) {
# This host, so we already have the file
$rfile = $pfile;
$host_need_unlink = 0;
}
elsif ($in{'source'} == 0) {
# Is the file the same on remote? (like if we have NFS)
local @st = stat($pfile);
local $rst = &remote_eval($s->{'host'}, "webmin",
"[ stat('$pfile') ]");
local @rst = @$rst;
if (@st && @rst && $st[7] == $rst[7] &&
$st[9] == $rst[9]) {
# File is the same! No need to download
$rfile = $pfile;
$host_need_unlink = 0;
}
else {
# Need to copy the file across :(
$rfile = &remote_write(
$s->{'host'}, $pfile);
}
}
elsif ($in{'source'} == 2 && $in{'down'}) {
# Ask the remote server to download the file
$rfile = &remote_foreign_call($s->{'host'}, "webmin",
"tempname");
if ($in{'url'} =~ /^ftp/) {
&remote_foreign_call($s->{'host'}, "webmin",
"ftp_download", $host, $file,
$rfile);
}
else {
&remote_foreign_call($s->{'host'}, "webmin",
"http_download", $host, $port,
$page, $rfile, undef, undef, $ssl);
}
}
else {
# Need to copy the file across :(
$rfile = &remote_write($s->{'host'}, $pfile);
}
# Do the install ..
local $rv = &remote_foreign_call($s->{'host'},
"webmin", "install_webmin_module", $rfile,
$host_need_unlink, $in{'nodeps'}, $grant);
if (ref($rv)) {
# It worked .. get all the module infos
local @mods;
foreach $m (@{$rv->[1]}) {
$m =~ s/^.*\///;
local %info = &remote_foreign_call(
$s->{'host'}, "webmin",
"get_module_info", $m);
if (!%info) {
# Must have been a theme
%info = &remote_foreign_call(
$s->{'host'}, "webmin",
"get_theme_info", $m);
$info{'theme'} = 1;
}
push(@mods, \%info);
}
print $wh &serialise_variable(\@mods);
# Re-request all users, groups, modules and themes
# from the server
$h->{'modules'} = [ grep { !$_->{'clone'} }
&remote_foreign_call($s->{'host'},
"webmin", "get_all_module_infos", 1) ];
$h->{'themes'} = [ &remote_foreign_call($s->{'host'},
"webmin", "list_themes") ];
$h->{'users'} = [ &remote_foreign_call($s->{'host'},
"acl", "list_users") ];
$h->{'groups'} = [ &remote_foreign_call($s->{'host'},
"acl", "list_groups") ];
&save_webmin_host($h);
}
else {
print $wh &serialise_variable($rv);
}
close($wh);
exit;
}
close($wh);
$p++;
}
# Get back all the results
$p = 0;
foreach $h (@hosts) {
local $rh = "READ$p";
local $line = <$rh>;
close($rh);
local $rv = &unserialise_variable($line);
local ($s) = grep { $_->{'id'} == $h->{'id'} } @servers;
local $d = &server_name($s);
if (!$line) {
print &text('do_failed', $d, "Unknown reason"),"
\n";
}
elsif (!ref($rv)) {
print &text('do_failed', $d, $rv),"
\n";
}
else {
# List the modules installed, and add to lists
foreach $m (@$rv) {
if ($m->{'theme'}) {
print &text('do_success_theme', $d, "$m->{'desc'}"), "
\n";
}
else {
print &text('do_success_mod', $d, "$m->{'desc'}"), "
\n";
}
}
}
$p++;
}
unlink($pfile) if ($need_unlink);
print "
$text{'do_done'}
\n";
&remote_finished();
&ui_print_footer("", $text{'index_return'});
sub download_error
{
unlink($pfile) if ($need_unlink);
print "
$whatfailed : $_[0]
\n"; &ui_print_footer("", $text{'index_return'}); exit; }