package Mogstored::ChildProcess::DiskUsage; use strict; use base 'Mogstored::ChildProcess'; my $docroot; sub pre_exec_init { my $class = shift; $SIG{TERM} = 'DEFAULT'; # override custom one from earlier $ENV{MOG_DOCROOT} = Perlbal->service('mogstored')->{docroot}; } sub run { $docroot = $ENV{MOG_DOCROOT}; die "\$ENV{MOG_DOCROOT} not set" unless $docroot; die "\$ENV{MOG_DOCROOT} not set to a directory" unless -d $docroot; # (runs in exec'd child process) $0 = "mogstored [diskusage]"; select((select(STDOUT), $|++)[0]); my $start_ppid = getppid(); # Discover whether or not we have GNU df. my $gnu_df = ''; `df -P / 2>/dev/null >/dev/null`; if ($? eq 0) { $gnu_df = '-P'; } while (1) { look_at_disk_usage($gnu_df); sleep 10; # shut ourselves down if our parent mogstored # has gone away. my $ppid = getppid(); exit(0) unless $ppid == $start_ppid && kill(0,$ppid); } } sub look_at_disk_usage { my $err = sub { warn "$_[0]\n"; }; my $path = $ENV{MOG_DOCROOT}; $path =~ s!/$!!; my $gnu_df = shift; # find all devices below us my @devnum; if (opendir(D, $path)) { @devnum = grep { /^dev\d+$/ } readdir(D); closedir(D); } else { return $err->("Failed to open $path: $!"); } foreach my $devnum (@devnum) { my $rval = `df $gnu_df -l -k $path/$devnum`; my $uperK = ($rval =~ /512-blocks/i) ? 2 : 1; # units per kB foreach my $l (split /\r?\n/, $rval) { next unless $l =~ /^(.+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)\s+(.+)$/; my ($dev, $total, $used, $avail, $useper, $disk) = ($1, $2, $3, $4, $5, $6); unless ($disk =~ m!$devnum/?$!) { $disk = "$path/$devnum"; } # FIXME: We're stupidly throwing away the 'avail' value here. # This causes mogilefs to run aground when used with ext # partitions using reserved space. Drop the reserved space from # the total, and in the future add available to the device table # and just use that. $total = $used + $avail; # create string to print my $now = time; my $output = { time => time(), device => $dev, # /dev/sdh1 total => int($total / $uperK), # integer: total KiB blocks used => int($used / $uperK), # integer: used KiB blocks available => int($avail / $uperK), # integer: available KiB blocks 'use' => $useper, # "45%" disk => $disk, # mount point of disk (/var/mogdata/dev8), or path if not a mount }; if ($ENV{MOG_DEV_USAGE_VIA_DU}) { my $size = `du -k -c -s $path/$devnum`; if ($size =~ /^(\d+)/) { $output->{used} = $1; } } # size of old file we'll be overwriting in place (we'll want # to pad with newlines/spaces, before we truncate it, for # minimizing races) my $ufile = "$disk/usage"; my $old_size = (-s $ufile) || 0; my $mode = $old_size ? "+<" : ">"; # string we'll be writing my $new_data = ""; foreach (sort keys %$output) { $new_data .= "$_: $output->{$_}\n"; } my $new_size = length $new_data; my $pad_len = $old_size > $new_size ? ($old_size - $new_size) : 0; $new_data .= "\n" x $pad_len; # write the file, all at once (with padding) then remove padding my $rv = open(my $fh, $mode, $ufile); unless ($rv) { $err->("Unable to open '$ufile' for writing: $!"); next; } unless (syswrite($fh, $new_data)) { close($fh); $err->("Error writing to '$ufile': $!"); next; } truncate($fh, $new_size) if $pad_len; close($fh); } } } 1;