source: MondoRescue/devel/mr/lib/MondoRescue/LVM.pm@ 2558

Last change on this file since 2558 was 2558, checked in by Bruno Cornec, 14 years ago

r3620@localhost: bruno | 2010-01-28 00:04:14 +0100
Large update of devel branch as sync point around mranalyze-lvm (in progress)

File size: 18.8 KB
Line 
1#!/usr/bin/perl -w
2#
3# Mindi subroutines related to LVM brought by the MondoRescue project
4#
5# $Id$
6#
7# Copyright B. Cornec 2008-2010
8# Provided under the GPL v2
9
10package MondoRescue::LVM;
11
12use strict 'vars';
13use Data::Dumper;
14use English;
15use lib qw (lib);
16use ProjectBuilder::Base;
17use ProjectBuilder::Conf;
18use MondoRescue::Base;
19
20# Inherit from the "Exporter" module which handles exporting functions.
21
22use Exporter;
23
24# Export, by default, all the functions into the namespace of
25# any code which uses this module.
26
27our @ISA = qw(Exporter);
28our @EXPORT = qw(mr_lvm_check mr_lvm_get_conf mr_lvm_read_conf mr_lvm_write_conf mr_lvm_edit_conf mr_lvm_apply_from_conf);
29
30=pod
31
32=head1 NAME
33
34MondoRescue::LVM, part of the mondorescue.org
35
36=head1 DESCRIPTION
37
38This modules provides low level functions for LVM support in the Mondorescue project
39
40=head1 USAGE
41
42=over 4
43
44=item B<mr_lvm_check>
45
46This function checks the usage of LVM and gets the version used
47It returns 2 parameters, the LVM version, and the lvm command to use if needed
48The LVM version could be undef, 0 (no LVM), 1 or 2 at the moment, or further not yet supported version
49It potentially takes one parameter, the LVM version, already known, in which case it easily deduced the LVM command.
50If LVM version is undefined then no LVM Handling should be done.
51It has to run on on a system where LVM is activated to return useful results so typically on the system to backup
52
53=cut
54
55sub mr_lvm_check {
56
57my $lvmver = shift;
58
59# Get params from the conf file
60my ($lvmds_t,$lvmproc_t,$lvmcmd_t,$lvmpath_t) = pb_conf_get("mr_lvmdiskscan","mr_lvmprocfile","mr_lvmcmd","mr_lvmpath");
61my $lvmds = $lvmds_t->{$ENV{PBPROJ}};
62my $lvmproc = $lvmproc_t->{$ENV{PBPROJ}};
63my $lvmcmd = $lvmcmd_t->{$ENV{PBPROJ}};
64my $lvmpath = $lvmpath_t->{$ENV{PBPROJ}};
65
66# That file is not mandatory anymore
67if (not defined $lvmver) {
68 if (! -x $lvmproc) {
69 pb_log(1,"$lvmproc doesn't exist\n");
70 } else {
71 # Check LVM volumes presence
72 pb_log(2,"Checking with $lvmproc\n");
73 open(LVM,$lvmproc) || mr_exit(-1,"Unable to open $lvmproc");
74 while (<LVM>) {
75 if (/0 VGs 0 PVs 0 LVs/) {
76 pb_log(1,"No LVM volumes found in $lvmproc\n");
77 return(0,undef);
78 }
79 }
80 close(LVM);
81 }
82}
83
84# Check LVM version
85if (not defined $lvmver) {
86 pb_log(2,"LVM version value is not known\n");
87 if (-x $lvmds) {
88 pb_log(2,"Checking with $lvmds\n");
89 open(LVM,"$lvmds --help 2>&1 |") || mr_exit(-1,"Unable to execute $lvmds");
90 while (<LVM>) {
91 if (/Logical Volume Manager/ || /LVM version:/) {
92 $lvmver = $_;
93 chomp($lvmver);
94 $lvmver =~ s/:([0-9])\..*/$1/;
95 }
96 }
97 close(LVM);
98 pb_log(2,"Found a LVM version of $lvmver with $lvmds --help\n") if (defined $lvmver);
99 }
100}
101
102if (not defined $lvmver) {
103 pb_log(2,"LVM version value is still not known\n");
104 if (-x $lvmcmd) {
105 pb_log(2,"Checking with $lvmcmd\n");
106 open(LVM,"$lvmcmd version |") || mr_exit(-1,"Unable to execute $lvmcmd");
107 while (<LVM>) {
108 if (/LVM version/) {
109 $lvmver = $_;
110 chomp($lvmver);
111 $lvmver =~ s/:([0-9])\..*/$1/;
112 $lvmver =~ s/[\s]*LVM version[:]*[\s]+([0-9])\..*/$1/;
113 }
114 }
115 close(LVM);
116 pb_log(2,"Found a LVM version of $lvmver with $lvmcmd version\n") if (defined $lvmver);
117 }
118}
119
120if (not defined $lvmver) {
121 # Still not found
122 mr_log(0,"Unable to determine LVM version.\nIf you think this is wrong, please report to the dev team with the result of the commands:\n$lvmds --help and $lvmcmd version\n");
123} elsif ($lvmver == 1) {
124 $lvmcmd = "$lvmpath";
125} elsif ($lvmver == 2) {
126 $lvmcmd .= " ";
127} else {
128 pb_log(0,"Unknown LVM version $lvmver\n");
129}
130# Here $lvmcmd contains a full path name
131pb_log(1,"Found LVM version $lvmver\n");
132return ($lvmver,$lvmcmd);
133
134}
135
136=over 4
137
138=item B<mr_lvm_get_conf>
139
140This function returns 1 parameters, the LVM structure or undef if no LVM
141That LVM structure contains all the information related to the current LVM configuration
142
143=cut
144
145sub mr_lvm_get_conf {
146
147my $lvm = undef;
148
149my ($lvmver,$lvmcmd) = mr_lvm_check();
150return(undef) if ((not defined $lvmver) || ($lvmver == 0));
151
152# Analyze the existing physical volumes
153open(LVM,$lvmcmd."pvs --noheadings --nosuffix --units m --separator : -o pv_name,vg_name,pv_all,pv_fmt,pv_uuid,dev_size,pv_mda_free,pv_mda_size |") || mr_exit(-1,"Unable to execute ".$lvmcmd."pvs");
154while (<LVM>) {
155 s/^[\s]*//;
156
157 my ($pv_name,$vg_name,$pe_start,$pv_size,$pv_free,$pv_used,$pv_attr,$pv_pe_count,$pv_pe_alloc_count,$pv_tags,$pv_mda_count,$pv_uuid,$dev_size,$pv_mda_free,$pv_mda_size) = split(/:/);
158
159=pod
160
161The LVM hash is indexed by VGs, provided by the vg_name attribute of the pvs command
162vg_name - Name of the volume group linked to this PV
163
164=cut
165
166 $lvm->{$vg_name}->{'pvnum'}++;
167
168=pod
169
170The structure contains an array of PVs called pvs and starting at 1, containing the name of the PV as provided by the pv_name attribute of the pvs command
171pv_name - Name of the physical volume PV
172
173=cut
174
175 # Array of PVs for that VG
176 $lvm->{$vg_name}->{'pvs'}->[$lvm->{$vg_name}->{'pvnum'}] = $pv_name;
177
178=pod
179
180All the PV fields from the pvs command are gathered under their PV name (substructure)
181The following names are used:
182
183From pvs -o help
184pe_start - Offset to the start of data on the underlying device.
185pv_size - Size of PV in current units.
186pv_free - Total amount of unallocated space in current units.
187pv_used - Total amount of allocated space in current units.
188pv_attr - Various attributes - see man page.
189pv_pe_count - Total number of Physical Extents.
190pv_pe_alloc_count - Total number of allocated Physical Extents.
191pv_tags - Tags, if any.
192pv_mda_count - Number of metadata areas on this device.
193pv_fmt - Type of metadata.
194pv_uuid - Unique identifier.
195dev_size - Size of underlying device in current units.
196pv_mda_free - Free metadata area space on this device in current units.
197pv_mda_size - Size of smallest metadata area on this device in current units.
198
199=cut
200
201 $lvm->{$vg_name}->{$pv_name}->{'pe_start'} = $pe_start;
202 $lvm->{$vg_name}->{$pv_name}->{'pv_size'} = $pv_size;
203 $lvm->{$vg_name}->{$pv_name}->{'pv_free'} = $pv_free;
204 $lvm->{$vg_name}->{$pv_name}->{'pv_used'} = $pv_used;
205 $lvm->{$vg_name}->{$pv_name}->{'pv_attr'} = $pv_attr;
206 $lvm->{$vg_name}->{$pv_name}->{'pv_pe_count'} = $pv_pe_count;
207 $lvm->{$vg_name}->{$pv_name}->{'pv_pe_alloc_count'} = $pv_pe_alloc_count;
208 $lvm->{$vg_name}->{$pv_name}->{'pv_tags'} = $pv_tags;
209 $lvm->{$vg_name}->{$pv_name}->{'pv_mda_count'} = $pv_mda_count;
210 $lvm->{$vg_name}->{$pv_name}->{'pv_uuid'} = $pv_uuid;
211 $lvm->{$vg_name}->{$pv_name}->{'dev_size'} = $dev_size;
212 $lvm->{$vg_name}->{$pv_name}->{'pv_mda_free'} = $pv_mda_free;
213 $lvm->{$vg_name}->{$pv_name}->{'pv_mda_size'} = $pv_mda_size;
214}
215close(LVM);
216
217# Analyze the existing volume groups
218#open(LVM,$lvmcmd."vgdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgdisplay -c");
219open(LVM,$lvmcmd."vgs --noheadings --nosuffix --units m --separator : | -o vg_all") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgs");
220while (<LVM>) {
221
222=pod
223
224All the VG fields from the vgs command are gathered under the VG name
225The following names are used:
226
227From vgs -o help
228vg_fmt - Type of metadata.
229vg_uuid - Unique identifier.
230vg_attr - Various attributes - see man page.
231vg_size - Total size of VG in current units.
232vg_free - Total amount of free space in current units.
233vg_sysid - System ID indicating when and where it was created.
234vg_extent_size - Size of Physical Extents in current units.
235vg_extent_count - Total number of Physical Extents.
236vg_free_count - Total number of unallocated Physical Extents.
237max_lv - Maximum number of LVs allowed in VG or 0 if unlimited.
238max_pv - Maximum number of PVs allowed in VG or 0 if unlimited.
239pv_count - Number of PVs.
240lv_count - Number of LVs.
241snap_count - Number of snapshots.
242vg_seqno - Revision number of internal metadata. Incremented whenever it changes.
243vg_tags - Tags, if any.
244vg_mda_count - Number of metadata areas in use by this VG.
245vg_mda_free - Free metadata area space for this VG in current units.
246vg_mda_size - Size of smallest metadata area for this VG in current units.
247
248=cut
249 s/^[\s]*//;
250 my ($vg_fmt,$vg_uuid,$vg_name,$vg_attr,$vg_size,$vg_free,$vg_sysid,$vg_extend_size,$vg_extend_count,$vg_free_count,$max_lv,$max_pv,$pv_count,$lv_count,$snap_count,$vg_seqno,$vg_tags,$vg_mda_count,$vg_mda_free,$vg_mda_size) = split(/:/);
251 $lvm->{$vg_name}->{'vg_fmt'} = $vg_fmt;
252 $lvm->{$vg_name}->{'vg_uuid'} = $vg_uuid;
253 $lvm->{$vg_name}->{'vg_attr'} = $vg_attr;
254 $lvm->{$vg_name}->{'vg_size'} = $vg_size;
255 $lvm->{$vg_name}->{'vg_free'} = $vg_free;
256 $lvm->{$vg_name}->{'vg_sysid'} = $vg_sysid;
257 $lvm->{$vg_name}->{'vg_extend_size'} = $vg_extend_size;
258 $lvm->{$vg_name}->{'vg_extend_count'} = $vg_extend_count;
259 $lvm->{$vg_name}->{'vg_free_count'} = $vg_free_count;
260 $lvm->{$vg_name}->{'max_lv'} = $max_lv;
261 $lvm->{$vg_name}->{'max_pv'} = $max_pv;
262 $lvm->{$vg_name}->{'pv_count'} = $pv_count;
263 $lvm->{$vg_name}->{'lv_count'} = $lv_count;
264 $lvm->{$vg_name}->{'snap_count'} = $snap_count;
265 $lvm->{$vg_name}->{'vg_seqno'} = $vg_seqno;
266 $lvm->{$vg_name}->{'vg_tags'} = $vg_tags;
267 $lvm->{$vg_name}->{'vg_mda_count'} = $vg_mda_count;
268 $lvm->{$vg_name}->{'vg_mda_free'} = $vg_mda_free;
269 $lvm->{$vg_name}->{'vg_mda_size'} = $vg_mda_size;
270}
271close(LVM);
272
273# Analyze the existing logical volumes
274#open(LVM,$lvmcmd."lvdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvdisplay -c");
275open(LVM,$lvmcmd."lvs --noheadings --nosuffix --units m --separator : -o vg_name,lv_all|") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvs");
276while (<LVM>) {
277 s/^[\s]*//;
278
279=pod
280
281The structure contains an array of LVs called lvs and starting at 1, containing the name of the PV as provided by the pv_name attribute of the pvs command
282pv_name - Name of the physical volume PV
283
284=cut
285
286 # Array of PVs for that VG
287 $lvm->{$vg_name}->{'pvs'}->[$lvm->{$vg_name}->{'pvnum'}] = $pv_name;
288
289=pod
290
291All the PV fields from the pvs command are gathered under their PV name (substructure)
292The following names are used:
293
294
295 # From lvs -o help
296
297 #vg_name - Name of the related volume group
298 #lv_uuid - Unique identifier.
299 #lv_name - Name. LVs created for internal use are enclosed in brackets.
300 #lv_attr - Various attributes - see man page.
301 #lv_major - Persistent major number or -1 if not persistent.
302 #lv_minor - Persistent minor number or -1 if not persistent.
303 #lv_read_ahead - Read ahead setting in current units.
304 #lv_kernel_major - Currently assigned major number or -1 if LV is not active.
305 #lv_kernel_minor - Currently assigned minor number or -1 if LV is not active.
306 #lv_kernel_read_ahead - Currently-in-use read ahead setting in current units.
307 #lv_size - Size of LV in current units.
308 #seg_count - Number of segments in LV.
309 #origin - For snapshots, the origin device of this LV.
310 #origin_size - For snapshots, the size of the origin device of this LV.
311 #snap_percent - For snapshots, the percentage full if LV is active.
312 #copy_percent - For mirrors and pvmove, current percentage in-sync.
313 #move_pv - For pvmove, Source PV of temporary LV created by pvmove.
314 #convert_lv - For lvconvert, Name of temporary LV created by lvconvert.
315 #lv_tags - Tags, if any.
316 #mirror_log - For mirrors, the LV holding the synchronisation log.
317 #modules - Kernel device-mapper modules required for this LV.
318
319=cut
320
321 my ($vg_name,$lv_uuid,$lv_name,$lv_attr,$lv_major,$lv_minor,$lv_read_ahead,$lv_kernel_major,$lv_kernel_minor,$lv_kernel_read_ahead,$lv_size,$seg_count,$origin,$origin_size,$snap_percent,$copy_percent,$move_pv,$convert_lv,$lv_tags,$mirror_log,$modules) = split(/:/);
322 # The LVM hash is indexed by VGs
323 $lvm->{$vg_name}->{'lvnum'}++;
324 # That array will start at 1 then
325 # Array of LVs for that VG
326 $lvm->{$vg_name}->{'lvs'}->[$lvm->{$vg_name}->{'lvnum'}] = $lv_name;
327 # All LV fields gathered under the LV name
328 $lvm->{$vg_name}->{$lv_name}->{'lv_uuid'} = $lv_uuid;
329 $lvm->{$vg_name}->{$lv_name}->{'lv_attr'} = $lv_attr;
330 $lvm->{$vg_name}->{$lv_name}->{'lv_major'} = $lv_major;
331 $lvm->{$vg_name}->{$lv_name}->{'lv_minor'} = $lv_minor;
332 $lvm->{$vg_name}->{$lv_name}->{'lv_read_ahead'} = $lv_read_ahead;
333 $lvm->{$vg_name}->{$lv_name}->{'lv_kernel_major'} = $lv_kernel_major;
334 $lvm->{$vg_name}->{$lv_name}->{'lv_kernel_minor'} = $lv_kernel_minor;
335 $lvm->{$vg_name}->{$lv_name}->{'lv_kernel_read_ahead'} = $lv_kernel_read_ahead;
336 $lvm->{$vg_name}->{$lv_name}->{'lv_size'} = $lv_size;
337 $lvm->{$vg_name}->{$lv_name}->{'origin'} = $origin;
338 $lvm->{$vg_name}->{$lv_name}->{'origin_size'} = $origin_size;
339 $lvm->{$vg_name}->{$lv_name}->{'snap_percent'} = $snap_percent;
340 $lvm->{$vg_name}->{$lv_name}->{'copy_percent'} = $copy_percent;
341 $lvm->{$vg_name}->{$lv_name}->{'move_pv'} = $move_pv;
342 $lvm->{$vg_name}->{$lv_name}->{'convert_lv'} = $convert_lv;
343 $lvm->{$vg_name}->{$lv_name}->{'lv_tags'} = $lv_tags;
344 $lvm->{$vg_name}->{$lv_name}->{'mirror_log'} = $mirror_log;
345 $lvm->{$vg_name}->{$lv_name}->{'modules'} = $modules;
346}
347close(LVM);
348return($lvm);
349}
350
351=item B<mr_lvm_analyze>
352
353This function outputs in a file descriptor the LVM analysis done
354It returns 1 parameters, the LVM version or 0 if no LVM
355
356=cut
357
358sub mr_lvm_analyze {
359
360my $OUTPUT = shift;
361
362my ($lvmver,$lvmcmd) = mr_lvm_check();
363my $lvm = mr_lvm_get_conf();
364return(undef) if ($lvmver == 0);
365
366print $OUTPUT "LVM:$lvmver\n";
367
368# Analyze the existing physical volumes
369#open(LVM,$lvmcmd."pvdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."pvdisplay -c");
370open(LVM,$lvmcmd."pvs --noheadings --nosuffix --units m --separator : |") || mr_exit(-1,"Unable to execute ".$lvmcmd."pvs");
371while (<LVM>) {
372 s/^[\s]*//;
373 my ($pv,$vg,$foo,$foo2,$size,$foo3) = split(/:/);
374 $lvm->{$vg}->{'pvnum'}++;
375 # that array will start at 1 then
376 $lvm->{$vg}->{'pv'}->[$lvm->{$vg}->{'pvnum'}] = $pv;
377 $lvm->{$vg}->{$pv}->{'size'} = $size;
378 print $OUTPUT "PV:$_";
379}
380close(LVM);
381
382# Analyze the existing volume groups
383#open(LVM,$lvmcmd."vgdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgdisplay -c");
384open(LVM,$lvmcmd."vgs --noheadings --nosuffix --units m --separator : |") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgs");
385while (<LVM>) {
386 s/^[\s]*//;
387 print $OUTPUT "VG:$_";
388}
389close(LVM);
390
391# Analyze the existing logical volumes
392#open(LVM,$lvmcmd."lvdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvdisplay -c");
393open(LVM,$lvmcmd."lvs --noheadings --nosuffix --units m --separator : |") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvs");
394while (<LVM>) {
395 s/^[\s]*//;
396 print $OUTPUT "LV:$_";
397}
398close(LVM);
399return($lvm);
400}
401
402
403=over 4
404
405=item B<mr_lvm_prepare>
406
407This function outputs in a file descriptor the LVM setup needed to restore LVM conf
408It returns 1 parameters, the LVM version or 0 if no LVM
409
410=cut
411
412sub mr_lvm_prepare {
413
414my $INPUT = shift;
415my $OUTPUT = shift;
416my $mrmult = shift;
417my $lvmcmd;
418my $lvmver;
419
420# Generate the startup scrit needed to restore LVM conf
421# from what is given on input
422# Multiply by the multiplier given in input or 1 of none
423
424my $firsttime = 0;
425while (<$INPUT>) {
426 if (/^LVM:/) {
427 my $tag;
428 my $foo;
429 ($tag,$lvmver) = split(/:/);
430 ($foo,$lvmcmd) = mr_lvm_check($lvmver);
431
432 print $OUTPUT "# Desactivate Volume Groups\n";
433 print $OUTPUT $lvmcmd."vgchange -an\n";
434 print $OUTPUT "\n";
435
436 } elsif (/^PV:/) {
437 # This is for pvdisplay -c
438 #my ($tag,$pvname,$vgname,$pvsize,$ipvn,$pvstat,$pvna,$lvnum,$pesize,$petot,$pefree,$pelloc) = split(/:/);
439 my ($tag,$pvname,$vgname,$lvmv,$more,$pesize,$pefree) = split(/:/);
440 print $OUTPUT "# Creating Physical Volumes $pvname\n";
441 print $OUTPUT $lvmcmd."pvcreate -ff -y";
442 print $OUTPUT " -s ".$pesize*$mrmult if (defined $pesize);
443 print $OUTPUT " $pvname\n";
444 print $OUTPUT "\n";
445 } elsif (/^VG:/) {
446 # This if for vgdisplay -c
447 #my ($tag,$vgname,$vgaccess,$vgstat,$vgnum,$lvmaxnum,$lvnum,$ocalvinvg,$lvmaxsize,$pvmaxnum,$cnumpv,$anumpv,$vgsize,$pesize,$penum,$pealloc,$pefree,$uuid) = split(/:/);
448 my ($tag,$vgname,$pvnum,$lvnum,$attr,$vgsize,$vgfree) = split(/:/);
449 if ($lvmver < 2) {
450 print $OUTPUT "# Removing device first as LVM v1 doesn't do it\n";
451 print $OUTPUT "rm -Rf /dev/$vgname\n";
452 }
453 #$lvmaxnum = 255 if (($lvmaxnum > 256) or (not defined $lvmaxnum));
454 #$pvmaxnum = 255 if (($pvmaxnum > 256) or (not defined $pvmaxnum));
455 print $OUTPUT "# Create Volume Group $vgname\n";
456 # Pb sur pesize unite ?
457 print $OUTPUT $lvmcmd."vgcreate $vgname ";
458 #print $OUTPUT "-p $pvmaxnum -l $lvmaxnum";
459 #print $OUTPUT " -s ".$pesize."\n" if (defined $pesize);
460 print $OUTPUT "\n";
461
462 } elsif (/^LV:/) {
463 if ($firsttime == 0) {
464 print $OUTPUT "\n";
465 print $OUTPUT "# Activate All Volume Groups\n";
466 print $OUTPUT $lvmcmd."vgchange -ay\n";
467 print $OUTPUT "\n";
468 $firsttime = 1;
469 }
470 my ($tag,$lvname,$vgname,$lvaccess,$lvstat,$lvnum,$oclv,$lvsize,$leinlv,$lealloc,$allocpol,$readahead,$major,$minor) = split(/:/);
471 print $OUTPUT "# Create Logical Volume $lvname\n";
472 print $OUTPUT $lvmcmd."lvcreate -n $lvname -L ".$lvsize*$mrmult;
473 print $OUTPUT " -r $readahead" if (defined $readahead);
474 print $OUTPUT " $vgname\n";
475 #[ "$stripes" ] && output="$output -i $stripes"
476 #[ "$stripesize" ] && output="$output -I $stripesize"
477 }
478}
479
480print $OUTPUT "\n";
481print $OUTPUT "# Scanning again Volume Groups\n";
482print $OUTPUT $lvmcmd."vgscan\n";
483print $OUTPUT "\n";
484
485}
486
487=back
488
489=head1 WEB SITES
490
491The main Web site of the project is available at L<http://www.mondorescue.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.mondorescue.org/>.
492
493=head1 USER MAILING LIST
494
495The mailing list of the project is available at L<mailto:mondo@lists.sf.net>
496
497=head1 AUTHORS
498
499The Mondorescue.org team L<http://www.mondorescue.org/> lead by Bruno Cornec L<mailto:bruno@mondorescue.org>.
500
501=head1 COPYRIGHT
502
503This module is distributed under the GPL v2.0 license
504described in the file C<COPYING> included with the distribution.
505
506
507=cut
508
5091;
510
Note: See TracBrowser for help on using the repository browser.