diff --git a/PveSensors/PVEMod_Utils.pm b/PveSensors/PVEMod_Utils.pm deleted file mode 100644 index a16d4e1..0000000 --- a/PveSensors/PVEMod_Utils.pm +++ /dev/null @@ -1,259 +0,0 @@ -package PVE::PVEMod::Utils; - -use strict; -use warnings; -use Exporter 'import'; - -use JSON; -use Fcntl qw(O_CREAT O_EXCL O_WRONLY); - -use PVE::PVEMod::Config qw($DEBUG_ENABLED $VERSION $pve_mod_working_dir); - -our @EXPORT_OK = qw( - debug - read_sysfs - is_process_alive - read_lock_pid - acquire_exclusive_lock - ensure_pve_mod_directory_exists - check_executable - startup_message - setup_collector_signals - safe_write_json - safe_read_json - parse_csv_line -); - -# ============================================================================ -# Debug -# ============================================================================ - -# debug function showing line number and call chain -# Usage: debug(__LINE__, "message") -sub debug { - return unless $DEBUG_ENABLED; - - my ($line, $message) = @_; - - my @caller1 = caller(1); # who called debug() - my @caller2 = caller(2); # parent of caller - - my $sub1 = $caller1[3] || 'main'; - my $sub2 = $caller2[3]; - - $sub1 =~ s/.*:://; - - if (defined $sub2) { - $sub2 =~ s/.*:://; - warn "[$sub2 -> $sub1:$line] $message\n"; - } else { - warn "[$sub1:$line] $message\n"; - } -} - -# ============================================================================ -# File / Process helpers -# ============================================================================ - -sub read_sysfs { - my ($path) = @_; - - return "unknown" unless defined $path && -f $path; - - if (open my $fh, '<', $path) { - my $value = <$fh>; - close $fh; - - if (defined $value) { - chomp $value; - $value =~ s/^\s+|\s+$//g; - return $value ne '' ? $value : "unknown"; - } - } - - return "unknown"; -} - -sub is_process_alive { - my ($pid) = @_; - return -d "/proc/$pid"; -} - -sub read_lock_pid { - my ($lock_path) = @_; - - return undef unless open(my $fh, '<', $lock_path); - - my $pid = <$fh>; - close($fh); - chomp $pid if defined $pid; - - return $pid; -} - -sub acquire_exclusive_lock { - my ($lock_path, $purpose) = @_; - $purpose //= 'lock'; - - my $fh; - - if (sysopen($fh, $lock_path, O_CREAT|O_EXCL|O_WRONLY, 0644)) { - debug(__LINE__, "Acquired $purpose on first try"); - return $fh; - } - - debug(__LINE__, ucfirst($purpose) . " exists, checking if stale"); - - my $lock_pid = read_lock_pid($lock_path); - - if (!defined $lock_pid) { - debug(__LINE__, "Could not read $purpose file: $!"); - return undef; - } - - if ($lock_pid eq '' || $lock_pid !~ /^\d+$/) { - debug(__LINE__, "Invalid PID in $purpose: '" . ($lock_pid // 'undefined') . "', removing"); - unlink($lock_path); - } elsif (is_process_alive($lock_pid)) { - debug(__LINE__, ucfirst($purpose) . " holder PID $lock_pid is still alive"); - return undef; - } else { - debug(__LINE__, ucfirst($purpose) . " holder PID $lock_pid is dead, removing stale lock"); - unlink($lock_path); - } - - unless (sysopen($fh, $lock_path, O_CREAT|O_EXCL|O_WRONLY, 0644)) { - debug(__LINE__, "Failed to acquire $purpose on retry: $!"); - return undef; - } - - debug(__LINE__, "Acquired $purpose after removing stale lock"); - return $fh; -} - -sub ensure_pve_mod_directory_exists { - unless (-d $pve_mod_working_dir) { - debug(__LINE__, "Creating directory $pve_mod_working_dir"); - unless (mkdir($pve_mod_working_dir, 0755)) { - debug(__LINE__, "Failed to create $pve_mod_working_dir: $!. PVE Mod cannot start."); - die "Failed to create $pve_mod_working_dir: $!"; - } - debug(__LINE__, "Directory $pve_mod_working_dir created"); - } else { - debug(__LINE__, "Directory $pve_mod_working_dir already exists"); - } -} - -# Returns 1 if executable exists, or debug mode is active with a debug file present. -# Returns 0 otherwise. -sub check_executable { - my ($exec_path, $type, $debug_mode_enabled, $debug_file) = @_; - - if (defined $debug_mode_enabled && $debug_mode_enabled) { - if (defined $debug_file && -f $debug_file) { - debug(__LINE__, "Debug mode enabled for $type, using debug file: $debug_file"); - return 1; - } elsif (defined $debug_file) { - debug(__LINE__, "Debug mode enabled for $type but debug file missing: $debug_file"); - return 0; - } else { - debug(__LINE__, "Debug mode enabled for $type, skipping executable check for $exec_path"); - return 1; - } - } - - unless (-x $exec_path) { - debug(__LINE__, "$type executable not found or not executable: $exec_path"); - return 0; - } - - debug(__LINE__, "$type executable found: $exec_path"); - return 1; -} - -sub startup_message { - debug(__LINE__, "PVE Mod is being started. Version $VERSION"); -} - -# Setup common TERM/INT signal handlers for collector processes. -# $shutdown_ref is a scalar ref that will be set to 1 on signal. -sub setup_collector_signals { - my ($name, $shutdown_ref, $extra_cleanup) = @_; - - $SIG{TERM} = sub { - debug(__LINE__, "Collector $name received SIGTERM"); - $$shutdown_ref = 1; - $extra_cleanup->() if $extra_cleanup; - }; - $SIG{INT} = sub { - debug(__LINE__, "Collector $name received SIGINT"); - $$shutdown_ref = 1; - $extra_cleanup->() if $extra_cleanup; - }; -} - -# ============================================================================ -# JSON helpers -# ============================================================================ - -sub safe_write_json { - my ($filepath, $data, $pretty) = @_; - $pretty //= 1; - - eval { - open my $fh, '>', $filepath or die "Failed to open $filepath: $!"; - my $json = $pretty ? JSON->new->pretty->encode($data) : encode_json($data); - print $fh $json; - close $fh; - debug(__LINE__, "Wrote JSON to $filepath"); - }; - if ($@) { - debug(__LINE__, "Error writing to $filepath: $@"); - return 0; - } - return 1; -} - -sub safe_read_json { - my ($filepath, $as_string) = @_; - - return unless -f $filepath; - - my $result; - eval { - open my $fh, '<', $filepath or die "Failed to open $filepath: $!"; - local $/; - my $json = <$fh>; - close $fh; - - if ($as_string) { - $result = $json; - } else { - $result = decode_json($json); - } - debug(__LINE__, "Read JSON from $filepath"); - }; - if ($@) { - debug(__LINE__, "Error reading $filepath: $@"); - return; - } - return $result; -} - -# ============================================================================ -# CSV helper -# ============================================================================ - -sub parse_csv_line { - my ($line, $expected_fields) = @_; - - return unless $line; - $line =~ s/^\s+|\s+$//g; - - my @values = map { s/^\s+|\s+$//gr } split(/,/, $line); - - return unless !$expected_fields || @values >= $expected_fields; - return @values; -} - -1; diff --git a/pve-mod-gui-sensors.sh b/pve-mod-gui-sensors.sh index f72e81f..5da14ac 100644 --- a/pve-mod-gui-sensors.sh +++ b/pve-mod-gui-sensors.sh @@ -1747,4 +1747,4 @@ done # If no arguments were provided or all arguments have been processed, print the usage message if [[ $executed -eq 0 ]]; then usage -fi \ No newline at end of file +fi