retrofitted mobundle from other project
[deployable.git] / deployable
blob5b86f693648ca0ea84f78985551e76a64f7e6a2c
1 #!/usr/bin/env perl
2 use strict;
3 use warnings;
4 use Carp;
5 use version; our $VERSION = qv('0.1.1');
6 use Fatal qw( close );
7 use Pod::Usage qw( pod2usage );
8 use Getopt::Long qw( :config gnu_getopt );
9 use English qw( -no_match_vars );
10 use File::Basename qw( basename dirname );
11 use File::Spec::Functions qw( file_name_is_absolute catfile );
12 use File::Temp qw( tempfile );
13 use POSIX qw( strftime );
14 use Cwd qw( cwd realpath );
15 use Archive::Tar;
16 use File::Find::Rule;
17 use Data::Dumper;
18 use Encode;
20 my %config = (
21 output => '-',
22 remote => catfile(dirname(realpath(__FILE__)), 'remote'),
23 tarfile => [],
24 heredir => [],
25 rootdir => [],
26 root => [],
27 deploy => [],
29 GetOptions(
30 \%config,
31 qw(
32 usage! help! man! version!
34 bundle|all-exec|X!
35 bzip2|bz2|j!
36 cleanup|c!
37 deploy|exec|d=s@
38 gzip|gz|z!
39 heredir|H=s@
40 include-archive-tar|T!
41 no-tar!
42 output|o=s
43 root|r=s@
44 rootdir|R=s@
45 tar|t=s
46 workdir|work-directory|deploy-directory|w=s
49 pod2usage(message => "$0 $VERSION", -verbose => 99, -sections => '')
50 if $config{version};
51 pod2usage(-verbose => 99, -sections => 'USAGE') if $config{usage};
52 pod2usage(-verbose => 99, -sections => 'USAGE|EXAMPLES|OPTIONS')
53 if $config{help};
54 pod2usage(-verbose => 2) if $config{man};
56 pod2usage(
57 message => 'working directory must be an absolute path',
58 -verbose => 99,
59 -sections => ''
60 ) if exists $config{workdir} && !file_name_is_absolute($config{workdir});
62 if ($config{'include-archive-tar'}) {
63 $config{remote} = catfile(dirname(realpath(__FILE__)), 'remote-at');
64 if (!-e $config{remote}) { # "make" it
65 print {*STDERR} "### Making remote-at...\n";
66 my $startdir = cwd();
67 chdir dirname realpath __FILE__;
68 system {'make'} qw( make remote-at );
69 chdir $startdir;
70 } ## end if (!-e $config{remote...
71 } ## end if ($config{'include-archive-tar'...
73 # Establish output channel
74 my $out_fh = \*STDOUT;
75 if ($config{output} ne '-') {
76 open my $fh, '>', $config{output} ## no critic
77 or croak "open('$config{output}'): $OS_ERROR";
78 $out_fh = $fh;
80 binmode $out_fh;
82 # Emit script code to be executed remotely. It is guaranteed to end
83 # with __END__, so that all what comes next is data
84 print {$out_fh} get_remote_script();
86 # If a tarfile was given, simply put it and exit
87 if (@{$config{tarfile}}) {
88 croak "UNSUPPORTED";
89 open my $fh, '<', $config{tarfile}
90 or croak "open('$config{tarfile}'): $OS_ERROR";
91 print {$out_fh} <$fh>;
92 close $fh;
93 close $out_fh;
94 exit 0;
95 } ## end if (@{$config{tarfile}...
97 # Where all the data will be kept
98 print_configuration($out_fh, \%config);
100 print_here_stuff($out_fh, \%config, @ARGV);
101 print_root_stuff($out_fh, \%config);
103 close $out_fh;
105 # Set as executable
106 if ($config{output} ne '-') {
107 chmod oct(755), $config{output}
108 or carp "chmod(0755, '$config{output}'): $OS_ERROR";
111 sub header {
112 my %params = @_;
113 my $namesize = length $params{name};
114 return "$namesize $params{size}\n$params{name}";
117 sub print_configuration { # FIXME
118 my ($fh, $config) = @_;
119 my %general_configuration;
120 for my $name (qw( workdir cleanup bundle deploy gzip bzip2 )) {
121 $general_configuration{$name} = $config->{$name}
122 if exists $config->{$name};
124 my $configuration = Dumper \%general_configuration;
125 print {$fh} header(name => 'config.pl', size => length($configuration)),
126 "\n", $configuration, "\n\n";
127 } ## end sub freeze_configuration
129 # Process files and directories. All these will be reported in the
130 # extraction directory, i.e. basename() will be applied to them. For
131 # directories, they will be re-created
132 sub print_here_stuff {
133 my $fh = shift;
134 my $config = shift;
135 my @ARGV = @_;
137 my $ai = Deployable::Tar->new($config);
138 $ai->add(
139 '.' => \@ARGV,
140 map { $_ => [ '.' ] } @{$config->{heredir}}
143 print {$fh} header(name => 'here', size => $ai->size()), "\n";
144 $ai->copy_to($fh);
145 print {$fh} "\n\n";
147 return;
148 } ## end sub get_here_tar
150 sub print_root_stuff {
151 my ($fh, $config) = @_;
153 my $ai = Deployable::Tar->new($config);
154 $ai->add(
155 '.' => $config->{rootdir},
156 map { $_ => [ '.' ] } @{$config->{root}}
159 print {$fh} header(name => 'root', size => $ai->size()), "\n";
160 $ai->copy_to($fh);
161 print {$fh} "\n\n";
163 return;
164 } ## end sub get_root_tar
166 sub get_remote_script {
167 open my $fh, '<', $config{remote}
168 or croak "open('$config{remote}'): $OS_ERROR";
169 my @lines;
170 while (<$fh>) {
171 last if /\A __END__ \s*\z/mxs;
172 push @lines, $_;
174 close $fh;
175 return join '', @lines, "__END__\n";
176 } ## end sub get_remote_script
178 package Deployable::Tar;
180 sub new {
181 my $package = shift;
182 my $self = { ref $_[0] ? %{$_[0]} : @_ };
183 $package = 'Deployable::Tar::Internal';
184 if (! $self->{'no-tar'}) {
185 if ((exists $self->{tar}) || (open my $fh, '-|', 'tar', '--help')) {
186 $package = 'Deployable::Tar::External';
187 $self->{tar} ||= 'tar';
190 bless $self, $package;
191 $self->initialise();
192 return $self;
196 package Deployable::Tar::External;
197 use File::Temp qw( :seekable );
198 use English qw( -no_match_vars );
199 use Cwd ();
200 use Carp;
201 our @ISA = qw( Deployable::Tar );
203 sub initialise {
204 my $self = shift;
205 $self->{_temp} = File::Temp->new();
206 $self->{_filename} = Cwd::abs_path($self->{_temp}->filename());
207 return $self;
210 sub add {
211 my $self = shift;
212 my $tar = $self->{tar};
213 delete $self->{_compressed};
214 while (@_) {
215 my ($directory, $stuff) = splice @_, 0, 2;
216 my @stuff = @$stuff;
217 while (@stuff) {
218 my @chunk = splice @stuff, 0, 50;
219 system {$tar} $tar, 'rvf', $self->{_filename},
220 '-C', $directory, '--', @chunk;
223 return $self;
226 sub _compress {
227 my $self = shift;
228 return if exists $self->{_compressed};
230 $self->{_temp}->sysseek(0, SEEK_SET);
231 if ($self->{bzip2}) {
232 require IO::Compress::Bzip2;
233 $self->{_compressed} = File::Temp->new();
235 # double-quotes needed to force usage of filename
236 # instead of filehandle
237 IO::Compress::Bzip2::bzip2($self->{_temp}, "$self->{_compressed}");
239 elsif ($self->{gzip}) {
240 require IO::Compress::Gzip;
241 $self->{_compressed} = File::Temp->new();
243 # double-quotes needed to force usage of filename
244 # instead of filehandle
245 IO::Compress::Gzip::gzip($self->{_temp}, "$self->{_compressed}");
247 else {
248 $self->{_compressed} = $self->{_temp};
251 return $self;
254 sub size {
255 my ($self) = @_;
256 $self->_compress();
257 return (stat $self->{_compressed})[7];
260 sub copy_to {
261 my ($self, $out_fh) = @_;
262 $self->_compress();
263 my $in_fh = $self->{_compressed};
264 $in_fh->sysseek(0, SEEK_SET);
265 while ('true') {
266 my $nread = $in_fh->sysread(my $buffer, 4096);
267 croak "sysread(): $OS_ERROR" unless defined $nread;
268 last unless $nread;
269 print {$out_fh} $buffer;
271 return $self;
275 package Deployable::Tar::Internal;
276 use Archive::Tar ();
277 use Cwd ();
278 use File::Find::Rule ();
279 our @ISA = qw( Deployable::Tar );
281 sub initialise {
282 my $self = shift;
283 $self->{_tar} = Archive::Tar->new();
284 return $self;
287 sub add {
288 my $self = shift;
289 delete $self->{_string};
290 my $tar = $self->{_tar};
291 my $cwd = Cwd::getcwd();
292 while (@_) {
293 my ($directory, $stuff) = splice @_, 0, 2;
294 chdir $directory;
295 for my $item (@$stuff) {
296 $tar->add_files($_) for File::Find::Rule->in($item);
298 chdir $cwd;
300 return $self;
303 sub size {
304 my ($self) = @_;
305 $self->{_string} = $self->{_tar}->write() unless exists $self->{_string};
306 return length $self->{_string};
309 sub copy_to {
310 my ($self, $out_fh) = @_;
311 $self->{_string} = $self->{_tar}->write() unless exists $self->{_string};
312 print {$out_fh} $self->{_string};
315 __END__
317 =head1 NAME
319 deployable - create a deploy script for some files/scripts
321 =head1 VERSION
323 See version at beginning of script, variable $VERSION, or call
325 shell$ deployable --version
327 =head1 USAGE
329 deployable [--usage] [--help] [--man] [--version]
331 deployable [--bundle|--all-exec|-X] [--bzip2|--bz2|-j] [--cleanup|-c]
332 [--deploy|--exec|d <program>] [--gzip|-gz|-z]
333 [--heredir|-H <dirname>] [--include-archive-tar|-T]
334 [--output|-o <filename>] [--root|-r <dirname>]
335 [--rootdir|-R <dirname>] [--tarfile|--tar|-t <filename>]
336 [--workdir|-w <path>] [ files or directories... ]
338 =head1 EXAMPLES
340 # pack some files and a deploy script together.
341 shell$ deployable script.sh file.txt some/directory -d script.sh
343 # Use a directory's contents as elements for the target root
344 shell$ ls -1 /path/to/target/root
349 # The above will be deployed as /etc, /opt, /usr and /var
350 shell$ deployable -o dep.pl --root /path/to/target/root
352 # Include directory /path/to/etc for inclusion and extraction
353 # directly as /etc
354 shell$ deployable -o dep.pl --rootdir /path/to/etc
356 =head1 DESCRIPTION
358 This is a meta-script to create deploy scripts. The latter ones are
359 suitable to be distributed in order to deploy something.
361 You basically have to provide two things: files to install and programs
362 to be executed. Files can be put directly into the deployed script, or
363 can be included in gzipped tar archives.
365 When called, this script creates a deploy script for you. This script
366 includes all the specified files, and when executed it will extract
367 those files and execute the given programs. In this way, you can ship
368 both files and logic needed to correctly install those files, but this
369 is of course of of scope.
371 All files and archives will be extracted under a configured path
372 (see L<--workdir> below), which we'll call I<workdir> from now on. Under
373 the I<workdir> a temporary directory will be created, and the files
374 will be put in the temporary directory. You can specify if you want to
375 clean up this temporary directory or keep it, at your choice. (You're able
376 to both set a default for this cleanup when invoking deployable, or when
377 invoking the deploy script itself). The temporary directory will be
378 called I<tmpdir> in the following.
380 There are several ways to embed files to be shipped:
382 =over
384 =item *
386 pass the name of an already-prepared tar file via L</--tarfile>. This is
387 by far the most flexible of the options, but it requires more work on
388 your side. If you pass this option, all other input files/directories will
389 be ignored;
391 =item *
393 specify the file name directly on the command line. A file given in this
394 way will always be extracted into the I<tmpdir>, whatever its initial path
395 was;
397 =item *
399 specify the name of a directory on the command line. In this case,
400 C<tar> will be used to archive the directory, with the usual option to
401 turn absolute paths into relative ones; this means that directories will
402 be re-created under I<tmpdir> when extraction is performed;
404 =item *
406 give the name of a directory to be used as a "here directory", using
407 the C<--heredir|-H> option. This is much the same as giving the directory
408 name (see above), but in this case C<tar> will be told to change into the
409 directory first, and archive '.'. This means that the contents of the
410 "here-directory" will be extracted directly into I<tmpdir>.
412 =back
414 =head2 Extended Example
416 Suppose you have a few server which have the same configuration, apart
417 from some specific stuff (e.g. the hostname, the IP addresses, etc.).
418 You'd like to perform changes to all with the minimum work possible...
419 so you know you should script something.
421 For example, suppose you want to update a few files in /etc, setting these
422 files equal for all hosts. You would typically do the following:
424 # In your computer
425 shell$ mkdir -p /tmp/newfiles/etc
426 shell$ cd /tmp/newfiles/etc
427 # Craft the new files
428 shell$ cd ..
429 shell$ tar cvzf newetc.tar.gz etc
431 # Now, for each server:
432 shell$ scp newetc.tar.gz $server:/tmp
433 shell$ ssh $server tar xvzf /tmp/newetc.tar.gz -C /
436 So far, so good. But what if you need to kick in a little more logic?
437 For example, if you update some configuration files, you'll most likey
438 want to restart some services. So you could do the following:
440 shell$ mkdir -p /tmp/newfiles/tmp
441 shell$ cd /tmp/newfiles/tmp
442 # craft a shell script to be executed remotely and set the exec bit
443 # Suppose it's called deploy.sh
444 shell$ cd ..
445 shell$ tar cvzf newetc.tar.gz etc tmp
447 # Now, for each server:
448 shell$ scp newetc.tar.gz $server:/tmp
449 shell$ ssh $server tar xvzf /tmp/newetc.tar.gz -C /
450 shell$ ssh $server /tmp/deploy.sh
452 And what if you want to install files depending on the particular machine?
453 Or you have a bundle of stuff to deploy and a bunch of scripts to execute?
454 You can use deployable. In this case, you can do the following:
456 shell$ mkdir -p /tmp/newfiles/etc
457 shell$ cd /tmp/newfiles/etc
458 # Craft the new files
459 shell$ cd ..
460 # craft a shell script to be executed remotely and set the exec bit
461 # Suppose it's called deploy.sh
462 shell$ deployable -o deploy.pl etc deploy.sh --exec deploy.sh
464 # Now, for each server
465 shell$ scp deploy.pl $server:/tmp
466 shell$ ssh $server /tmp/deploy.pl
468 And you're done. This can be particularly useful if you have another
469 layer of deployment, e.g. if you have to run a script to decide which
470 of a group of archives should be deployed. For example, you could craft
471 a different new "etc" for each server (which is particularly true if
472 network configurations are in the package), and produce a simple script
473 to choose which file to use based on the MAC address of the machine. In
474 this case you could have:
476 =over
478 =item newetc.*.tar.gz
480 a bunch of tar files with the configurations for each different server
482 =item newetc.list
484 a list file with the association between the MAC addresses and the
485 real tar file to deploy from the bunch in the previous bullet
487 =item deploy-the-right-stuff.sh
489 a script to get the real MAC address of the machine, select the right
490 tar file and do the deployment.
492 =back
494 So, you can do the following:
496 shell$ deployable -o deploy.pl newetc.*.tar.gz newetc.list \
497 deploy-the-right-stuff.sh --exec deploy-the-right-stuff.sh
499 # Now, for each server:
500 shell$ scp deploy.pl $server:/tmp
501 shell$ ssh $server /tmp/deploy.pl
503 So, once you have the deploy script on the target machine all you need
504 to do is to execute it. This can come handy when you cannot access the
505 machines from the network, but you have to go there physically: you
506 can prepare all in advance, and just call the deploy script.
509 =head1 OPTIONS
511 Meta-options:
513 =over
515 =item B<--help>
517 print a somewhat more verbose help, showing usage, this description of
518 the options and some examples from the synopsis.
520 =item B<--man>
522 print out the full documentation for the script.
524 =item B<--usage>
526 print a concise usage line and exit.
528 =item B<--version>
530 print the version of the script.
532 =back
534 Real-world options:
536 =over
538 =item B<< --bundle | --all-exec | -X >>
540 Set bundle flag in the produced script. If the bundle flag is set, the
541 I<deploy script> will treat all executables in the main deployment
542 directory as scripts to be executed.
544 By default the flag is not set.
546 =item B<< --bzip2 | --bz2 | -j >>
548 Compress tar archives with bzip2.
550 =item B<< --cleanup | -c >>
552 Set cleanup flag in the produced script. If the cleanup flag is set, the
553 I<deploy script> will clean up after having performed all operations.
555 You can set this flag to C<0> by using C<--no-cleanup>.
557 =item B<< --deploy | --exec | -d <filename> >>
559 Set the name of a program to execute after extraction. You can provide
560 multiple program names, they will be executed in the same order.
562 =item B<< --gzip | --gz | -z >>
564 Compress tar archives with gzip.
566 =item B<< --heredir | -H <path> >>
568 Set the name of a "here directory" (see L<DESCRIPTION>). You can use this
569 option multiple times to provide multiple directories.
571 =item B<< --include-archive-tar | -T >>
573 Embed L<Archive::Tar> (with its dependencies L<Archive::Tar::Constant> and
574 L<Archive::Tar::File>) inside the final script. Use this when you know (or
575 aren't sure) that L<Archive::Tar> will not be available in the target
576 machine.
578 =item B<< --no-tar >>
580 Don't use system C<tar>.
582 =item B<< --output | -o <filename> >>
584 Set the output file name. By default the I<deploy script> will be given
585 out on the standard output; if you provide a filename (different from
586 C<->, of course!) the script will be saved there and the permissions will
587 be set to 0755.
589 =item B<< --root | -r <dirname> >>
591 Include C<dirname> contents for deployment under root directory. The
592 actual production procedure is: hop into C<dirname> and grab a tarball
593 of C<.>. During deployment, hop into C</> and extract the tarball.
595 This is useful if you're already building up the absolute deployment
596 layout under a given directory: just treat that directory as if it were
597 the root of the target system.
599 =item B<< --rootdir | -R <dirname >>
601 Include C<dirname> as a directory that will be extracted under root
602 directory. The actual production procedure is: grab a tarball of
603 C<dirname>. During deployment, hop into C</> and extract the tarball.
605 This is useful if you have a directory (or a group of directories) that
606 you want to deploy directly under the root.
608 =item B<< --tar | -t <program-path> >>
610 Set the system C<tar> program to use.
612 =item B<< --workdir | --deploy-directory | -w <path> >>
614 Set the working directory for the deploy.
616 =back
618 =head1 THE DEPLOY SCRIPT
620 The net result of calling this script is to produce another script,
621 that we call the I<deploy script>. This script is made of two parts: the
622 code, which is fixed, and the configurations/files, which is what is
623 actually produced. The latter part is put after the C<__END__> marker,
624 as usual.
626 Stuff in the configuration part is always hexified in order to prevent
627 strange tricks or errors. Comments will help you devise what's inside the
628 configurations themselves.
630 The I<deploy script> has options itself, even if they are quite minimal.
631 In particular, it supports the same options C<--workdir|-w> and
632 C<--cleanup> described above, allowing the final user to override the
633 configured values. By default, the I<workdir> is set to C</tmp/our-deploy>
634 and the script will clean up after itself.
636 The following options are supported in the I<deploy script>:
638 =over
640 =item B<--usage | --man | --help>
642 print a minimal help and exit
644 =item B<--version>
646 print script version and exit
648 =item B<--bundle | --all-exec | -X>
650 treat all executables in the main deployment directory as scripts
651 to be executed
653 =item B<--cleanup | --no-cleanup>
655 perform / don't perform temporary directory cleanup after work done
657 =item B<< --deploy | --no-deploy >>
659 deploy scripts are executed by default (same as specifying '--deploy')
660 but you can prevent it.
662 =item B<--dryrun | --dry-run>
664 print final options and exit
666 =item B<< --filelist | --list | -l >>
668 print a list of files that are shipped in the deploy script
670 =item B<< --heretar | --here-tar | -H >>
672 print out the tar file that contains all the files that would be
673 extracted in the temporary directory, useful to redirect to file or
674 pipe to the tar program
676 =item B<< --inspect <dirname> >>
678 just extract all the stuff into <dirname> for inspection. Implies
679 C<--no-deploy>, C<--no-tempdir>, ignores C<--bundle> (as a consequence of
680 C<--no-deploy>), disables C<--cleanup> and sets the working directory
681 to C<dirname>
683 =item B<< --no-tar >>
685 don't use system C<tar>
687 =item B<< --rootar | --root-tar | -R >>
689 print out the tar file that contains all the files that would be
690 extracted in the root directory, useful to redirect to file or
691 pipe to the tar program
693 =item B<--show | --show-options | -s>
695 print configured options and exit
697 =item B<< --tar | -t <program-path> >>
699 set the system C<tar> program to use.
701 =item B<< --tempdir | --no-tempdir >>
703 by default a temporary directory is created (same as specifying
704 C<--tempdir>), but you can execute directly in the workdir (see below)
705 without creating it.
707 =item B<--workdir | --work-directory | --deploy-directory | -w>
709 working base directory (a temporary subdirectory will be created
710 there anyway)
712 =back
714 Note the difference between C<--show> and C<--dryrun>: the former will
715 give you the options that are "embedded" in the I<deploy script> without
716 taking into account other options given on the command line, while the
717 latter will give you the final options that would be used if the script
718 were called without C<--dryrun>.
720 =head2 Deploy Script Example Usage
722 In the following, we'll assume that the I<deploy script> is called
723 C<deploy.pl>.
725 To execute the script with the already configured options, you just have
726 to call it:
728 shell$ ./deploy.pl
730 If you just want to see which configurations are in the I<deploy script>:
732 shell$ ./deploy.pl --show
734 To see which files are included, you have two options. One is asking the
735 script:
737 shell$ ./deploy.pl --filelist
739 the other is piping to tar:
741 shell$ ./deploy.pl --tar | tar tvf -
743 Extract contents of the script in a temp directory and simply inspect
744 what's inside:
746 # extract stuff into subdirectory 'inspect' for... inspection
747 shell$ ./deploy.pl --no-tempdir --no-deploy --workdir inspect
749 =head2 Deploy Script Requirements
751 You'll need a working Perl with version at least 5.6.2.
753 If you specify L</--include-archive-tar>, the module L<Archive::Tar> will
754 be included as well. This should ease your life and avoid you to have
755 B<tar> on the target machine. On the other hand, if you already know
756 that B<tar> will be available, you can avoid including C<Archive::Tar>
757 and have the generated script use it (it could be rather slower anyway).
759 =head1 DIAGNOSTICS
761 Each error message should be enough explicit to be understood without the
762 need for furter explainations. Which is another way to say that I'm way
763 too lazy to list all possible ways that this script has to fail.
766 =head1 CONFIGURATION AND ENVIRONMENT
768 deployable requires no configuration files or environment variables.
770 Please note that deployable B<needs> to find its master B<remote> file
771 to produce the final script. This must be put in the same directory where
772 deployable is put. You should be able to B<symlink> deployable where you
773 think it's better, anyway - it will go search for the original file
774 and look for B<remote> inside the same directory. This does not apply to
775 hard links, of course.
778 =head1 DEPENDENCIES
780 All core modules, apart the following:
782 =over
784 =item B<< Archive::Tar >>
786 =item B<< File::Find::Rule >>
788 =back
790 =head1 BUGS AND LIMITATIONS
792 No bugs have been reported.
794 Please report any bugs or feature requests to the AUTHOR below.
796 Be sure to read L<CONFIGURATION AND ENVIRONMENT> for a slight limitation
797 about the availability of the B<remote> script.
799 =head1 AUTHOR
801 Flavio Poletti C<flavio [AT] polettix.it>
804 =head1 LICENSE AND COPYRIGHT
806 Copyright (c) 2008, Flavio Poletti C<flavio [AT] polettix.it>. All rights reserved.
808 This script is free software; you can redistribute it and/or
809 modify it under the same terms as Perl itself. See L<perlartistic>
810 and L<perlgpl>.
812 Questo script è software libero: potete ridistribuirlo e/o
813 modificarlo negli stessi termini di Perl stesso. Vedete anche
814 L<perlartistic> e L<perlgpl>.
817 =head1 DISCLAIMER OF WARRANTY
819 BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
820 FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
821 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
822 PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
823 EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
824 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
825 ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
826 YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
827 NECESSARY SERVICING, REPAIR, OR CORRECTION.
829 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
830 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
831 REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
832 LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
833 OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
834 THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
835 RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
836 FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
837 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
838 SUCH DAMAGES.
840 =head1 NEGAZIONE DELLA GARANZIA
842 Poiché questo software viene dato con una licenza gratuita, non
843 c'è alcuna garanzia associata ad esso, ai fini e per quanto permesso
844 dalle leggi applicabili. A meno di quanto possa essere specificato
845 altrove, il proprietario e detentore del copyright fornisce questo
846 software "così com'è" senza garanzia di alcun tipo, sia essa espressa
847 o implicita, includendo fra l'altro (senza però limitarsi a questo)
848 eventuali garanzie implicite di commerciabilità e adeguatezza per
849 uno scopo particolare. L'intero rischio riguardo alla qualità ed
850 alle prestazioni di questo software rimane a voi. Se il software
851 dovesse dimostrarsi difettoso, vi assumete tutte le responsabilità
852 ed i costi per tutti i necessari servizi, riparazioni o correzioni.
854 In nessun caso, a meno che ciò non sia richiesto dalle leggi vigenti
855 o sia regolato da un accordo scritto, alcuno dei detentori del diritto
856 di copyright, o qualunque altra parte che possa modificare, o redistribuire
857 questo software così come consentito dalla licenza di cui sopra, potrà
858 essere considerato responsabile nei vostri confronti per danni, ivi
859 inclusi danni generali, speciali, incidentali o conseguenziali, derivanti
860 dall'utilizzo o dall'incapacità di utilizzo di questo software. Ciò
861 include, a puro titolo di esempio e senza limitarsi ad essi, la perdita
862 di dati, l'alterazione involontaria o indesiderata di dati, le perdite
863 sostenute da voi o da terze parti o un fallimento del software ad
864 operare con un qualsivoglia altro software. Tale negazione di garanzia
865 rimane in essere anche se i dententori del copyright, o qualsiasi altra
866 parte, è stata avvisata della possibilità di tali danneggiamenti.
868 Se decidete di utilizzare questo software, lo fate a vostro rischio
869 e pericolo. Se pensate che i termini di questa negazione di garanzia
870 non si confacciano alle vostre esigenze, o al vostro modo di
871 considerare un software, o ancora al modo in cui avete sempre trattato
872 software di terze parti, non usatelo. Se lo usate, accettate espressamente
873 questa negazione di garanzia e la piena responsabilità per qualsiasi
874 tipo di danno, di qualsiasi natura, possa derivarne.
876 =cut