Checking in changes prior to tagging of version 2.73.
[MogileFS-Server.git] / lib / Mogstored / ChildProcess / DiskUsage.pm
blob24e0a47f4ea16d273ace86a791acdf93873987b4
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 # Discover whether or not we have GNU df.
25 my $gnu_df = '';
26 `df -P / 2>/dev/null >/dev/null`;
27 if ($? eq 0) {
28 $gnu_df = '-P';
31 while (1) {
32 look_at_disk_usage($gnu_df);
33 sleep 10;
35 # shut ourselves down if our parent mogstored
36 # has gone away.
37 my $ppid = getppid();
38 exit(0) unless $ppid == $start_ppid && kill(0,$ppid);
42 sub look_at_disk_usage {
43 my $err = sub { warn "$_[0]\n"; };
44 my $path = $ENV{MOG_DOCROOT};
45 $path =~ s!/$!!;
46 my $gnu_df = shift;
48 # find all devices below us
49 my @devnum;
50 if (opendir(D, $path)) {
51 @devnum = grep { /^dev\d+$/ } readdir(D);
52 closedir(D);
53 } else {
54 return $err->("Failed to open $path: $!");
57 foreach my $devnum (@devnum) {
58 my $rval = `df $gnu_df -l -k $path/$devnum`;
59 my $uperK = ($rval =~ /512-blocks/i) ? 2 : 1; # units per kB
60 foreach my $l (split /\r?\n/, $rval) {
61 next unless $l =~ /^(.+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)\s+(.+)$/;
62 my ($dev, $total, $used, $avail, $useper, $disk) = ($1, $2, $3, $4, $5, $6);
64 unless ($disk =~ m!$devnum/?$!) {
65 $disk = "$path/$devnum";
68 # FIXME: We're stupidly throwing away the 'avail' value here.
69 # This causes mogilefs to run aground when used with ext
70 # partitions using reserved space. Drop the reserved space from
71 # the total, and in the future add available to the device table
72 # and just use that.
73 $total = $used + $avail;
75 # create string to print
76 my $now = time;
77 my $output = {
78 time => time(),
79 device => $dev, # /dev/sdh1
80 total => int($total / $uperK), # integer: total KiB blocks
81 used => int($used / $uperK), # integer: used KiB blocks
82 available => int($avail / $uperK), # integer: available KiB blocks
83 'use' => $useper, # "45%"
84 disk => $disk, # mount point of disk (/var/mogdata/dev8), or path if not a mount
87 if ($ENV{MOG_DEV_USAGE_VIA_DU}) {
88 my $size = `du -k -c -s $path/$devnum`;
89 if ($size =~ /^(\d+)/) {
90 $output->{used} = $1;
94 # size of old file we'll be overwriting in place (we'll want
95 # to pad with newlines/spaces, before we truncate it, for
96 # minimizing races)
97 my $ufile = "$disk/usage";
98 my $old_size = (-s $ufile) || 0;
99 my $mode = $old_size ? "+<" : ">";
101 # string we'll be writing
102 my $new_data = "";
103 foreach (sort keys %$output) {
104 $new_data .= "$_: $output->{$_}\n";
107 my $new_size = length $new_data;
108 my $pad_len = $old_size > $new_size ? ($old_size - $new_size) : 0;
109 $new_data .= "\n" x $pad_len;
111 # write the file, all at once (with padding) then remove padding
112 my $rv = open(my $fh, $mode, $ufile);
113 unless ($rv) {
114 $err->("Unable to open '$ufile' for writing: $!");
115 next;
117 unless (syswrite($fh, $new_data)) {
118 close($fh);
119 $err->("Error writing to '$ufile': $!");
120 next;
122 truncate($fh, $new_size) if $pad_len;
123 close($fh);