Compare commits

...

16 Commits

Author SHA1 Message Date
897e86ad60 2.4.5 2025-03-25 13:12:38 +00:00
ed78db20e2 fix(install): Improve interactive terminal detection and update installation instructions 2025-03-25 13:12:38 +00:00
bd00dfe02c 2.4.4 2025-03-25 13:08:28 +00:00
55c040df82 fix(install): Improve interactive mode detection and non-interactive installation handling in install.sh 2025-03-25 13:08:28 +00:00
e68654a022 2.4.3 2025-03-25 12:57:12 +00:00
89a5d23d2f fix(readme): Update Quick Install command syntax in readme for auto-yes installation 2025-03-25 12:57:12 +00:00
f9aa1cfd2f 2.4.2 2025-03-25 11:49:50 +00:00
e47f316d0a fix(daemon): Refactor shutdown initiation logic in daemon by moving the initiateShutdown and monitorDuringShutdown methods from the SNMP manager to the daemon, and update calls accordingly 2025-03-25 11:49:50 +00:00
901127f784 2.4.1 2025-03-25 11:36:11 +00:00
dc4fd5afba fix(docs): Update readme with detailed legal and trademark guidance 2025-03-25 11:36:11 +00:00
a7ced10f92 2.4.0 2025-03-25 11:31:24 +00:00
9b9e009523 feat(installer): Add auto-yes flag to installer and update installation documentation 2025-03-25 11:31:24 +00:00
1819b6827a 2.3.0 2025-03-25 11:25:03 +00:00
bd5b85f6b0 feat(installer/cli): Add OS detection and git auto-installation support to install.sh and improve service setup prompt in CLI 2025-03-25 11:25:03 +00:00
c7db209da7 2.2.0 2025-03-25 11:17:10 +00:00
bbb8f4a22c feat(cli): Add config command to display current configuration and update CLI help 2025-03-25 11:17:10 +00:00
9 changed files with 505 additions and 43 deletions

View File

@ -1,5 +1,57 @@
# Changelog
## 2025-03-25 - 2.4.5 - fix(install)
Improve interactive terminal detection and update installation instructions
- Enhanced install.sh to better detect non-interactive environments and provide clearer guidance for both interactive and non-interactive installations
- Updated README.md quick install instructions to recommend process substitution and clarify auto-yes usage
## 2025-03-25 - 2.4.4 - fix(install)
Improve interactive mode detection and non-interactive installation handling in install.sh
- Detect and warn when running without a controlling terminal
- Attempt to use /dev/tty for user input when possible
- Update prompts and error messages for auto-installation of dependencies
- Clarify installation instructions in readme for interactive and non-interactive modes
## 2025-03-25 - 2.4.3 - fix(readme)
Update Quick Install command syntax in readme for auto-yes installation
- Changed installation command to use: curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh | sudo bash -c "bash -s -- -y"
## 2025-03-25 - 2.4.2 - fix(daemon)
Refactor shutdown initiation logic in daemon by moving the initiateShutdown and monitorDuringShutdown methods from the SNMP manager to the daemon, and update calls accordingly
- Moved initiateShutdown and monitorDuringShutdown to the daemon class for improved cohesion
- Updated references in the daemon to call its own shutdown method instead of the SNMP manager
- Removed redundant initiateShutdown method from the SNMP manager
## 2025-03-25 - 2.4.1 - fix(docs)
Update readme with detailed legal and trademark guidance
- Clarified legal section by adding trademark and company information
- Ensured users understand that licensing terms do not imply endorsement by the company
## 2025-03-25 - 2.4.0 - feat(installer)
Add auto-yes flag to installer and update installation documentation
- Enhance install.sh to parse -y/--yes and -h/--help options, automating git installation when auto-yes is provided
- Improve user prompts for dependency installation and provide clearer instructions
- Update readme.md to document new installer options and enhanced file system and service changes details
## 2025-03-25 - 2.3.0 - feat(installer/cli)
Add OS detection and git auto-installation support to install.sh and improve service setup prompt in CLI
- Implemented helper functions in install.sh to detect OS type and automatically install git if missing
- Prompt user for git installation if not present before cloning the repository
- Enhanced CLI service setup flow to offer starting the NUPST service immediately after installation
## 2025-03-25 - 2.2.0 - feat(cli)
Add 'config' command to display current configuration and update CLI help
- Introduce new 'config' command to show SNMP settings, thresholds, and configuration file location
- Update help text to include details for 'nupst config' command
## 2025-03-25 - 2.1.0 - feat(cli)
Add uninstall command to CLI and update shutdown delay for graceful VM shutdown

View File

@ -2,7 +2,45 @@
# NUPST Installer Script
# Downloads and installs NUPST globally on the system
# Can be used directly with curl: curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh | sudo bash
# Can be used directly with curl:
# Without auto-installing dependencies:
# curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh | sudo bash
# With auto-installing dependencies:
# curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh | sudo bash -s -- -y
#
# Options:
# -y, --yes Automatically answer yes to all prompts
# -h, --help Show this help message
# Parse command line arguments
AUTO_YES=0
SHOW_HELP=0
for arg in "$@"; do
case $arg in
-y|--yes)
AUTO_YES=1
shift
;;
-h|--help)
SHOW_HELP=1
shift
;;
*)
# Unknown option
;;
esac
done
if [ $SHOW_HELP -eq 1 ]; then
echo "NUPST Installer Script"
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " -y, --yes Automatically answer yes to all prompts"
echo " -h, --help Show this help message"
exit 0
fi
# Check if running as root
if [ "$EUID" -ne 0 ]; then
@ -12,11 +50,112 @@ fi
# Detect if script is being piped or run directly
PIPED=0
INTERACTIVE=1
if [ ! -t 0 ]; then
# Being piped, need to clone the repo
PIPED=1
fi
# Check if stdin is a terminal
if [ ! -t 0 ] || [ ! -t 1 ]; then
# Either stdin or stdout is not a terminal, check if -y was provided
if [ $AUTO_YES -ne 1 ]; then
echo "Script detected it's running in a non-interactive environment without -y flag."
echo "Attempting to find a controlling terminal for interactive prompts..."
# Try to use a controlling terminal for user input
if [ -t 1 ]; then
# Stdout is a terminal, use it
exec < /dev/tty 2>/dev/null || INTERACTIVE=0
else
# Try to find controlling terminal
exec < /dev/tty 2>/dev/null || INTERACTIVE=0
fi
if [ $INTERACTIVE -eq 0 ]; then
echo "ERROR: No controlling terminal available for interactive prompts."
echo "For interactive installation (RECOMMENDED):"
echo " sudo bash <(curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh)"
echo "For non-interactive installation with automatic dependency installation:"
echo " sudo bash <(curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh) -y"
exit 1
else
echo "Interactive terminal found, continuing with prompts..."
fi
fi
fi
# Helper function to detect OS type
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
elif type lsb_release >/dev/null 2>&1; then
OS=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
elif [ -f /etc/lsb-release ]; then
. /etc/lsb-release
OS=$DISTRIB_ID
elif [ -f /etc/debian_version ]; then
OS="debian"
elif [ -f /etc/redhat-release ]; then
if grep -q "CentOS" /etc/redhat-release; then
OS="centos"
elif grep -q "Fedora" /etc/redhat-release; then
OS="fedora"
else
OS="rhel"
fi
else
OS=$(uname -s)
fi
echo $OS
}
# Helper function to install git
install_git() {
OS=$(detect_os)
echo "Detected OS: $OS"
case "$OS" in
ubuntu|debian|pop|mint|elementary|kali|zorin)
echo "Installing git using apt..."
apt-get update && apt-get install -y git
;;
fedora|rhel|centos|almalinux|rocky)
echo "Installing git using dnf/yum..."
if command -v dnf &> /dev/null; then
dnf install -y git
else
yum install -y git
fi
;;
arch|manjaro|endeavouros|garuda)
echo "Installing git using pacman..."
pacman -Sy --noconfirm git
;;
opensuse*|suse|sles)
echo "Installing git using zypper..."
zypper install -y git
;;
alpine)
echo "Installing git using apk..."
apk add git
;;
*)
echo "Unsupported OS: $OS"
echo "Please install git manually and run the installer again."
exit 1
;;
esac
# Check if git was installed successfully
if ! command -v git &> /dev/null; then
echo "Failed to install git. Please install git manually and run the installer again."
exit 1
fi
echo "Git installed successfully."
}
# Define installation directory
INSTALL_DIR="/opt/nupst"
REPO_URL="https://code.foss.global/serve.zone/nupst.git"
@ -26,8 +165,31 @@ if [ $PIPED -eq 1 ]; then
# Check if git is installed
if ! command -v git &> /dev/null; then
echo "Git is required but not installed. Please install git first."
exit 1
echo "Git is required but not installed."
if [ $AUTO_YES -eq 1 ]; then
echo "Auto-installing git (-y flag provided)..."
install_git
elif [ $INTERACTIVE -eq 1 ]; then
# If interactive and no -y flag, ask the user
echo "Would you like to install git now? (y/N): "
read -r install_git_prompt
if [[ "$install_git_prompt" =~ ^[Yy]$ ]]; then
install_git
else
echo "Git installation skipped. Please install git manually and run the installer again."
echo "Alternatively, you can run the installer with -y flag to automatically install git:"
echo " sudo bash install.sh -y"
exit 1
fi
else
# Non-interactive mode without -y flag
echo "Error: Git is required but not installed."
echo "In non-interactive mode, use -y flag to auto-install dependencies:"
echo " curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh | sudo bash -s -- -y"
exit 1
fi
fi
# Check if installation directory exists

21
license Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Task Venture Capital GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,6 +1,6 @@
{
"name": "@serve.zone/nupst",
"version": "2.1.0",
"version": "2.4.5",
"description": "Node.js UPS Shutdown Tool for SNMP-enabled UPS devices",
"main": "dist/index.js",
"bin": {

View File

@ -19,8 +19,16 @@ NUPST is a command-line tool that monitors SNMP-enabled UPS devices and initiate
### Quick Install (One-line command)
```bash
# Install directly without cloning the repository (requires root privileges)
curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh | sudo bash
# RECOMMENDED: Install interactively using process substitution (requires root privileges)
# This ensures proper interactive prompts for dependencies
sudo bash <(curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh)
# Alternative: Install with auto-yes for dependencies (will install git automatically if needed)
# Use this for automated/non-interactive installations
sudo bash <(curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh) -y
# For systems where process substitution is not available (e.g., some older shells)
curl -sSL https://code.foss.global/serve.zone/nupst/raw/branch/main/install.sh | sudo bash -s -- -y
```
### Direct from Git
@ -33,20 +41,58 @@ cd nupst
# Option 1: Quick install (requires root privileges)
sudo ./install.sh
# Option 1a: Quick install with auto-yes for dependencies
sudo ./install.sh -y
# Option 2: Manual setup
./setup.sh
sudo ln -s $(pwd)/bin/nupst /usr/local/bin/nupst
```
### Installation Options
The installer script (`install.sh`) supports the following options:
```
-y, --yes Automatically answer yes to all prompts (like installing git)
-h, --help Show the help message
```
### From NPM
```bash
npm install -g @serve.zone/nupst
```
## System Changes
When installed, NUPST makes the following changes to your system:
### File System Changes
| Path | Description |
|------|-------------|
| `/opt/nupst/` | Main installation directory containing the NUPST files |
| `/etc/nupst/config.json` | Configuration file |
| `/usr/local/bin/nupst` | Symlink to the NUPST executable |
| `/etc/systemd/system/nupst.service` | Systemd service file (when enabled) |
### Service Changes
- Creates and enables a systemd service called `nupst.service` (when enabled with `nupst enable`)
- The service runs with root permissions to allow system shutdown capabilities
### Network Access
- NUPST only communicates with your UPS device via SNMP (default port 161)
- Brief connections to npmjs.org to check for updates
## Uninstallation
```bash
# Using the CLI tool:
sudo nupst uninstall
# If installed from git repository:
cd /path/to/nupst
sudo ./uninstall.sh
@ -57,9 +103,10 @@ npm uninstall -g @serve.zone/nupst
The uninstaller will:
- Stop and disable the systemd service (if installed)
- Remove the systemd service file
- Remove the symlink from /usr/local/bin
- Optionally remove configuration files from /etc/nupst
- Remove the systemd service file from `/etc/systemd/system/nupst.service`
- Remove the symlink from `/usr/local/bin/nupst`
- Optionally remove configuration files from `/etc/nupst/`
- Remove the repository directory from `/opt/nupst/` (when using `nupst uninstall`)
## Usage
@ -76,7 +123,9 @@ Usage:
nupst status - Show status of the systemd service and UPS status
nupst setup - Run the interactive setup to configure SNMP settings
nupst test - Test the current configuration by connecting to the UPS
nupst config - Display the current configuration
nupst update - Update NUPST from repository and refresh systemd service (requires root)
nupst uninstall - Completely uninstall NUPST from the system (requires root)
nupst help - Show this help message
Options:
@ -208,6 +257,21 @@ NUPST was designed with security in mind:
The codebase is small, focused, and designed to be easily auditable. All code is open source and available for review.
## License
## License and Legal Information
MIT
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
### Company Information
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/nupst',
version: '2.1.0',
version: '2.4.5',
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
}

110
ts/cli.ts
View File

@ -101,6 +101,10 @@ export class NupstCli {
case 'uninstall':
await this.uninstall();
break;
case 'config':
await this.showConfig();
break;
case 'help':
default:
@ -372,6 +376,7 @@ Usage:
nupst status - Show status of the systemd service and UPS status
nupst setup - Run the interactive setup to configure SNMP settings
nupst test - Test the current configuration by connecting to the UPS
nupst config - Display the current configuration
nupst update - Update NUPST from repository and refresh systemd service (requires root)
nupst uninstall - Completely uninstall NUPST from the system (requires root)
nupst help - Show this help message
@ -829,7 +834,7 @@ Options:
}
/**
* Optionally enable systemd service
* Optionally enable and start systemd service
* @param prompt Function to prompt for user input
*/
private async optionallyEnableService(prompt: (question: string) => Promise<string>): Promise<void> {
@ -838,11 +843,110 @@ Options:
} else {
const setupService = await prompt('Would you like to enable NUPST as a system service? (y/N): ');
if (setupService.toLowerCase() === 'y') {
await this.nupst.getSystemd().install();
console.log('Service installed. Use "nupst start" to start the service.');
try {
await this.nupst.getSystemd().install();
console.log('Service installed and enabled to start on boot.');
// Ask if the user wants to start the service now
const startService = await prompt('Would you like to start the NUPST service now? (Y/n): ');
if (startService.toLowerCase() !== 'n') {
await this.nupst.getSystemd().start();
console.log('NUPST service started successfully.');
} else {
console.log('Service not started. Use "nupst start" to start the service manually.');
}
} catch (error) {
console.error(`Failed to setup service: ${error.message}`);
}
}
}
}
/**
* Display the current configuration
*/
private async showConfig(): Promise<void> {
try {
// Try to load configuration
try {
await this.nupst.getDaemon().loadConfig();
} catch (error) {
console.error('┌─ Configuration Error ─────────────────────┐');
console.error('│ No configuration found.');
console.error('│ Please run \'nupst setup\' first to create a configuration.');
console.error('└──────────────────────────────────────────┘');
return;
}
// Get current configuration
const config = this.nupst.getDaemon().getConfig();
console.log('┌─ NUPST Configuration ──────────────────────┐');
// SNMP Settings
console.log('│ SNMP Settings:');
console.log(`│ Host: ${config.snmp.host}`);
console.log(`│ Port: ${config.snmp.port}`);
console.log(`│ Version: ${config.snmp.version}`);
console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
if (config.snmp.version === 1 || config.snmp.version === 2) {
console.log(`│ Community: ${config.snmp.community}`);
} else if (config.snmp.version === 3) {
console.log(`│ Security Level: ${config.snmp.securityLevel}`);
console.log(`│ Username: ${config.snmp.username}`);
// Show auth and privacy details based on security level
if (config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv') {
console.log(`│ Auth Protocol: ${config.snmp.authProtocol || 'None'}`);
}
if (config.snmp.securityLevel === 'authPriv') {
console.log(`│ Privacy Protocol: ${config.snmp.privProtocol || 'None'}`);
}
// Show timeout value
console.log(`│ Timeout: ${config.snmp.timeout / 1000} seconds`);
}
// Show OIDs if custom model is selected
if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) {
console.log('│ Custom OIDs:');
console.log(`│ Power Status: ${config.snmp.customOIDs.POWER_STATUS || 'Not set'}`);
console.log(`│ Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}`);
console.log(`│ Battery Runtime: ${config.snmp.customOIDs.BATTERY_RUNTIME || 'Not set'}`);
}
// Thresholds
console.log('│ Thresholds:');
console.log(`│ Battery: ${config.thresholds.battery}%`);
console.log(`│ Runtime: ${config.thresholds.runtime} minutes`);
console.log(`│ Check Interval: ${config.checkInterval / 1000} seconds`);
// Configuration file location
console.log('│');
console.log('│ Configuration File Location:');
console.log('│ /etc/nupst/config.json');
console.log('└──────────────────────────────────────────┘');
// Show service status
try {
const isActive = execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
const isEnabled = execSync('systemctl is-enabled nupst.service || true').toString().trim() === 'enabled';
console.log('┌─ Service Status ─────────────────────────┐');
console.log(`│ Service Active: ${isActive ? 'Yes' : 'No'}`);
console.log(`│ Service Enabled: ${isEnabled ? 'Yes' : 'No'}`);
console.log('└──────────────────────────────────────────┘');
} catch (error) {
// Ignore errors checking service status
}
} catch (error) {
console.error(`Failed to display configuration: ${error.message}`);
}
}
/**
* Completely uninstall NUPST from the system

View File

@ -1,7 +1,11 @@
import * as fs from 'fs';
import * as path from 'path';
import { exec } from 'child_process';
import { promisify } from 'util';
import { NupstSnmp, type ISnmpConfig } from './snmp.js';
const execAsync = promisify(exec);
/**
* Configuration interface for the daemon
*/
@ -269,7 +273,7 @@ export class NupstDaemon {
if (status.batteryCapacity < this.config.thresholds.battery) {
console.log('⚠️ WARNING: Battery capacity below threshold');
console.log(`Current: ${status.batteryCapacity}% | Threshold: ${this.config.thresholds.battery}%`);
await this.snmp.initiateShutdown('Battery capacity below threshold');
await this.initiateShutdown('Battery capacity below threshold');
return;
}
@ -277,10 +281,90 @@ export class NupstDaemon {
if (status.batteryRuntime < this.config.thresholds.runtime) {
console.log('⚠️ WARNING: Runtime below threshold');
console.log(`Current: ${status.batteryRuntime} min | Threshold: ${this.config.thresholds.runtime} min`);
await this.snmp.initiateShutdown('Runtime below threshold');
await this.initiateShutdown('Runtime below threshold');
return;
}
}
/**
* Initiate system shutdown with UPS monitoring during shutdown
* @param reason Reason for shutdown
*/
public async initiateShutdown(reason: string): Promise<void> {
console.log(`Initiating system shutdown due to: ${reason}`);
// Set a longer delay for shutdown to allow VMs and services to close
const shutdownDelayMinutes = 5;
try {
// Execute shutdown command with delay to allow for VM graceful shutdown
const { stdout } = await execAsync(`shutdown -h +${shutdownDelayMinutes} "UPS battery critical, shutting down in ${shutdownDelayMinutes} minutes"`);
console.log('Shutdown initiated:', stdout);
console.log(`Allowing ${shutdownDelayMinutes} minutes for VMs to shut down safely`);
// Monitor UPS during shutdown and force immediate shutdown if battery gets too low
console.log('Monitoring UPS during shutdown process...');
await this.monitorDuringShutdown();
} catch (error) {
console.error('Failed to initiate shutdown:', error);
// Try a different method if first one fails
try {
console.log('Trying alternative shutdown method...');
await execAsync('poweroff --force');
} catch (innerError) {
console.error('All shutdown methods failed:', innerError);
}
}
}
/**
* Monitor UPS during system shutdown
* Force immediate shutdown if battery gets critically low
*/
private async monitorDuringShutdown(): Promise<void> {
const EMERGENCY_RUNTIME_THRESHOLD = 5; // 5 minutes remaining is critical
const CHECK_INTERVAL = 30000; // Check every 30 seconds during shutdown
const MAX_MONITORING_TIME = 5 * 60 * 1000; // Max 5 minutes of monitoring
const startTime = Date.now();
console.log(`Emergency shutdown threshold: ${EMERGENCY_RUNTIME_THRESHOLD} minutes remaining battery runtime`);
// Continue monitoring until max monitoring time is reached
while (Date.now() - startTime < MAX_MONITORING_TIME) {
try {
console.log('Checking UPS status during shutdown...');
const status = await this.snmp.getUpsStatus(this.config.snmp);
console.log(`Current battery: ${status.batteryCapacity}%, Runtime: ${status.batteryRuntime} minutes`);
// If battery runtime gets critically low, force immediate shutdown
if (status.batteryRuntime < EMERGENCY_RUNTIME_THRESHOLD) {
console.log('┌─ EMERGENCY SHUTDOWN ─────────────────────┐');
console.log(`│ Battery runtime critically low: ${status.batteryRuntime} minutes`);
console.log('│ Forcing immediate shutdown!');
console.log('└──────────────────────────────────────────┘');
try {
await execAsync('shutdown -h now "EMERGENCY: UPS battery critically low, shutting down NOW"');
} catch (error) {
console.error('Emergency shutdown failed, trying alternative method...');
await execAsync('poweroff --force');
}
// Stop monitoring after initiating emergency shutdown
return;
}
// Wait before checking again
await this.sleep(CHECK_INTERVAL);
} catch (error) {
console.error('Error monitoring UPS during shutdown:', error);
await this.sleep(CHECK_INTERVAL);
}
}
console.log('UPS monitoring during shutdown completed');
}
/**
* Sleep for the specified milliseconds

View File

@ -1,13 +1,9 @@
import { exec } from 'child_process';
import { promisify } from 'util';
import * as dgram from 'dgram';
import type { IOidSet, ISnmpConfig, TUpsModel, IUpsStatus } from './types.js';
import { UpsOidSets } from './oid-sets.js';
import { SnmpPacketCreator } from './packet-creator.js';
import { SnmpPacketParser } from './packet-parser.js';
const execAsync = promisify(exec);
/**
* Class for SNMP communication with UPS devices
* Main entry point for SNMP functionality
@ -507,26 +503,5 @@ export class NupstSnmp {
});
}
/**
* Initiate system shutdown
* @param reason Reason for shutdown
*/
public async initiateShutdown(reason: string): Promise<void> {
console.log(`Initiating system shutdown due to: ${reason}`);
try {
// Execute shutdown command with 5 minute delay to allow for VM graceful shutdown
const { stdout } = await execAsync('shutdown -h +5 "UPS battery critical, shutting down in 5 minutes"');
console.log('Shutdown initiated:', stdout);
console.log('Allowing 5 minutes for VMs to shut down safely');
} catch (error) {
console.error('Failed to initiate shutdown:', error);
// Try a different method if first one fails
try {
console.log('Trying alternative shutdown method...');
await execAsync('poweroff --force');
} catch (innerError) {
console.error('All shutdown methods failed:', innerError);
}
}
}
// initiateShutdown method has been moved to the NupstDaemon class
}