1#
2#
3#
4# Copyright (c) 1998 Alan R. Barclay. All rights reserved. This program
5# is free software; you can redistribute it and/or modify it under
6# the same terms as Perl itself.
7
8package Filesys::DiskFree;
9
10use Carp;
11use strict;
12
13#qw();
14
15use vars qw($VERSION $Format %Df);
16
17$VERSION = 0.06;
18
19#
20# The format table
21#
22# Note, the format names are not gauranteed. If I find that there
23# is a reason to rename one, then they be renamed.
24#
25
26%Df = (
27    'linux' => {
28	'blocks' => "df -P",
29	'inodes' => "df -Pi",
30	'format' => "linuxish",
31    },
32    'solaris' =>  {
33	'blocks' => "df -k",
34	'inodes' => "df -k -o i -F ufs",
35	'format' => "svish",
36    },
37    'bsdos' => {
38	'blocks' => "df -i",
39	'inodes' => "df -i",
40	'format' => 'bsdish',
41    },
42    'freebsd' => {
43	'blocks' => "df -Pi",
44	'inodes' => "df -Pi",
45	'format' => 'bsdish',
46    },
47    'irix' => {
48    	'blocks' => "df",
49	'inodes' => "df -i",
50	'format' => "irixish",
51    },
52    'hpux' => {
53	'blocks' => "bdf -l -i",
54	'inodes' => "bdf -l -i",
55	'format' => 'hpuxish',
56    },
57    'dec_osf' => {
58	'blocks' => "df",
59	'inodes' => "df -i",
60	'format' => 'svish',
61    },
62);
63
64use strict;
65
66BEGIN    {
67    $Format = $^O;
68}
69
70sub new {
71    my $proto = shift;
72    my $class = ref($proto) || $proto;
73    my $self  = {
74    	FORMAT       => $Format,
75    	DEVICES	     => undef,
76    	MOUNTS	     => undef,
77    	MODE	     => 'blocks'
78    };
79
80    bless ($self, $class);
81    return $self;
82}
83
84sub set(){
85    my $self=shift;
86    my @return;
87
88    return undef if(defined $self->{'DEVICES'});
89
90    if(@_){
91	if($_[0] =~ m/format/i){
92	    push(@return,$self->{'FORMAT'});
93	    $self->{'FORMAT'}=$_[1] if(defined $_[1]);
94	}
95
96	if($_[0] =~ m/mode/i){
97	    push(@return,$self->{'MODE'});
98	    $self->{'MODE'}='blocks' if($_[1] =~ m/block/i and defined $_[1]);
99	    $self->{'MODE'}='inodes' if($_[1] =~ m/inode/i and defined $_[1]);
100	}
101    }
102    return @return;
103}
104
105sub command () {
106	my $self=shift;
107	return $Df{"\L".$self->{'FORMAT'}."\E"}{$self->{'MODE'}};
108}
109sub df(){
110    my $self=shift;
111    my $cmd="df";
112
113    $cmd=$self->command() or
114    	croak "No df command known for format ".$self->{'FORMAT'};
115    open(HANDLE,"$cmd|") or croak("Cannot fork $!");
116    return $self->load(\*HANDLE);
117    close(HANDLE) or croak("Cannot df $!");
118}
119
120sub load()  {
121    my $self=shift;
122    my $handle=shift;
123
124    if(ref $handle eq "GLOB"){
125    	while(<$handle>){
126    		$self->readline($_);
127    	}
128    } else {
129    	map { $self->readline($_) } split(/$\//,$handle);
130    }
131    return 'true';
132}
133
134sub readline() {
135    my $self=shift;
136    my $line=shift;
137    my ($device,$btotal,$bused,$bavail,$iused,$iavail,$mount,
138	$total,$used,$avail);
139
140    chomp($line);
141
142    $_=$Df{"\L".$self->{'FORMAT'}."\E"}{'format'};
143
144    if(/linuxish/i){
145    	return undef if($line =~ /^Filesystem.*Mounted on/i);
146    	($device,$total,$used,$avail,undef,$mount)=split(' ',$line);
147	if($self->{'MODE'} eq 'blocks'){
148		$total *= 1024;
149		$used *= 1024;
150		$avail *= 1024;
151	}
152    } elsif(/svish/i){
153    	return undef if($line =~ /^Filesystem.*Mounted on/i);
154	if($self->{'MODE'} eq 'blocks'){
155		($device,$total,$used,$avail,undef,$mount)=split(' ',$line);
156		$total *= 1024;
157		$used *= 1024;
158		$avail *= 1024;
159	} else {
160		($device,$used,$avail,undef,$mount)=split(' ',$line);
161		$total=$used+$avail;
162	}
163    } elsif(/bsdish/){
164    	return undef if($line =~ /^Filesystem.*Mounted on/i);
165    	($device,$btotal,$bused,$bavail,undef,$iused,$iavail,undef,$mount)=
166		split(' ',$line);
167	if($self->{'MODE'} eq 'blocks'){
168		$total=$btotal*512;
169		$used=$bused*512;
170		$avail=$bavail*512;
171	} elsif($self->{'MODE'} eq 'inodes'){
172		$total=undef;
173		$used=$iused*512;
174		$avail=$iavail*512;
175	}
176    } elsif(/irixish/){
177    	return undef if($line =~ /^Filesystem.*Mounted on/i);
178	if($self->{'MODE'} eq 'blocks'){
179		($device,undef,$btotal,$bused,$bavail,undef,$mount)=split(' ',$line);
180		$total=$btotal*512;
181		$used=$bused*512;
182		$avail=$bavail*512;
183	} elsif($self->{'MODE'} eq 'inodes'){
184		($device,undef,$btotal,$bused,$bavail,undef,$iused,$iavail,undef,$mount)=
185			split(' ',$line);
186		return undef if $iused =~ /[A-Za-z]+/ or $iused == 0;
187		$total = ($iused + $iavail) * 512;
188		$used=$iused*512;
189		$avail=$iavail*512;
190	}
191    } elsif(/hpuxish/){
192    	return undef if($line =~ /^Filesystem.*Mounted on/i);
193    	($device,$btotal,$bused,$bavail,undef,$iused,$iavail,undef,$mount)=
194		split(' ',$line);
195	if($self->{'MODE'} eq 'blocks'){
196		$total=$btotal*1024;
197		$used=$bused*1024;
198		$avail=$bavail*1024;
199	} elsif($self->{'MODE'} eq 'inodes'){
200		$total=($iused + $iavail);
201		$used=$iused;
202		$avail=$iavail;
203	}
204    } else {
205    	croak "Unknown encoding ".$Df{"\L".$self->{'FORMAT'}."\E"}{'format'}.
206    	      " for format ".$self->{'FORMAT'};
207    }
208    $self->{'MOUNTS'}{$mount}=$device;
209    $self->{'DEVICES'}{$device}={};
210    $self->{'DEVICES'}{$device}{'device'}=$device;
211    $self->{'DEVICES'}{$device}{'total'} =$total;
212    $self->{'DEVICES'}{$device}{'used'}  =$used;
213    $self->{'DEVICES'}{$device}{'avail'} =$avail;
214    $self->{'DEVICES'}{$device}{'mount'} =$mount;
215}
216
217sub device() { return extract(@_,'device'); }
218sub total()  { return extract(@_,'total');  }
219sub used()   { return extract(@_,'used');   }
220sub avail()  { return extract(@_,'avail');  }
221sub mount()  { return extract(@_,'mount');  }
222
223sub extract () {
224    my $self=shift;
225    my $device;
226    if(@_) {
227	my $thingy=shift;
228	if(defined($self->{'DEVICES'}{$thingy})){
229	    $device=$thingy;
230	} else {
231	    return undef unless(defined($self->{'MOUNTS'}));
232	    while(not defined($self->{'MOUNTS'}{$thingy})){
233		return undef if($thingy eq '/');
234		$thingy =~ s!/[^/]*?$!!  unless($thingy =~ s!/$!!);
235		$thingy = "/" unless($thingy =~ "/");
236	    }
237	    $device=$self->{'MOUNTS'}{$thingy}
238	}
239    	return $self->{'DEVICES'}{$device}{$_[0]};
240    }
241    return undef;
242}
243
244sub disks () {
245	my $self=shift;
246	return undef unless(defined($self->{'MOUNTS'}));
247	return keys %{$self->{'MOUNTS'}};
248}
249
2501;
251__END__
252
253
254=head1 NAME
255
256Filesys::DiskFree -- perform the Unix command 'df' in a portable fashion
257
258=head1 SYNOPSIS
259
260    use Filesys::DiskFree;
261
262    $handle = new Filesys::DiskFree;
263    $handle->df();
264    print "The root device is ".$handle->device("/")."\n";
265    print "It has ".$handle->avail("/")." bytes available\n";
266    print "It has ".$handle->total("/")." bytes total\n";
267    print "It has ".$handle->used("/")." bytes used\n";
268
269=head1 DESCRIPTION
270
271Filesys::DiskFree does about what the unix command df(1) does, listing
272the mounted disks, and the amount of free space used & available.
273
274=head2 Functions
275
276=over 4
277
278=item Filesys::DiskFree->set('option' => 'value')
279
280Sets various options within the module.
281
282The most common option to change is the mode, which can be either
283blocks or inodes. By default, blocks is used.
284
285If reading a file from a 'foreign' OS using the load() function,
286format may be used, which takes the name of an OS as set in the $^O
287variable.
288
289Returns the previous values of the options.
290
291=item Filesys::DiskFree->df()
292
293Perfoms a 'df' command, and stores the values for later use.
294
295=item Filesys::DiskFree->command()
296
297Returns the appropriate command to do a 'df' command, for the current
298format.  This is used when you wish to call a df on a remote system.
299Use the df() method for local df's.
300
301Returns undef if there isn't an appropriate command.
302
303=item Filesys::DiskFree->load($line)
304
305Reads in the output of a 'df', $line can be either a scalar or a filehandle.
306If $line is a filehandle, then the filehandle is read until EOF.
307
308Returns undef on failure
309
310=item Filesys::DiskFree->disks()
311
312Returns all the disks known about
313
314=item Filesys::DiskFree->device($id)
315
316Returns the device for $id, which is a scalar containing the device name of
317a disk or a filename, in which case the disk that filename in stored upon
318is used. If a filename doesn't begin with '/', then it is treated as
319if is '/'.
320
321=item Filesys::DiskFree->mount($id)
322
323Returns the mount point for $id, which is a scalar containing the device
324name of a disk or a filename, in which case the disk that filename in
325stored upon is used.
326
327=item Filesys::DiskFree->avail($id)
328
329Returns the amount of available space in bytes for $id, which is a scalar
330containing the device name of a disk or a filename, in which case the
331disk that filename in stored upon is used.
332
333=item Filesys::DiskFree->total($id)
334
335Returns the amount of total space in bytes for $id, which is a scalar
336containing the device name of a disk or a filename, in which case the
337disk that filename in stored upon is used.
338
339=item Filesys::DiskFree->used($id)
340
341Returns the amount of used space in bytes for $id, which is a scalar
342containing the device name of a disk or a filename, in which case the
343disk that filename in stored upon is used.
344
345=head1 BUGS
346
347It should support more formats, currently only Linux, Irix, Solaris &
348BSD are supported. Other formats will be added as available. Please sent
349your OS Name & version, the 'best' df options to use, and the output of
350df with those options, and the contents of $^O if you have access to a
351non-supported format.
352
353=head1 AUTHOR
354
355Alan R. Barclay <gorilla@drink.com>
356