3 # Download webmin and upgrade all managed servers of compatible types
5 require './cluster-webmin-lib.pl';
6 &foreign_require("proc", "proc-lib.pl");
7 &foreign_require("webmin", "webmin-lib.pl");
8 &foreign_require("webmin", "gnupg-lib.pl");
11 &ui_print_unbuffered_header(undef, $text{'upgrade_title'}, "");
13 # Save this CGI from being killed by the upgrade
14 $SIG{'TERM'} = 'IGNORE';
16 if ($in{'source'} == 0) {
18 &error_setup(&webmin::text('upgrade_err1', $in{'file'}));
20 if (!(-r $file)) { &inst_error($webmin::text{'upgrade_efile'}); }
22 elsif ($in{'source'} == 1) {
24 &error_setup($webmin::text{'upgrade_err2'});
28 &inst_error($webmin::text{'upgrade_ebrowser'});
31 print MOD $in{'upload'};
34 elsif ($in{'source'} == 2) {
35 # find latest version at www.webmin.com by looking at index page
36 &error_setup($webmin::text{'upgrade_err3'});
38 &http_download($webmin::update_host, $webmin::update_port, '/', $file, \$error);
39 $error && &inst_error($error);
42 if (/webmin-([0-9\.]+)\.tar\.gz/) {
49 if ($in{'mode'} eq 'rpm') {
50 $progress_callback_url = "http://$webmin::update_host/download/rpm/webmin-$site_version-1.noarch.rpm";
51 &http_download($webmin::update_host, $webmin::update_port,
52 "/download/rpm/webmin-$site_version-1.noarch.rpm", $file,
53 \$error, \&progress_callback);
55 elsif ($in{'mode'} eq 'deb') {
56 # Downloading Debian package
57 $progress_callback_url = "http://$webmin::update_host/download/deb/webmin_${site_version}_all.deb";
58 &http_download($webmin::update_host, $webmin::update_port,
59 "/download/deb/webmin_${site_version}_all.deb", $file,
60 \$error, \&progress_callback);
63 $progress_callback_url = "http://$webmin::update_host/download/webmin-$site_version.tar.gz";
64 &http_download($webmin::update_host, $webmin::update_port,
65 "/download/webmin-$site_version.tar.gz", $file,
66 \$error, \&progress_callback);
68 $error && &inst_error($error);
71 elsif ($in{'source'} == 5) {
72 # Download from some URL
73 &error_setup(&webmin::text('upgrade_err5', $in{'url'}));
75 $progress_callback_url = $in{'url'};
76 if ($in{'url'} =~ /^(http|https):\/\/([^\/]+)(\/.*)$/) {
78 $host = $2; $page = $3; $port = $ssl ? 443 : 80;
79 if ($host =~ /^(.*):(\d+)$/) { $host = $1; $port = $2; }
80 &http_download($host, $port, $page, $file, \$error,
81 \&progress_callback, $ssl);
83 elsif ($in{'url'} =~ /^ftp:\/\/([^\/]+)(:21)?\/(.*)$/) {
84 $host = $1; $ffile = $3;
85 &ftp_download($host, $ffile, $file,
86 \$error, \&progress_callback);
88 else { &inst_error($webmin::text{'upgrade_eurl'}); }
90 $error && &inst_error($error);
93 # Import the signature for RPM
94 if (&has_command("rpm")) {
95 system("rpm --import $root_directory/webmin/jcameron-key.asc >/dev/null 2>&1");
98 # Work out what kind of file we have (RPM or tar.gz)
99 if (`rpm -qp $file 2>&1` =~ /(^|\n)webmin-(\d+\.\d+)/) {
100 # Looks like a webmin RPM
104 elsif (`dpkg --info $file 2>&1` =~ /Package:\s+webmin/) {
105 # Looks like a Webmin Debian package
107 `dpkg --info $file 2>&1` =~ /Version:\s+(\S+)/;
111 # Check if it is a webmin tar.gz file
112 open(TAR, "gunzip -c $file | tar tf - 2>&1 |");
115 if (/^webmin-([0-9\.]+)\//) {
118 if (/^usermin-([0-9\.]+)\//) {
119 $usermin_version = $1;
121 if (/^[^\/]+\/(\S+)$/) {
124 if (/^(webmin-([0-9\.]+)\/([^\/]+))$/ && $3 ne ".") {
125 # Found a top-level file, or *possibly* a directory
126 # under some versions of tar. Keep it so we know which
130 elsif (/^(webmin-[0-9\.]+\/([^\/]+))\// && $2 ne ".") {
131 # Found a sub-directory, like webmin-1.xx/foo/
132 # Keep this, so that we know which modules to extract.
133 # Also keep the full directory like webmin-1.xx/foo
134 # to avoid treating it as a file.
140 if ($usermin_version) {
141 &inst_error(&webmin::text('upgrade_eusermin',$usermin_version));
144 if ($hasfile{'module.info'}) {
145 &inst_error(&webmin::text('upgrade_emod', 'index.cgi'));
148 &inst_error($webmin::text{'upgrade_etar'});
154 # Check the signature if possible and if requested
156 # Check the package signature
157 ($ec, $emsg) = &webmin::gnupg_setup();
159 if ($mode eq 'rpm') {
160 # Use rpm's gpg signature verification
161 system("rpm --import $webmin::module_root_directory/jcameron-key.asc >/dev/null 2>&1");
162 local $out = `rpm --checksig $file 2>&1`;
165 $emsg = &webmin::text('upgrade_echecksig',
170 # Do a manual signature check
171 if ($in{'source'} == 2) {
172 # Download the key for this tar.gz
173 local ($sigtemp, $sigerror);
174 &http_download($webmin::update_host, $webmin::update_port, "/download/sigs/webmin-$version.tar.gz-sig.asc", \$sigtemp, \$sigerror);
177 $emsg = &webmin::text(
178 'upgrade_edownsig', $sigerror);
181 local $data = `cat $file`;
183 &webmin::verify_data(
187 $emsg = &webmin::text(
188 "upgrade_everify$vc",
189 &html_escape($vmsg));
194 $emsg = $webmin::text{'upgrade_nosig'};
199 # Tell the user about any GnuPG error
207 print "$webmin::text{'upgrade_sigok'}<p>\n";
211 print "$webmin::text{'upgrade_nocheck'}<p>\n";
214 # gunzip the file if needed
218 if ($two eq "\037\213") {
219 if (!&has_command("gunzip")) {
220 &inst_error($webmin::text{'upgrade_egunzip'});
222 $newfile = &tempname();
223 $out = `gunzip -c $file 2>&1 >$newfile`;
226 &inst_error(&webmin::text('upgrade_egzip', "<tt>$out</tt>"));
228 unlink($file) if ($need_unlink);
233 # Setup error handler for down hosts
236 $inst_error_msg = join("", @_);
238 &remote_error_setup(\&inst_error);
240 # Build list of selected hosts
241 @hosts = &list_webmin_hosts();
242 @servers = &list_servers();
243 if ($in{'server'} == -2) {
244 # Upgrade servers know to run older versions?
245 @hosts = grep { $_->{'version'} < $version } @hosts;
246 print "<b>",&text('upgrade_header3', $version),"</b><p>\n";
248 elsif ($in{'server'} =~ /^group_(.*)/) {
249 # Upgrade members of some group
250 local ($group) = grep { $_->{'name'} eq $1 }
251 &servers::list_all_groups(\@servers);
252 @hosts = grep { local $hid = $_->{'id'};
253 local ($s) = grep { $_->{'id'} == $hid } @servers;
254 &indexof($s->{'host'}, @{$group->{'members'}}) >= 0 }
256 print "<b>",&text('upgrade_header4', $group->{'name'}),"</b><p>\n";
258 elsif ($in{'server'} != -1) {
260 @hosts = grep { $_->{'id'} == $in{'server'} } @hosts;
261 local ($s) = grep { $_->{'id'} == $hosts[0]->{'id'} } @servers;
262 print "<b>",&text('upgrade_header2', &server_name($s)),"</b><p>\n";
265 # Upgrading every host
266 print "<p><b>",&text('upgrade_header'),"</b><p>\n";
271 foreach $h (@hosts) {
272 local ($s) = grep { $_->{'id'} == $h->{'id'} } @servers;
274 local ($rh = "READ$p", $wh = "WRITE$p");
276 select($wh); $| = 1; select(STDOUT);
278 # Do the install in a subprocess
281 if (!$s->{'fast'} && $s->{'id'} != 0) {
282 print $wh &serialise_variable($text{'upgrade_efast'});
285 &remote_foreign_require($s->{'host'}, "webmin","webmin-lib.pl");
286 if ($inst_error_msg) {
287 # Failed to contact host ..
288 print $wh &serialise_variable($inst_error_msg);
292 # Check the remote host's version
293 local $rver = &remote_foreign_call($s->{'host'}, "webmin",
294 "get_webmin_version");
295 if ($version == $rver) {
296 print $wh &serialise_variable(
297 &webmin::text('upgrade_elatest', $version));
300 elsif ($version <= $rver) {
301 print $wh &serialise_variable(
302 &webmin::text('upgrade_eversion', $version));
306 # Check the install type on the remote host
307 local $rmode = &remote_eval($s->{'host'}, "webmin",
308 "chop(\$m = `cat \$root_directory/install-type`); \$m");
309 if ($rmode ne $mode) {
310 print $wh &serialise_variable(
311 &text('upgrade_emode',
312 $text{'upgrade_mode_'.$rmode},
313 $text{'upgrade_mode_'.$mode}));
317 # Get the file to the server somehow
319 local $host_need_unlink = 1;
321 # This host, so we already have the file
323 $host_need_unlink = 0;
325 elsif ($in{'source'} == 0) {
326 # Is the file the same on remote? (like if we have NFS)
327 local @st = stat($file);
328 local $rst = &remote_eval($s->{'host'}, "webmin",
329 "[ stat('$file') ]");
331 if (@st && @rst && $st[7] == $rst[7] &&
333 # File is the same! No need to download
335 $host_need_unlink = 0;
338 # Need to copy the file across :(
339 $rfile = &remote_write(
340 $s->{'host'}, $file);
344 # Need to copy the file across :(
345 $rfile = &remote_write($s->{'host'}, $file);
349 if ($mode eq "rpm") {
350 # Can just run RPM command
351 # XXX doesn't actually check output!
352 &remote_eval($s->{'host'}, "webmin", "system(\"rpm --import \$root_directory/webmin/jcameron-key.asc >/dev/null 2>&1\")");
353 ($out, $ex) = &remote_eval($s->{'host'}, "webmin", "\$out = `rpm -U --ignoreos --ignorearch '$rfile' >/dev/null 2>&1 </dev/null`; (\$out, \$?)");
354 &remote_eval($s->{'host'}, "webmin", "unlink('$rfile')")
355 if ($host_need_unlink);
357 print $wh &serialise_variable(
362 elsif ($mode eq "deb") {
363 # Can just run dpkg command
364 ($out, $ex) = &remote_eval($s->{'host'}, "webmin", "\$out = `dpkg --install '$rfile' >/dev/null 2>&1 </dev/null`; (\$out, \$?)");
365 &remote_eval($s->{'host'}, "webmin", "unlink('$rfile')")
366 if ($host_need_unlink);
368 print $wh &serialise_variable(
374 # Get the original install directory
375 local $rdir = &remote_eval($s->{'host'}, "webmin",
376 "chop(\$d = `cat \$config_directory/install-dir 2>/dev/null`); \$d");
378 # Extract tar.gz in temporary location first
379 $extract = &remote_foreign_call($s->{'host'}, "webmin", "tempname");
380 &remote_eval($s->{'host'}, "webmin", "mkdir('$extract', 0755)");
383 # Extract next to original dir
384 $oldroot = &remote_eval($s->{'host'}, "webmin", "\$root_directory");
385 $extract = "$oldroot/..";
389 # Extract only root files and modules that we
391 $topfiles = join(" ", map { quotemeta($_) }
392 grep { !$tardir{$_} } @topfiles);
393 local ($out, $ex) = &remote_eval($s->{'host'}, "webmin", "\$out = `cd '$extract' ; tar xf '$rfile' $topfiles 2>&1 >/dev/null`; (\$out, \$?)");
395 print $wh &serialise_variable(
399 @mods = grep { $intar{$_} } map { $_->{'dir'} }
400 &remote_foreign_call($s->{'host'},
401 "webmin", "get_all_module_infos", 1);
402 opendir(DIR, $root_directory);
403 foreach $d (readdir(DIR)) {
404 next if ($d =~ /^\./);
405 local $p = "$root_directory/$d";
406 if (-d $p && !-r "$p/module.info" &&
413 map { quotemeta("webmin-$version/$_") }
415 local ($out, $ex) = &remote_eval($s->{'host'}, "webmin", "\$out = `cd '$extract' ; tar xf '$rfile' $mods 2>&1 >/dev/null`; (\$out, \$?)");
418 # Unpack the whole tar file
419 local ($out, $ex) = &remote_eval($s->{'host'}, "webmin", "\$out = `cd '$extract' ; tar xf '$rfile' 2>&1 >/dev/null`; (\$out, \$?)");
422 print $wh &serialise_variable(
427 # Delete the original tar.gz
428 &remote_eval($s->{'host'}, "webmin", "unlink('$rfile')")
429 if ($host_need_unlink);
431 # Run setup.sh in the extracted directory
432 $setup = $rdir ? "./setup.sh '$rdir'" : "./setup.sh";
433 ($out, $ex) = &remote_eval($s->{'host'}, "webmin",
434 "\$SIG{'TERM'} = 'IGNORE';
435 \$ENV{'config_dir'} = \$config_directory;
436 \$ENV{'webmin_upgrade'} = 1;
437 \$ENV{'autothird'} = 1;
438 \$out = `(cd $extract/webmin-$version && $setup) </dev/null 2>&1 | tee /tmp/.webmin/webmin-setup.out`;
440 if ($ex || $out !~ /success|^0$/i) {
441 print $wh &serialise_variable(
446 # Can delete the temporary source directory
447 &remote_eval($s->{'host'}, "webmin",
448 "system(\"rm -rf \'$extract\'\")");
450 elsif ($in{'delete'}) {
451 # Can delete the old root directory
452 &remote_eval($s->{'host'}, "webmin",
453 "system(\"rm -rf \'$oldroot\'\")");
457 # Force an RPC re-connect to new version
459 &remote_foreign_require($s->{'host'}, "webmin","webmin-lib.pl");
460 if ($inst_error_msg) {
461 # Failed to contact host ..
462 print $wh &serialise_variable(
463 &text('upgrade_ereconn', $inst_error_msg));
466 &remote_foreign_require($s->{'host'}, "acl", "acl-lib.pl");
468 # Update local version number and module lists
469 $h->{'version'} = $version;
470 local @mods = &remote_foreign_call($s->{'host'},
471 "webmin", "get_all_module_infos", 1);
472 @mods = grep { !$_->{'clone'} } @mods;
473 $h->{'modules'} = \@mods;
474 local @themes = &remote_foreign_call($s->{'host'},
475 "webmin", "list_themes");
476 $h->{'themes'} = \@themes;
477 local @users = &remote_foreign_call($s->{'host'},
478 "acl", "list_users");
479 $h->{'users'} = \@users;
480 local @groups = &remote_foreign_call($s->{'host'},
481 "acl", "list_groups");
482 $h->{'groups'} = \@groups;
483 &save_webmin_host($h);
485 print $wh &serialise_variable("");
493 # Get back all the results
495 foreach $h (@hosts) {
496 local $rh = "READ$p";
499 local $rv = &unserialise_variable($line);
501 local ($s) = grep { $_->{'id'} == $h->{'id'} } @servers;
502 local $d = &server_name($s);
505 print &text('upgrade_failed', $d, "Unknown reason"),"<br>\n";
508 print &text('upgrade_failed', $d, $rv),"<br>\n";
511 print &text('upgrade_ok',
512 $text{'upgrade_mode_'.$mode}, $d),"<br>\n";
516 unlink($file) if ($need_unlink);
517 print "<p><b>$text{'upgrade_done'}</b><p>\n";
520 &ui_print_footer("", $text{'index_return'});
524 unlink($file) if ($need_unlink);
525 print "<br><b>$whatfailed : $_[0]</b> <p>\n";
526 &ui_print_footer("", $text{'index_return'});