install 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. #!/usr/bin/perl
  2. #
  3. # Copyright © 1996 Andy Guy <awpguy@acs.ucalgary.ca>
  4. # Copyright © 1998 Martin Schulze <joey@infodrom.north.de>
  5. # Copyright © 1999, 2009 Raphaël Hertzog <hertzog@debian.org>
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; version 2 of the License.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. use strict;
  19. use warnings;
  20. use vars qw(%config $ftp);
  21. #use diagnostics;
  22. use lib '/usr/lib/perl5/Debian';
  23. use lib '/usr/share/perl5/Debian';
  24. eval q{
  25. use Net::FTP;
  26. use File::Path;
  27. use File::Basename;
  28. use File::Find;
  29. use Data::Dumper;
  30. };
  31. if ($@) {
  32. print STDERR "Please install the 'perl' package if you want to use the\n" .
  33. "FTP access method of dselect.\n\n";
  34. exit 1;
  35. }
  36. use Dselect::Ftp;
  37. # exit value
  38. my $exit = 0;
  39. # deal with arguments
  40. my $vardir = $ARGV[0];
  41. my $method = $ARGV[1];
  42. my $option = $ARGV[2];
  43. if ($option eq "manual" ) {
  44. print "manual mode not supported yet\n";
  45. exit 1;
  46. }
  47. #print "vardir: $vardir, method: $method, option: $option\n";
  48. my $methdir = "$vardir/methods/ftp";
  49. # get info from control file
  50. read_config("$methdir/vars");
  51. chdir "$methdir";
  52. mkpath(["$methdir/$config{'dldir'}"], 0, 0755);
  53. #Read md5sums already calculated
  54. my %md5sums;
  55. if (-f "$methdir/md5sums") {
  56. local $/;
  57. open(MD5SUMS, "$methdir/md5sums") ||
  58. die "Couldn't read file $methdir/md5sums";
  59. my $code = <MD5SUMS>;
  60. close MD5SUMS;
  61. use vars qw($VAL1);
  62. my $res = eval $code;
  63. if ($@) {
  64. die "Couldn't eval $methdir/md5sums content: $@\n";
  65. }
  66. if (ref($res)) { %md5sums = %{$res} }
  67. }
  68. # get a block
  69. # returns a ref to a hash containing flds->fld contents
  70. # white space from the ends of lines is removed and newlines added
  71. # (no trailing newline).
  72. # die's if something unexpected happens
  73. sub getblk {
  74. my $fh = shift;
  75. my %flds;
  76. my $fld;
  77. while (<$fh>) {
  78. if ( ! /^$/ ) {
  79. FLDLOOP: while (1) {
  80. if ( /^(\S+):\s*(.*)\s*$/ ) {
  81. $fld = lc($1);
  82. $flds{$fld} = $2;
  83. while (<$fh>) {
  84. if ( /^$/ ) {
  85. return %flds;
  86. } elsif ( /^(\s.*)$/ ) {
  87. $flds{$fld} = $flds{$fld} . "\n" . $1;
  88. } else {
  89. next FLDLOOP;
  90. }
  91. }
  92. return %flds;
  93. } else {
  94. die "Expected a start of field line, but got:\n$_";
  95. }
  96. }
  97. }
  98. }
  99. return %flds;
  100. }
  101. # process status file
  102. # create curpkgs hash with version (no version implies not currently installed)
  103. # of packages we want
  104. print "Processing status file...\n";
  105. my %curpkgs;
  106. sub procstatus {
  107. my (%flds, $fld);
  108. open (STATUS, "$vardir/status") or die "Could not open status file";
  109. while (%flds = getblk(\*STATUS), %flds) {
  110. if($flds{'status'} =~ /^install ok/) {
  111. my $cs = (split(/ /, $flds{'status'}))[2];
  112. if(($cs eq "not-installed") ||
  113. ($cs eq "half-installed") ||
  114. ($cs eq "config-files")) {
  115. $curpkgs{$flds{'package'}} = "";
  116. } else {
  117. $curpkgs{$flds{'package'}} = $flds{'version'};
  118. }
  119. }
  120. }
  121. close(STATUS);
  122. }
  123. procstatus();
  124. sub dcmpvers {
  125. my($a, $p, $b) = @_;
  126. my ($r);
  127. $r = system("dpkg", "--compare-versions", "$a", "$p", "$b");
  128. $r = $r/256;
  129. if ($r == 0) {
  130. return 1;
  131. } elsif ($r == 1) {
  132. return 0;
  133. }
  134. die "dpkg --compare-versions $a $p $b - failed with $r";
  135. }
  136. # process package files, looking for packages to install
  137. # create a hash of these packages pkgname => version, filenames...
  138. # filename => md5sum, size
  139. # for all packages
  140. my %pkgs;
  141. my %pkgfiles;
  142. sub procpkgfile {
  143. my $fn = shift;
  144. my $site = shift;
  145. my $dist = shift;
  146. my(@files,@sizes,@md5sums,$pkg,$ver,$fl,$nfs,$fld);
  147. my(%flds);
  148. open(PKGFILE, "$fn") or die "Could not open package file $fn";
  149. while(%flds = getblk(\*PKGFILE), %flds) {
  150. $pkg = $flds{'package'};
  151. $ver = $curpkgs{$pkg};
  152. @files = split(/[\s\n]+/, $flds{'filename'});
  153. @sizes = split(/[\s\n]+/, $flds{'size'});
  154. @md5sums = split(/[\s\n]+/, $flds{'md5sum'});
  155. if ( defined($ver) && ( ($ver eq "") || dcmpvers( $ver, "lt", $flds{'version'} ) )) {
  156. $pkgs{$pkg} = [ $flds{'version'}, [ @files ], $site ];
  157. $curpkgs{$pkg} = $flds{'version'};
  158. }
  159. $nfs = scalar(@files);
  160. if(($nfs != scalar(@sizes)) || ($nfs != scalar(@md5sums)) ) {
  161. print "Different number of filenames, sizes and md5sums for $flds{'package'}\n";
  162. } else {
  163. my $i = 0;
  164. foreach $fl (@files) {
  165. $pkgfiles{$fl} = [ $md5sums[$i], $sizes[$i], $site, $dist ];
  166. $i++;
  167. }
  168. }
  169. }
  170. }
  171. print "\nProcessing Package files...\n";
  172. my ($dist,$site,$fn,$i,$j);
  173. $i = 0;
  174. foreach $site (@{$config{'site'}}) {
  175. $j = 0;
  176. foreach $dist (@{$site->[2]}) {
  177. $fn = $dist;
  178. $fn =~ tr#/#_#;
  179. $fn = "Packages.$site->[0].$fn";
  180. if (-f $fn) {
  181. print " $site->[0] $dist...\n";
  182. procpkgfile($fn,$i,$j);
  183. } else {
  184. print "Could not find packages file for $site->[0] $dist distribution (re-run Update)\n"
  185. }
  186. $j++;
  187. }
  188. $i++;
  189. }
  190. my $dldir = $config{'dldir'};
  191. # md5sum
  192. sub md5sum($) {
  193. my $fn = shift;
  194. my $m = `md5sum $fn`;
  195. $m = (split(" ", $m))[0];
  196. $md5sums{"$dldir/$fn"} = $m;
  197. return $m;
  198. }
  199. # construct list of files to get
  200. # hash of filenames => size of downloaded part
  201. # query user for each paritial file
  202. print "\nConstructing list of files to get...\n";
  203. my %downloads;
  204. my ($pkg, $dir, @info, @files, $csize, $size);
  205. my $totsize = 0;
  206. foreach $pkg (keys(%pkgs)) {
  207. @files = @{$pkgs{$pkg}[1]};
  208. foreach $fn (@files) {
  209. #Look for a partial file
  210. if (-f "$dldir/$fn.partial") {
  211. rename "$dldir/$fn.partial", "$dldir/$fn";
  212. }
  213. $dir = dirname($fn);
  214. if(! -d "$dldir/$dir") {
  215. mkpath(["$dldir/$dir"], 0, 0755);
  216. }
  217. @info = @{$pkgfiles{$fn}};
  218. $csize = int($info[1]/1024)+1;
  219. if(-f "$dldir/$fn") {
  220. $size = -s "$dldir/$fn";
  221. if($info[1] > $size) {
  222. # partial download
  223. if(yesno("y", "continue file: $fn (" . nb($size) ."/" .
  224. nb($info[1]). ")")) {
  225. $downloads{$fn} = $size;
  226. $totsize += $csize - int($size/1024);
  227. } else {
  228. $downloads{$fn} = 0;
  229. $totsize += $csize;
  230. }
  231. } else {
  232. # check md5sum
  233. if (! exists $md5sums{"$dldir/$fn"}) {
  234. $md5sums{"$dldir/$fn"} = md5sum("$dldir/$fn");
  235. }
  236. if ($md5sums{"$dldir/$fn"} eq $info[0]) {
  237. print "already got: $fn\n";
  238. } else {
  239. print "corrupted: $fn\n";
  240. $downloads{$fn} = 0;
  241. }
  242. }
  243. } else {
  244. my $ffn = $fn;
  245. $ffn =~ s/binary-[^\/]+/.../;
  246. print "want: " .
  247. $config{'site'}[$pkgfiles{$fn}[2]][0] . " $ffn (${csize}k)\n";
  248. $downloads{$fn} = 0;
  249. $totsize += $csize;
  250. }
  251. }
  252. }
  253. my $avsp = `df -Pk $dldir| awk '{ print \$4}' | tail -n 1`;
  254. chomp $avsp;
  255. print "\nApproximate total space required: ${totsize}k\n";
  256. print "Available space in $dldir: ${avsp}k\n";
  257. #$avsp = `df -k $::dldir| paste -s | awk '{ print \$11}'`;
  258. #chomp $avsp;
  259. if($totsize == 0) {
  260. print "Nothing to get.";
  261. } else {
  262. if($totsize > $avsp) {
  263. print "Space required is greater than available space,\n";
  264. print "you will need to select which items to get.\n";
  265. }
  266. # ask user which files to get
  267. if(($totsize > $avsp) || yesno("n", "Do you want to select the files to get")) {
  268. $totsize = 0;
  269. my @files = sort(keys(%downloads));
  270. my $fn;
  271. my $def = "y";
  272. foreach $fn (@files) {
  273. my @info = @{$pkgfiles{$fn}};
  274. my $csize = int($info[1] / 1024) + 1;
  275. my $rsize = int(($info[1] - $downloads{$fn}) / 1024) + 1;
  276. if ($rsize + $totsize > $avsp) {
  277. print "no room for: $fn\n";
  278. delete $downloads{$fn};
  279. } else {
  280. if(yesno($def, $downloads{$fn}
  281. ? "download: $fn ${rsize}k/${csize}k (total = ${totsize}k)"
  282. : "download: $fn ${rsize}k (total = ${totsize}k)")) {
  283. $def = "y";
  284. $totsize += $rsize;
  285. } else {
  286. $def = "n";
  287. delete $downloads{$fn};
  288. }
  289. }
  290. }
  291. }
  292. }
  293. sub download() {
  294. my $i = 0;
  295. my ($site, $ftp);
  296. foreach $site (@{$config{'site'}}) {
  297. my @getfiles = grep { $pkgfiles{$_}[2] == $i } keys %downloads;
  298. my @pre_dist = (); # Directory to add before $fn
  299. #Scan distributions for looking at "(../)+/dir/dir"
  300. my ($n,$cp);
  301. $cp = -1;
  302. foreach (@{$site->[2]}) {
  303. $cp++;
  304. $pre_dist[$cp] = "";
  305. $n = (s#\.\./#../#g);
  306. next if (! $n);
  307. if (m#^((?:\.\./){$n}(?:[^/]+/){$n})#) {
  308. $pre_dist[$cp] = $1;
  309. }
  310. }
  311. if (! @getfiles) { $i++; next; }
  312. $ftp = do_connect ($site->[0], #$::ftpsite,
  313. $site->[4], #$::username,
  314. $site->[5], #$::password,
  315. $site->[1], #$::ftpdir,
  316. $site->[3], #$::passive,
  317. $config{'use_auth_proxy'},
  318. $config{'proxyhost'},
  319. $config{'proxylogname'},
  320. $config{'proxypassword'});
  321. $::ftp = $ftp;
  322. local $SIG{'INT'} = sub { die "Interrupted !\n"; };
  323. my ($fn,$rsize,$res,$pre);
  324. foreach $fn (@getfiles) {
  325. $pre = $pre_dist[$pkgfiles{$fn}[3]] || "";
  326. if ($downloads{$fn}) {
  327. $rsize = ${pkgfiles{$fn}}[1] - $downloads{$fn};
  328. print "getting: $pre$fn (". nb($rsize) . "/" .
  329. nb($pkgfiles{$fn}[1]) . ")\n";
  330. } else {
  331. print "getting: $pre$fn (". nb($pkgfiles{$fn}[1]) . ")\n";
  332. }
  333. $res = $ftp->get("$pre$fn", "$dldir/$fn", $downloads{$fn});
  334. if(! $res) {
  335. my $r = $ftp->code();
  336. print $ftp->message() . "\n";
  337. if (!($r == 550 || $r == 450)) {
  338. return 1;
  339. } else {
  340. #Try to find another file or this package
  341. print "Looking for another version of the package...\n";
  342. my ($dir,$package) = ($fn =~ m#^(.*)/([^/]+)_[^/]+.deb$#);
  343. my $protected = $package;
  344. $protected =~ s/\+/\\\+/g;
  345. my $list = $ftp->ls("$pre$dir");
  346. if ($ftp->ok() && ref($list)) {
  347. foreach (@{$list}) {
  348. if (m/($dir\/${protected}_[^\/]+.deb)/i) {
  349. print "Package found : $_\n";
  350. print "getting: $_ (size not known)\n";
  351. $res = $ftp->get($_, "$dldir/$1");
  352. if (! $res) {
  353. $r = $ftp->code();
  354. print $ftp->message() . "\n";
  355. return 1 if ($r != 550 and $r != 450);
  356. }
  357. }
  358. }
  359. }
  360. }
  361. }
  362. # fully got, remove it from list in case we have to re-download
  363. delete $downloads{$fn};
  364. }
  365. $ftp->quit();
  366. $i++;
  367. }
  368. return 0;
  369. }
  370. # download stuff (protect from ^C)
  371. if($totsize != 0) {
  372. if(yesno("y", "\nDo you want to download the required files")) {
  373. DOWNLOAD_TRY: while (1) {
  374. print "Downloading files... use ^C to stop\n";
  375. eval {
  376. if ((download() == 1) && yesno("y", "\nDo you want to retry downloading at once")) {
  377. next DOWNLOAD_TRY;
  378. }
  379. };
  380. if($@ =~ /Interrupted|Timeout/i ) {
  381. # close the FTP connection if needed
  382. if ((ref($::ftp) =~ /Net::FTP/) and ($@ =~ /Interrupted/i)) {
  383. $::ftp->abort();
  384. $::ftp->quit();
  385. undef $::ftp;
  386. }
  387. print "FTP ERROR\n";
  388. if (yesno("y", "\nDo you want to retry downloading at once")) {
  389. # get the first $fn that foreach would give:
  390. # this is the one that got interrupted.
  391. my $ffn;
  392. MY_ITER: foreach $ffn (keys(%downloads)) {
  393. $fn = $ffn;
  394. last MY_ITER;
  395. }
  396. my $size = -s "$dldir/$fn";
  397. # partial download
  398. if(yesno("y", "continue file: $fn (at $size)")) {
  399. $downloads{$fn} = $size;
  400. } else {
  401. $downloads{$fn} = 0;
  402. }
  403. next DOWNLOAD_TRY;
  404. } else {
  405. $exit = 1;
  406. last DOWNLOAD_TRY;
  407. }
  408. } elsif ($@) {
  409. print "An error occurred ($@) : stopping download\n";
  410. }
  411. last DOWNLOAD_TRY;
  412. }
  413. }
  414. }
  415. # remove duplicate packages (keep latest versions)
  416. # move half downloaded files out of the way
  417. # delete corrupted files
  418. print "\nProcessing downloaded files...(for corrupt/old/partial)\n";
  419. my %vers; # package => version
  420. my %files; # package-version => files...
  421. # check a deb or split deb file
  422. # return 1 if it a deb file, 2 if it is a split deb file
  423. # else 0
  424. sub chkdeb($) {
  425. my ($fn) = @_;
  426. # check to see if it is a .deb file
  427. if(!system("dpkg-deb --info $fn 2>&1 >/dev/null && dpkg-deb --contents $fn 2>&1 >/dev/null")) {
  428. return 1;
  429. } elsif(!system("dpkg-split --info $fn 2>&1 >/dev/null")) {
  430. return 2;
  431. }
  432. return 0;
  433. }
  434. sub getdebinfo($) {
  435. my ($fn) = @_;
  436. my $type = chkdeb($fn);
  437. my ($pkg, $ver);
  438. if($type == 1) {
  439. open(PKGFILE, "dpkg-deb --field $fn |");
  440. my %fields = getblk(\*PKGFILE);
  441. close(PKGFILE);
  442. $pkg = $fields{'package'};
  443. $ver = $fields{'version'};
  444. if($fields{'package_revision'}) { $ver .= '-' . $fields{'package_revision'}; }
  445. return $pkg, $ver;
  446. } elsif ( $type == 2) {
  447. open(PKGFILE, "dpkg-split --info $fn|");
  448. while(<PKGFILE>) {
  449. /Part of package:\s*(\S+)/ and $pkg = $+;
  450. /\.\.\. version:\s*(\S+)/ and $ver = $+;
  451. }
  452. close(PKGFILE);
  453. return $pkg, $ver;
  454. }
  455. print "could not figure out type of $fn\n";
  456. return $pkg, $ver;
  457. }
  458. # process deb file to make sure we only keep latest versions
  459. sub prcdeb($$) {
  460. my ($dir, $fn) = @_;
  461. my ($pkg, $ver) = getdebinfo($fn);
  462. if(!defined($pkg) || !defined($ver)) {
  463. print "could not get package info from file\n";
  464. return 0;
  465. }
  466. if($vers{$pkg}) {
  467. if(dcmpvers($vers{$pkg}, "eq", $ver)) {
  468. $files{$pkg . $ver} = [ $files{$pkg . $ver }, "$dir/$fn" ];
  469. } elsif (dcmpvers($vers{$pkg}, "gt", $ver)) {
  470. print "old version\n";
  471. unlink $fn;
  472. } else { # else $ver is gt current version
  473. my ($c);
  474. foreach $c (@{$files{$pkg . $vers{$pkg}}}) {
  475. print "replaces: $c\n";
  476. unlink "$vardir/methods/ftp/$dldir/$c";
  477. }
  478. $vers{$pkg} = $ver;
  479. $files{$pkg . $ver} = [ "$dir/$fn" ];
  480. }
  481. } else {
  482. $vers{$pkg} = $ver;
  483. $files{$pkg . $ver} = [ "$dir/$fn" ];
  484. }
  485. }
  486. sub prcfile() {
  487. my ($fn) = $_;
  488. if (-f $fn and $fn ne '.') {
  489. my $dir = ".";
  490. if (length($File::Find::dir) > length($dldir)) {
  491. $dir = substr($File::Find::dir, length($dldir)+1);
  492. }
  493. print "$dir/$fn\n";
  494. if(defined($pkgfiles{"$dir/$fn"})) {
  495. my @info = @{$pkgfiles{"$dir/$fn"}};
  496. my $size = -s $fn;
  497. if($size == 0) {
  498. print "zero length file\n";
  499. unlink $fn;
  500. } elsif($size < $info[1]) {
  501. print "partial file\n";
  502. rename $fn, "$fn.partial";
  503. } elsif(( (exists $md5sums{"$dldir/$fn"})
  504. and ($md5sums{"$dldir/$fn"} ne $info[0]) )
  505. or
  506. (md5sum($fn) ne $info[0])) {
  507. print "corrupt file\n";
  508. unlink $fn;
  509. } else {
  510. prcdeb($dir, $fn);
  511. }
  512. } elsif($fn =~ /.deb$/) {
  513. if(chkdeb($fn)) {
  514. prcdeb($dir, $fn);
  515. } else {
  516. print "corrupt file\n";
  517. unlink $fn;
  518. }
  519. } else {
  520. print "non-debian file\n";
  521. }
  522. }
  523. }
  524. find(\&prcfile, "$dldir/");
  525. # install .debs
  526. if(yesno("y", "\nDo you want to install the files fetched")) {
  527. print "Installing files...\n";
  528. #Installing pre-dependent package before !
  529. my (@flds, $package, @filename, $r);
  530. while (@flds = `dpkg --predep-package`, $? == 0) {
  531. foreach (@flds) {
  532. s/\s*\n//;
  533. $package= $_ if s/^Package: //i;
  534. @filename= split(/ +/,$_) if s/^Filename: //i;
  535. }
  536. @filename = map { "$dldir/$_" } @filename;
  537. next if (! @filename);
  538. $r = system('dpkg', '-iB', '--', @filename);
  539. if ($r) { print "DPKG ERROR\n"; $exit = 1; }
  540. }
  541. #Installing other packages after
  542. $r = system("dpkg", "-iGREOB", $dldir);
  543. if($r) {
  544. print "DPKG ERROR\n";
  545. $exit = 1;
  546. }
  547. }
  548. sub removeinstalled {
  549. my $fn = $_;
  550. if (-f $fn and $fn ne '.') {
  551. my $dir = ".";
  552. if (length($File::Find::dir) > length($dldir)) {
  553. $dir = substr($File::Find::dir, length($dldir)+1);
  554. }
  555. if($fn =~ /.deb$/) {
  556. my($pkg, $ver) = getdebinfo($fn);
  557. if(!defined($pkg) || !defined($ver)) {
  558. print "Could not get info for: $dir/$fn\n";
  559. } else {
  560. if($curpkgs{$pkg} and dcmpvers($ver, "le", $curpkgs{$pkg})) {
  561. print "deleting: $dir/$fn\n";
  562. unlink $fn;
  563. } else {
  564. print "leaving: $dir/$fn\n";
  565. }
  566. }
  567. } else {
  568. print "non-debian: $dir/$fn\n";
  569. }
  570. }
  571. }
  572. # remove .debs that have been installed (query user)
  573. # first need to reprocess status file
  574. if(yesno("y", "\nDo you wish to delete the installed package (.deb) files?")) {
  575. print "Removing installed files...\n";
  576. %curpkgs = ();
  577. procstatus();
  578. find(\&removeinstalled, "$dldir/");
  579. }
  580. # remove whole ./debian directory if user wants to
  581. if(yesno("n", "\nDo you want to remove $dldir directory?")) {
  582. rmtree("$dldir");
  583. }
  584. #Store useful md5sums
  585. foreach (keys %md5sums) {
  586. next if (-f $_);
  587. delete $md5sums{$_};
  588. }
  589. open(MD5SUMS, ">$methdir/md5sums") ||
  590. die "Can't open $methdir/md5sums in write mode : $!\n";
  591. print MD5SUMS Dumper(\%md5sums);
  592. close MD5SUMS;
  593. exit $exit;