package ONO::Core::HostOS;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################
use strict;
use ONO::IO;
use ONO::Core::Kernel;
#: ONO_Core_HostOS is responsible for low level communication with the host
#: operating system.
#:
#: Using this module in production projects should be avoided whenever
#: possible, try to use ONO::IO or other library / toolbox features instead.
###############################################################################
# Host OS
###############################################################################
my $HOSTOS = "BSD/UNIX";
if (-e "/Library/QuickTime") {
$HOSTOS = "MacOS";
}
sub host_os {
#: Returns the host os type, either BSD/UNIX or MacOS. This is required at
#: the ONO::Core level, as MacOS is different from other systems in some
#: ways. Linux distributions will simply be identified as BSD/UNIX systems.
return $HOSTOS;
}
###############################################################################
# CPAN
###############################################################################
sub cpan {
#: Returns a list of CPAN Perl Modules that are recommended for ONO, and
#: that are required for all ONO features to be fully functional.
#:
#: Switches:
#:
#: -H human readable information about requirements (HTML)
my ($CPAN,$err1,$err2);
foreach my $module (ONO::IO->list("ono/sys/ONO/Resources/PerlModules.txt")) {
$module =~ s~^(.*?):(.*)\n~$2~;
my $type = $1;
if ($_[1] =~ /H/) {
eval "use $module";
if (!$@) {
# $module = qq~<span class="green">$module</span>~;
} else {
if ($type eq "REQUIRED") {
$module = qq~<span class="lightred">$module</span>~;
$err1++;
}
if ($type eq "RECOMMENDED") {
$module = qq~<span class="orange">$module</span>~;
$err2++;
}
}
}
$CPAN .= "$module, ";
}
$CPAN =~ s~, $~~;
return ($CPAN,$err1,$err2);
}
###############################################################################
# SYSTEM INFO
###############################################################################
sub system {
#: Returns the host os type, plus some additional system information.
my $SYS;
if ($HOSTOS eq "MacOS") {
# maxosx
my @data = `sw_vers`;
foreach my $dat (@data) {
$dat =~ s~[^A-Za-z0-9\.\:]~~g;
if ($dat =~ s~ProductVersion:~~) {
$SYS = "MacOSX $dat";
}
}
} else {
# linux
my @data = `cat /etc/issue`;
foreach my $dat (@data) {
$dat =~ s~[^A-Za-z0-9\.\:\ \/]~~g;
if ($dat =~ m~(Linux|Ubuntu)~i) {
$SYS = $dat;
}
}
}
$SYS =~ s~[^A-Za-z0-9\ \-\.]~~gi;
if (!$SYS) {
$SYS = "BSD/UNIX";
}
return $SYS;
}
###############################################################################
# PERL INFO
###############################################################################
sub perl {
#: Return the current Perl version.
my @data = `perl -v`;
foreach my $dat (@data) {
$dat =~ s~[^A-Za-z0-9\.\:\ \/\,]~~g;
if ($dat =~ s~This is perl, v(.*?) built~~i) {
return "Perl $1";
}
}
}
###############################################################################
# WHO
###############################################################################
sub who {
#: List UNIX users who are currently online on the host OS.
my @who;
foreach my $line (`who`) {
my $ip;
if ($line =~ /\((.*?)\)/) {
$ip = $1;
}
$line =~ s~( |\n|\r|\t)(.*)$~~;
$line =~ s~( |\n|\r|\t)~~gi;
if ($ip) {
$line .= qq~ ($ip)~;
}
@who = (@who,$line);
}
return @who;
}
###############################################################################
# MYSQL INFO
###############################################################################
sub mysql {
#: Return the current MySQL version.
my $MYSQL = `mysql -V`;
# ubuntu:
if (!$MYSQL) {
$MYSQL = `/usr/bin/mysql -V`;
$MYSQL =~ s~^/usr/bin/mysql~~;
}
# macos:
if (!$MYSQL) {
$MYSQL = `/usr/local/mysql/bin/mysql -V`;
$MYSQL =~ s~^/usr/local/mysql/bin/mysql~~;
}
$MYSQL =~ s~(\n|\r|\t)~~gi;
$MYSQL =~ s~^ ~~gi;
$MYSQL =~ s~^ ~~gi;
return $MYSQL;
}
###############################################################################
# CPU LOAD STATISTICS
###############################################################################
# This uses mod_perl caching, as the output usually doesn't change quickly
my ($cpu_load_cached) = &cpu_load;
my $cpu_load_cached_time = time();
sub cpu_load_cached {
#: Return the current CPU load, which is being cached for 5 seconds.
#: This returns a scalar value, which is the current load.
if ($ENV{'MOD_PERL'} && time() > $cpu_load_cached_time + 4) {
($cpu_load_cached) = &cpu_load;
$cpu_load_cached_time = time();
}
return $cpu_load_cached;
}
sub cpu_load {
#: Return the current CPU load as an array (UNIX-like format).
my ($l1,$l2,$l3);
# detect the OS platform
if ($HOSTOS eq "MacOS") {
# if it's MacOSX:
my $loadavg = ONO::Core::Kernel->exec("/usr/sbin/sysctl vm.loadavg");
$loadavg =~ s~[^0-9\ \{\}]~~g;
$loadavg =~ m~\{ (.*?) (.*?) (.*?)\}~;
$l1 = $1;
$l2 = $2;
$l3 = $3;
} else {
# if it's Linux:
foreach my $load (ONO::Core::Kernel->sys("list","/proc/loadavg")) {
if ($load =~ /(.*?)\.(.*?) (.*?)\.(.*?) (.*?)\.(.*?) /) {
$l1 = "$1$2";
$l2 = "$3$4";
$l3 = "$5$6";
}
}
}
$l1 =~ s~[^0-9]~~g;
$l2 =~ s~[^0-9]~~g;
$l3 =~ s~[^0-9]~~g;
return ($l1,$l2,$l3);
}
###############################################################################
# CPU LOAD STATISTICS
###############################################################################
sub cpu_load_percent {
#: Return the current CPU load as percentage, taking multiple CPU cores and
#: multithreading into consideration.
#:
#: By default, this returns 3x percentage, 3x absolute, plus the number of
#: cores.
#:
#: -c current (returns string instead of array)
my $switches = $_[1];
my $cores = &cpu_cores;
my @load = &cpu_load;
my @per;
for (my $i = 0; $i < 3; $i++) {
$per[$i] = int(100/($cores*100)*$load[$i]);
}
if ($switches =~ /c/) {
return $per[0];
} else {
return ($per[0],$per[1],$per[2],$load[0],$load[1],$load[2],$cores);
}
}
###############################################################################
# CPU LOAD PASS
###############################################################################
sub cpu_load_pass {
#: CPU load threshold test, using the format cpu_load_pass(percent).
#:
#: Returns 1 if load LOWER than percent
#: Returns 0 if load HIGHER than percent
#:
#: Example: ONO_Core_HostOS->cpu_load_pass(50) will thus allow to execute
#: code if the ENTIRE load of the system is LOWER than 50 percent
#:
#: Don't use this function here if possible, use ONO::IO->sysload() instead.
if (&cpu_load_percent("","c") < $_[1]) {
return 1;
} else {
return 0;
}
}
###############################################################################
# SYSTEM STATUS
###############################################################################
sub system_status {
#: Return the current system status, or system health.
#:
#: Switches:
#:
#: -h human readable
#: -H human readable in colored box
my @per = &cpu_load_percent;
my (
$mem,
$used,
$free,
$mem_per1,
$mem_per2,
) = &mem_load;
if ($_[1] =~ /(h|H)/) {
my $STAT = "CPU/$per[6]: $per[3] ($per[0]%), $per[4] ($per[1]%), $per[5] ($per[2]%) / MEM: $used/$mem ($mem_per1%)";
if ($_[1] =~ /H/) {
my $color = "green";
if ($per[0] > 50) {
$color = "yellow";
}
if ($per[0] > 75) {
$color = "red";
}
$STAT = qq~<div class="box_$color pad10_2">$STAT</div>~;
}
return $STAT;
} else {
return (@per,$mem,$used,$free,$mem_per1,$mem_per2);
}
}
###############################################################################
# CPU CORES
###############################################################################
# This uses mod_perl caching, as the output usually doesn't change quickly
my $cores = &_cpu_cores;
sub cpu_cores {
#: Return the number of current CPU cores.
return $cores;
}
sub _cpu_cores {
#: Internal function, helps to detect CPU cores.
#: Don't use this, use cpu_cores() instead.
my $cores = 1;
# detect the OS platform
if (-e "/Library/Frameworks") {
$cores = `/usr/sbin/sysctl -n hw.ncpu`;
} else {
$cores = `grep ^processor /proc/cpuinfo | wc -l`;
}
$cores =~ s~[^0-9]~~g;
return $cores;
}
###############################################################################
# MEMORY
###############################################################################
sub mem {
#: Return current total memory of the system.
my ($mem) = &mem_load;
return $mem;
}
###############################################################################
# MEMORY
###############################################################################
sub mem_load_percent {
#: Return current memory load, as percentage.
my ($mem,$used) = &mem_load;
if ($mem && $used) {
return int(100/$mem*$used);
} else {
return 100;
}
}
###############################################################################
# MEMORY
###############################################################################
sub mem_load {
#: Return current memory load, detailed (total, used, free, percent,
#: percent).
my ($mem,$used,$free,$p1,$p2) = (0,0,0,0,0);
# detect the OS platform
if (-e "/Library/Frameworks") {
$mem = `/usr/sbin/sysctl -n hw.memsize`;
$mem = int($mem/1024/1024);
foreach my $line (`top -l 1`) {
if ($line =~ /^PhysMem: (.*?) used \((.*?) wired\), (.*?) unused\./) {
my $free = $3;
$free =~ s~[^0-9]~~;
$used = $mem - $free;
}
}
} else {
my ($fr,$bu,$ca);
foreach my $line (`more /proc/meminfo`) {
if ($line =~ /^MemTotal:(.*?)kB/) {
$mem = $1;
}
if ($line =~ /^MemFree:(.*?)kB/) {
$fr = $1;
}
if ($line =~ /^Buffers:(.*?)kB/) {
$bu = $1;
}
if ($line =~ /^Cached:(.*?)kB/) {
$ca = $1;
}
}
$used = int(($mem - $fr - $bu - $ca)/1024);
$mem = int($mem/1024);
}
$mem =~ s~[^0-9]~~g;
$used =~ s~[^0-9]~~g;
$free = $mem-$used;
if ($mem) {
$p1 = int(100/$mem*$used);
$p2 = int(100/$mem*$free);
}
return ($mem,$used,$free,$p1,$p2);
}
###############################################################################
# end of script
###############################################################################
1;
__END__