Use actual available disk space.
[MogileFS-Server.git] / lib / Mogstored / ChildProcess / DiskUsage.pm
blobc72bb25d41c25ed8561993f42caf8c830f8a7ee7
1 package Mogstored::ChildProcess::DiskUsage;
2 use strict;
3 use base 'Mogstored::ChildProcess';
5 my $docroot;
7 sub pre_exec_init {
8 my $class = shift;
9 $SIG{TERM} = 'DEFAULT'; # override custom one from earlier
10 $ENV{MOG_DOCROOT} = Perlbal->service('mogstored')->{docroot};
13 sub run {
14 $docroot = $ENV{MOG_DOCROOT};
15 die "\$ENV{MOG_DOCROOT} not set" unless $docroot;
16 die "\$ENV{MOG_DOCROOT} not set to a directory" unless -d $docroot;
18 # (runs in exec'd child process)
19 $0 = "mogstored [diskusage]";
20 select((select(STDOUT), $|++)[0]);
22 my $start_ppid = getppid();
24 while (1) {
25 look_at_disk_usage();
26 sleep 10;
28 # shut ourselves down if our parent mogstored
29 # has gone away.
30 my $ppid = getppid();
31 exit(0) unless $ppid == $start_ppid && kill(0,$ppid);
35 sub look_at_disk_usage {
36 my $err = sub { warn "$_[0]\n"; };
37 my $path = $ENV{MOG_DOCROOT};
38 $path =~ s!/$!!;
40 # find all devices below us
41 my @devnum;
42 if (opendir(D, $path)) {
43 @devnum = grep { /^dev\d+$/ } readdir(D);
44 closedir(D);
45 } else {
46 return $err->("Failed to open $path: $!");
49 foreach my $devnum (@devnum) {
50 my $rval = `df -P -l -k $path/$devnum`;
51 my $uperK = ($rval =~ /512-blocks/i) ? 2 : 1; # units per kB
52 foreach my $l (split /\r?\n/, $rval) {
53 next unless $l =~ /^(.+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)\s+(.+)$/;
54 my ($dev, $total, $used, $avail, $useper, $disk) = ($1, $2, $3, $4, $5, $6);
56 unless ($disk =~ m!$devnum/?$!) {
57 $disk = "$path/$devnum";
60 # FIXME: We're stupidly throwing away the 'avail' value here.
61 # This causes mogilefs to run aground when used with ext
62 # partitions using reserved space. Drop the reserved space from
63 # the total, and in the future add available to the device table
64 # and just use that.
65 $total = $used + $avail;
67 # create string to print
68 my $now = time;
69 my $output = {
70 time => time(),
71 device => $dev, # /dev/sdh1
72 total => int($total / $uperK), # integer: total KiB blocks
73 used => int($used / $uperK), # integer: used KiB blocks
74 available => int($avail / $uperK), # integer: available KiB blocks
75 'use' => $useper, # "45%"
76 disk => $disk, # mount point of disk (/var/mogdata/dev8), or path if not a mount
79 if ($ENV{MOG_DEV_USAGE_VIA_DU}) {
80 my $size = `du -k -c -s $path/$devnum`;
81 if ($size =~ /^(\d+)/) {
82 $output->{used} = $1;
86 # size of old file we'll be overwriting in place (we'll want
87 # to pad with newlines/spaces, before we truncate it, for
88 # minimizing races)
89 my $ufile = "$disk/usage";
90 my $old_size = (-s $ufile) || 0;
91 my $mode = $old_size ? "+<" : ">";
93 # string we'll be writing
94 my $new_data = "";
95 foreach (sort keys %$output) {
96 $new_data .= "$_: $output->{$_}\n";
99 my $new_size = length $new_data;
100 my $pad_len = $old_size > $new_size ? ($old_size - $new_size) : 0;
101 $new_data .= "\n" x $pad_len;
103 # write the file, all at once (with padding) then remove padding
104 my $rv = open(my $fh, $mode, $ufile);
105 unless ($rv) {
106 $err->("Unable to open '$ufile' for writing: $!");
107 next;
109 unless (syswrite($fh, $new_data)) {
110 close($fh);
111 $err->("Error writing to '$ufile': $!");
112 next;
114 truncate($fh, $new_size) if $pad_len;
115 close($fh);