Handle hostnames with upper-case letters
[webmin.git] / cluster-software / do_install.cgi
1 #!/usr/local/bin/perl
2 # do_install.cgi
3 # Install some package on all hosts, in parallel. If the package was
4 # downloaded from a URL, have the hosts do the same - otherwise, transfer
5 # it to each.
6
7 require './cluster-software-lib.pl';
8 &ReadParse();
9
10 # Work out package names, for display to use
11 @packages = $in{'source'} == 3 ? split(/\s+/, $in{'file'}) :
12             $in{'unknownfile'} ? ( $in{'unknownfile'} ) :
13                                  &software::file_packages($in{'file'});
14 foreach $p (@packages) {
15         local ($n, $d) = split(/\s+/, $p, 2);
16         push(@names, $n);
17         push(@descs, $d || $n);
18         }
19
20 $in{'source'} == 3 || -r $in{'file'} || &error($text{'do_edeleted'});
21 &ui_print_header(undef, $text{'do_title'}, "");
22
23 # Setup error handler for down hosts
24 sub inst_error
25 {
26 $inst_error_msg = join("", @_);
27 }
28 &remote_error_setup(\&inst_error);
29
30 # Work out hosts to install on
31 @hosts = &list_software_hosts();
32 @already = grep { local ($alr) = grep { $_->{'name'} eq $names[0] }
33                                     @{$_->{'packages'}};
34                   $alr } @hosts;
35 @hosts = &create_on_parse("do_header", \@already, join(" ", @names));
36 @servers = &list_servers();
37
38 $p = 0;
39 foreach $h (@hosts) {
40         # Install on one host
41         local ($s) = grep { $_->{'id'} == $h->{'id'} } @servers;
42         local $d = $s->{'desc'} || $s->{'realhost'} || $s->{'host'};
43
44         local ($rh = "READ$p", $wh = "WRITE$p");
45         pipe($rh, $wh);
46         select($wh); $| = 1; select(STDOUT);
47         if (!fork()) {
48                 # Do the install in a subprocess
49                 close($rh);
50
51                 &remote_foreign_require($s->{'host'}, "software",
52                                         "software-lib.pl");
53                 if ($inst_error_msg) {
54                         # Failed to contact host ..
55                         print $wh &serialise_variable([ $inst_error_msg ]);
56                         exit;
57                         }
58                 local $rfile;
59                 local $need_unlink = 1;
60                 if ($in{'source'} == 3) {
61                         # Installing from an update service like APT or YUM
62                         $need_unlink = 0;
63                         }
64                 elsif (!$s->{'id'}) {
65                         # This host, so we already have the file
66                         $rfile = $in{'file'};
67                         $need_unlink = 0;
68                         }
69                 elsif ($in{'source'} == 0) {
70                         # Is the file the same on remote (like if we have NFS)
71                         local @st = stat($in{'file'});
72                         local $rst = &remote_eval($s->{'host'}, "software",
73                                                   "[ stat('$in{'file'}') ]");
74                         local @rst = @$rst;
75                         if (@st && @rst && $st[7] == $rst[7] &&
76                             $st[9] == $rst[9]) {
77                                 # File is the same! No need to download
78                                 $rfile = $in{'file'};
79                                 $need_unlink = 0;
80                                 }
81                         else {
82                                 # Need to copy the file across :(
83                                 local $filename = $in{'file'};
84                                 $filename =~ /([^\/\\]+)$/;
85                                 $rfile = &remote_write(
86                                         $s->{'host'}, $in{'file'}, undef, "$1");
87                                 }
88                         }
89                 elsif ($in{'source'} == 2 && $in{'down'}) {
90                         # Ask the remote server to download the file
91                         local $filename = $in{'file'};
92                         $filename =~ /([^\/\\]+$)/;
93                         $rfile = &remote_foreign_call($s->{'host'}, "software",
94                                                       "tempname", $1);
95                         if ($in{'ftpfile'}) {
96                                 &remote_foreign_call($s->{'host'}, "software",
97                                     "ftp_download", $in{'host'}, $in{'ftpfile'},
98                                     $rfile);
99                                 }
100                         else {
101                                 &remote_foreign_call($s->{'host'}, "software",
102                                     "http_download", $in{'host'}, $in{'port'},
103                                     $in{'page'}, $rfile, undef, undef,
104                                     $in{'ssl'});
105                                 }
106                         }
107                 else {
108                         # Need to copy the file across :(
109                         local $filename = $in{'file'};
110                         $filename =~ /([^\/\\]+)$/;
111                         $rfile = &remote_write($s->{'host'}, $in{'file'},
112                                                undef, "$1");
113                         }
114
115                 # Do the install ..
116                 local @rv;
117                 if ($in{'source'} != 3) {
118                         # Installing some package
119                         for($i=0; $i<@names; $i++) {
120                                 local $error = &remote_foreign_call(
121                                         $s->{'host'}, "software",
122                                         "install_package", $rfile,
123                                         $names[$i], \%in);
124                                 if ($error) {
125                                         push(@rv, $error);
126                                         }
127                                 else {
128                                         # Success .. get the package details
129                                         push(@rv, [ &remote_foreign_call($s->{'host'}, "software", "package_info", $names[$i]) ] );
130                                         }
131                                 }
132                         }
133                 else {
134                         # Install from update system
135                         local $rus = &remote_eval($s->{'host'}, "software",
136                                                   '$update_system');
137                         if ($rus ne $software::update_system) {
138                                 push(@rv, &text('install_erus',
139                                         $rus, $software::update_system));
140                                 }
141                         else {
142                                 local @resp = &remote_foreign_call($s->{'host'},
143                                         "software", "capture_function_output",
144                                         "update_system_install", $in{'file'});
145                                 if (@{$resp[1]}) {
146                                         # Worked .. get package details
147                                         foreach $p (@{$resp[1]}) {
148                                                 push(@rv, [ &remote_foreign_call($s->{'host'}, "software", "package_info", $p) ] );
149                                                 }
150                                         }
151                                 else {
152                                         push(@rv, $text{'install_eupdate'});
153                                         }
154                                 }
155                         }
156                 &remote_eval($s->{'host'}, "software", "unlink('$rfile')")
157                         if ($need_unlink);
158
159                 print $wh &serialise_variable(\@rv);
160                 close($wh);
161                 exit;
162                 }
163         close($wh);
164         $p++;
165         }
166
167 # Get back all the results
168 $p = 0;
169 foreach $h (@hosts) {
170         local $rh = "READ$p";
171         local $line = <$rh>;
172         close($rh);
173         local $rv = &unserialise_variable($line);
174
175         local ($s) = grep { $_->{'id'} == $h->{'id'} } @servers;
176         local $d = $s->{'desc'} || $s->{'realhost'} || $s->{'host'};
177
178         if (!$line) {
179                 print &text('do_failed', $d, "Unknown reason"),"<br>\n";
180                 }
181         else {
182                 $i=0;
183                 foreach $r (@$rv) {
184                         if (ref($r)) {
185                                 # Install went ok!
186                                 print &text('do_success', $d),"<br>\n";
187                                 $pinfo[$i] = $r if (!$pinfo[$i] && @$r);
188                                 if (!@$r) {
189                                         # Failed to get info! Need a refresh..
190                                         $refresh{$s->{'id'}} = 1;
191                                         }
192                                 elsif (&indexof($names[$i],
193                                              @{$h->{'packages'}}) < 0) {
194                                         push(@{$h->{'packages'}},
195                                              { 'name' => $names[$i],
196                                                'desc' => $descs[$i],
197                                                'class' => $pinfo[$i]->[1],
198                                                'version' => $pinfo[$i]->[4] });
199                                         &save_software_host($h);
200                                         }
201                                 }
202                         else {
203                                 # Failed for some reason..
204                                 print &text('do_failed', $d, $r),"<br>\n";
205                                 }
206                         $i++;
207                         }
208                 }
209         $p++;
210         }
211
212 unlink($in{'file'}) if ($in{'need_unlink'});
213 print "<p><b>$text{'do_done'}</b><p>\n";
214
215 # Show details of installed packages, where we have them
216 for($i=0; $i<@names; $i++) {
217         next if (!$pinfo[$i]);
218         print "<table border width=100%>\n";
219         print "<tr $tb> <td><b>$text{'do_details'}</b></td> </tr>\n";
220         print "<tr $cb> <td><table width=100%>\n";
221
222         if ($pinfo[$i]->[2]) {
223                 print "<tr> <td valign=top width=20%><b>$text{'do_desc'}</b></td>\n";
224                 print "<td colspan=3><pre>$pinfo[$i]->[2]</pre></td> </tr>\n";
225                 }
226
227         print "<tr> <td width=20%><b>$text{'do_pack'}</b></td> <td>$pinfo[$i]->[0]</td>\n";
228         print "<td width=20%><b>$text{'do_class'}</b></td> <td>",
229                 $pinfo[$i]->[1] ? $pinfo[$i]->[1] : $text{'do_none'},"</td> </tr>\n";
230
231         print "<tr> <td width=20%><b>$text{'do_ver'}</b></td> <td>$pinfo[$i]->[4]</td>\n";
232         print "<td width=20%><b>$text{'do_vend'}</b></td> <td>$pinfo[$i]->[5]</td> </tr>\n";
233
234         print "<tr> <td width=20%><b>$text{'do_arch'}</b></td> <td>$pinfo[$i]->[3]</td>\n";
235         print "<td width=20%><b>$text{'do_inst'}</b></td> <td>$pinfo[$i]->[6]</td> </tr>\n";
236         print "</table></td></tr></table><p>\n";
237         }
238
239 &remote_finished();
240 &ui_print_footer("", $text{'index_return'});
241