1#!/usr/local/bin/perl 2 3# 4# Authentic Theme (https://github.com/authentic-theme/authentic-theme) 5# Copyright Ilia Rostovtsev <programming@rostovtsev.io> 6# Licensed under MIT (https://github.com/authentic-theme/authentic-theme/blob/master/LICENSE) 7# 8use strict; 9no warnings 'uninitialized'; 10 11use lib ("$ENV{'THEME_ROOT'}/lib"); 12use Async; 13 14BEGIN {push(@INC, "..");} 15use WebminCore; 16 17our (%in, $config_directory, $var_directory, $current_theme, $remote_user); 18do("$ENV{'THEME_ROOT'}/authentic-funcs.pl"); 19 20init_config(); 21ReadParse(); 22 23# Load theme language and settings 24our %text = load_language($current_theme); 25my %settings = settings("$config_directory/$current_theme/settings.js", 'settings_'); 26 27my $option = sub { 28 if ($_[1]) { 29 return $settings{"settings_sysinfo_real_time_$_[0]"}; 30 } 31 if (!scalar %settings) { 32 return 1; 33 } 34 return ($settings{"settings_sysinfo_real_time_$_[0]"} eq 'false') ? 0 : 1; 35}; 36 37my %data; 38my $tdata = {}; 39my $sdata = $in{'sdata'} ? 1 : 0; 40my $fdatad = "$var_directory/modules/$current_theme"; 41my $fdata = "$fdatad/stats-$remote_user.json"; 42my $cdata = read_file_contents($fdata); 43my $time = time(); 44my $ddata = sub { 45 my ($k, $d) = @_; 46 47 # Save and return data 48 if (!$k) { 49 if (&$option('stored')) { 50 51 # Clear existing cache if available features was disabled 52 my %map = (cpu => ['cpu', 'disk'], 53 mem => ['mem', 'virt'], 54 load => ['proc', 'net']); 55 56 foreach my $key (keys %map) { 57 my $feature = acl_system_status($key); 58 if (ref($map{$key}) eq 'ARRAY') { 59 foreach my $skey (@{ $map{$key} }) { 60 if (!$feature) { 61 delete $cdata->{$skey}; 62 } 63 } 64 } else { 65 if (!$feature) { 66 delete $cdata->{ $map{$key} }; 67 } 68 } 69 } 70 71 # Cache complete dataset 72 mkdir($fdatad, 0700) if (!-d $fdatad); 73 write_file_contents($fdata, convert_to_json($cdata)); 74 75 # Return requested data 76 if ($sdata) { 77 $data{'fcached'} = $cdata; 78 } else { 79 $data{'scached'} = $tdata; 80 } 81 } 82 return; 83 } 84 85 # Store complete dataset 86 if (ref($cdata->{$k}) ne 'ARRAY') { 87 $cdata->{$k} = []; 88 } 89 push(@{ $cdata->{$k} }, 90 { x => $time, 91 y => $d 92 }); 93 94 my $n = &$option('stored_length', 1) || 600; 95 96 # User option sanity check 97 if ($n < 600 || $n > 86400) { 98 $n = 600; 99 } 100 101 # Trim dataset 102 for my $i (0 .. @{ $cdata->{$k} }) { 103 if (defined(@{ $cdata->{$k} }[$i])) { 104 if (($time - @{ $cdata->{$k} }[$i]->{'x'}) > $n) { 105 delete @{ $cdata->{$k} }[$i]; 106 } 107 } 108 } 109 @{ $cdata->{$k} } = grep {$_ ne undef} @{ $cdata->{$k} }; 110 111 # Store single dataset 112 if (ref($tdata->{$k}) ne 'ARRAY') { 113 $tdata->{$k} = []; 114 } 115 push(@{ $tdata->{$k} }, 116 { x => time(), 117 y => $d 118 }); 119}; 120 121eval {$cdata = convert_from_json($cdata) if ($cdata);}; 122unlink($fdata), $cdata = {} if ($@); 123$cdata ||= {}; 124 125if ($in{'xhr-stats'} =~ /[[:alpha:]]/) { 126 my $target = $in{'xhr-stats'}; 127 if ($target eq 'general') { 128 129 # Run in async to reduce script execution time 130 my $network; 131 if (acl_system_status('load')) { 132 $network = Async->new(sub {network_stats('io')}); 133 } 134 135 if (foreign_check("proc")) { 136 foreign_require("proc"); 137 138 # CPU stats 139 if (acl_system_status('cpu')) { 140 my @cpuinfo = defined(&proc::get_cpu_info) ? proc::get_cpu_info() : (); 141 my @cpuusage = defined(&proc::get_cpu_io_usage) ? proc::get_cpu_io_usage() : (); 142 if (@cpuinfo && @cpuusage) { 143 144 # CPU load 145 my $cpu = int($cpuusage[0] + $cpuusage[1] + $cpuusage[3]); 146 $data{'cpu'} = [$cpu, text('body_load', ($cpuinfo[0], $cpuinfo[1], $cpuinfo[2]))]; 147 &$ddata('cpu', $cpu); 148 149 # Disk I/O 150 my $in = $cpuusage[5]; 151 my $out = $cpuusage[6]; 152 if ($in && $out || $in eq '0' || $out eq '0') { 153 $data{'io'} = [$in, $out]; 154 &$ddata('disk', [$in, $out]); 155 } 156 } 157 } 158 159 # Memory stats 160 if (acl_system_status('mem')) { 161 my @memory = defined(&proc::get_memory_info) ? proc::get_memory_info() : (); 162 if (@memory) { 163 $data{'mem'} = []; 164 $data{'virt'} = []; 165 166 if (@memory && $memory[0] && $memory[0] > 0) { 167 my $mem = (100 - int(($memory[1] / $memory[0]) * 100)); 168 $data{'mem'} = [$mem, 169 text(($memory[4] ? 'body_used_cached_total' : 'body_used'), 170 nice_size($memory[0] * 1024), 171 nice_size(($memory[0] - $memory[1]) * 1024), 172 ($memory[4] ? nice_size($memory[4] * 1024) : undef) 173 )]; 174 &$ddata('mem', $mem); 175 } 176 if (@memory && $memory[2] && $memory[2] > 0) { 177 my $virt = (100 - int(($memory[3] / $memory[2]) * 100)); 178 $data{'virt'} = [$virt, 179 text('body_used', 180 nice_size(($memory[2]) * 1024), 181 nice_size(($memory[2] - $memory[3]) * 1024) 182 )]; 183 &$ddata('virt', $virt); 184 } 185 } 186 } 187 188 # Number of running processes 189 if (acl_system_status('load')) { 190 my @processes = proc::list_processes(); 191 my $proc = scalar(@processes); 192 $data{'proc'} = $proc; 193 &$ddata('proc', $proc); 194 } 195 } 196 197 # Disk space 198 if (acl_system_status('disk')) { 199 if (foreign_check("mount") && &$option('status_disk')) { 200 foreign_require("mount"); 201 202 my @disk_space = defined(&mount::local_disk_space) ? mount::local_disk_space() : (); 203 if (@disk_space) { 204 $data{'disk'} = []; 205 206 if (@disk_space && $disk_space[0] && $disk_space[0] > 0) { 207 my $disk = int(($disk_space[0] - $disk_space[1]) / $disk_space[0] * 100); 208 $data{'disk'} = [$disk, 209 text('body_used_and_free', 210 nice_size($disk_space[0]), 211 nice_size($disk_space[1]), 212 nice_size($disk_space[0] - $disk_space[1]) 213 )]; 214 } 215 } 216 } 217 } 218 219 # Network I/O 220 if ($network) { 221 for (;;) { 222 if ($network->ready) { 223 if (!$network->error) { 224 my $nrs = unserialise_variable($network->result); 225 my $in = @{$nrs}[0]; 226 my $out = @{$nrs}[1]; 227 228 if ($in && $out || $in eq '0' || $out eq '0') { 229 $data{'net'} = [$in, $out]; 230 &$ddata('net', [$in, $out]); 231 } 232 } 233 last; 234 } 235 select(undef, undef, undef, 0.1); 236 } 237 } 238 239 # Reverse output for LTR users 240 if (get_text_ltr()) { 241 my @watched = ('mem', 'virt', 'disk'); 242 foreach my $key (@watched) { 243 if ($data{$key} && $data{$key}[1]) { 244 $data{$key}[1] = reverse_string($data{$key}[1], "/"); 245 } 246 } 247 } 248 } 249 &$ddata(); 250} 251 252print_json(\%data); 253