fix(daemon, setup): Improve shutdown command detection and fallback logic; update setup script to use absolute Node/npm paths
This commit is contained in:
parent
83ba9c2611
commit
be6a7314c3
@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-03-26 - 2.6.5 - fix(daemon, setup)
|
||||
Improve shutdown command detection and fallback logic; update setup script to use absolute Node/npm paths
|
||||
|
||||
- Use execFileAsync to execute shutdown commands reliably
|
||||
- Add multiple fallback alternatives for shutdown and emergency shutdown handling
|
||||
- Update setup.sh to log the Node and NPM versions using absolute paths without modifying PATH
|
||||
|
||||
## 2025-03-26 - 2.6.4 - fix(setup)
|
||||
Improve installation process in setup script by cleaning up package files and ensuring a minimal net-snmp dependency installation.
|
||||
|
||||
|
26
setup.sh
26
setup.sh
@ -239,10 +239,20 @@ echo "dist_ts directory successfully downloaded from npm registry."
|
||||
# Make launcher script executable
|
||||
chmod +x "$SCRIPT_DIR/bin/nupst"
|
||||
|
||||
# Add our Node.js bin directory to the PATH temporarily
|
||||
# Set path to our Node.js binaries
|
||||
NODE_BIN_DIR="$SCRIPT_DIR/vendor/$NODE_DIR/bin"
|
||||
NODE_BIN="$NODE_BIN_DIR/node"
|
||||
NPM_BIN="$NODE_BIN_DIR/npm"
|
||||
|
||||
# Ensure we have executable permissions
|
||||
chmod +x "$NODE_BIN" "$NPM_BIN"
|
||||
|
||||
# Save original PATH but don't modify it
|
||||
# We'll use the full paths to binaries instead
|
||||
OLD_PATH="$PATH"
|
||||
export PATH="$NODE_BIN_DIR:$PATH"
|
||||
|
||||
echo "Using Node binary: $NODE_BIN"
|
||||
echo "Using NPM binary: $NPM_BIN"
|
||||
|
||||
# Remove existing node_modules directory and package files
|
||||
echo "Cleaning up existing installation..."
|
||||
@ -278,12 +288,11 @@ echo '{
|
||||
|
||||
# Install ONLY net-snmp
|
||||
echo "Installing ONLY net-snmp dependency..."
|
||||
echo "Using Node.js binary from: $NODE_BIN_DIR"
|
||||
echo "Node version: $(node --version)"
|
||||
echo "NPM version: $(npm --version)"
|
||||
echo "Node version: $("$NODE_BIN" --version)"
|
||||
echo "NPM version: $("$NPM_BIN" --version)"
|
||||
|
||||
# Use clean install to ensure only net-snmp is installed
|
||||
npm --prefix "$SCRIPT_DIR" install --no-audit --no-fund
|
||||
# Use absolute paths to binaries to ensure we use our Node.js
|
||||
"$NPM_BIN" --prefix "$SCRIPT_DIR" install --no-audit --no-fund
|
||||
|
||||
INSTALL_STATUS=$?
|
||||
if [ $INSTALL_STATUS -ne 0 ]; then
|
||||
@ -301,8 +310,7 @@ else
|
||||
rm -f "$SCRIPT_DIR/package.json.bak"
|
||||
fi
|
||||
|
||||
# Restore the original PATH
|
||||
export PATH="$OLD_PATH"
|
||||
# We didn't modify PATH, so no need to restore it
|
||||
|
||||
echo "NUPST setup completed successfully."
|
||||
echo "You can now run NUPST using: $SCRIPT_DIR/bin/nupst"
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/nupst',
|
||||
version: '2.6.4',
|
||||
version: '2.6.5',
|
||||
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
|
||||
}
|
||||
|
176
ts/daemon.ts
176
ts/daemon.ts
@ -1,11 +1,12 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { exec } from 'child_process';
|
||||
import { exec, execFile } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import { NupstSnmp } from './snmp/manager.js';
|
||||
import type { ISnmpConfig } from './snmp/types.js';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
/**
|
||||
* Configuration interface for the daemon
|
||||
@ -298,23 +299,101 @@ export class NupstDaemon {
|
||||
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`);
|
||||
// Find shutdown command in common system paths
|
||||
const shutdownPaths = [
|
||||
'/sbin/shutdown',
|
||||
'/usr/sbin/shutdown',
|
||||
'/bin/shutdown',
|
||||
'/usr/bin/shutdown'
|
||||
];
|
||||
|
||||
let shutdownCmd = '';
|
||||
for (const path of shutdownPaths) {
|
||||
try {
|
||||
if (fs.existsSync(path)) {
|
||||
shutdownCmd = path;
|
||||
console.log(`Found shutdown command at: ${shutdownCmd}`);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// Continue checking other paths
|
||||
}
|
||||
}
|
||||
|
||||
if (shutdownCmd) {
|
||||
// Execute shutdown command with delay to allow for VM graceful shutdown
|
||||
console.log(`Executing: ${shutdownCmd} -h +${shutdownDelayMinutes} "UPS battery critical..."`);
|
||||
const { stdout } = await execFileAsync(shutdownCmd, [
|
||||
'-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`);
|
||||
} else {
|
||||
// Try using the PATH to find shutdown
|
||||
try {
|
||||
console.log('Shutdown command not found in common paths, trying via PATH...');
|
||||
const { stdout } = await execAsync(`shutdown -h +${shutdownDelayMinutes} "UPS battery critical, shutting down in ${shutdownDelayMinutes} minutes"`, {
|
||||
env: process.env // Pass the current environment
|
||||
});
|
||||
console.log('Shutdown initiated:', stdout);
|
||||
} catch (e) {
|
||||
throw new Error(`Shutdown command not found: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Try alternative shutdown methods
|
||||
const alternatives = [
|
||||
{ cmd: 'poweroff', args: ['--force'] },
|
||||
{ cmd: 'halt', args: ['-p'] },
|
||||
{ cmd: 'systemctl', args: ['poweroff'] },
|
||||
{ cmd: 'reboot', args: ['-p'] } // Some systems allow reboot -p for power off
|
||||
];
|
||||
|
||||
for (const alt of alternatives) {
|
||||
try {
|
||||
// First check if command exists in common system paths
|
||||
const paths = [
|
||||
`/sbin/${alt.cmd}`,
|
||||
`/usr/sbin/${alt.cmd}`,
|
||||
`/bin/${alt.cmd}`,
|
||||
`/usr/bin/${alt.cmd}`
|
||||
];
|
||||
|
||||
let cmdPath = '';
|
||||
for (const path of paths) {
|
||||
if (fs.existsSync(path)) {
|
||||
cmdPath = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdPath) {
|
||||
console.log(`Trying alternative shutdown method: ${cmdPath} ${alt.args.join(' ')}`);
|
||||
await execFileAsync(cmdPath, alt.args);
|
||||
return; // Exit if successful
|
||||
} else {
|
||||
// Try using PATH environment
|
||||
console.log(`Trying alternative via PATH: ${alt.cmd} ${alt.args.join(' ')}`);
|
||||
await execAsync(`${alt.cmd} ${alt.args.join(' ')}`, {
|
||||
env: process.env // Pass the current environment
|
||||
});
|
||||
return; // Exit if successful
|
||||
}
|
||||
} catch (altError) {
|
||||
console.error(`Alternative method ${alt.cmd} failed:`, altError);
|
||||
// Continue to next method
|
||||
}
|
||||
}
|
||||
|
||||
console.error('All shutdown methods failed');
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,10 +425,79 @@ export class NupstDaemon {
|
||||
console.log('└──────────────────────────────────────────┘');
|
||||
|
||||
try {
|
||||
await execAsync('shutdown -h now "EMERGENCY: UPS battery critically low, shutting down NOW"');
|
||||
// Find shutdown command in common system paths
|
||||
const shutdownPaths = [
|
||||
'/sbin/shutdown',
|
||||
'/usr/sbin/shutdown',
|
||||
'/bin/shutdown',
|
||||
'/usr/bin/shutdown'
|
||||
];
|
||||
|
||||
let shutdownCmd = '';
|
||||
for (const path of shutdownPaths) {
|
||||
if (fs.existsSync(path)) {
|
||||
shutdownCmd = path;
|
||||
console.log(`Found shutdown command at: ${shutdownCmd}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shutdownCmd) {
|
||||
console.log(`Executing emergency shutdown: ${shutdownCmd} -h now`);
|
||||
await execFileAsync(shutdownCmd, ['-h', 'now', 'EMERGENCY: UPS battery critically low, shutting down NOW']);
|
||||
} else {
|
||||
// Try using the PATH to find shutdown
|
||||
console.log('Shutdown command not found in common paths, trying via PATH...');
|
||||
await execAsync('shutdown -h now "EMERGENCY: UPS battery critically low, shutting down NOW"', {
|
||||
env: process.env // Pass the current environment
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Emergency shutdown failed, trying alternative method...');
|
||||
await execAsync('poweroff --force');
|
||||
console.error('Emergency shutdown failed, trying alternative methods...');
|
||||
|
||||
// Try alternative shutdown methods in sequence
|
||||
const alternatives = [
|
||||
{ cmd: 'poweroff', args: ['--force'] },
|
||||
{ cmd: 'halt', args: ['-p'] },
|
||||
{ cmd: 'systemctl', args: ['poweroff'] }
|
||||
];
|
||||
|
||||
for (const alt of alternatives) {
|
||||
try {
|
||||
// Check common paths
|
||||
const paths = [
|
||||
`/sbin/${alt.cmd}`,
|
||||
`/usr/sbin/${alt.cmd}`,
|
||||
`/bin/${alt.cmd}`,
|
||||
`/usr/bin/${alt.cmd}`
|
||||
];
|
||||
|
||||
let cmdPath = '';
|
||||
for (const path of paths) {
|
||||
if (fs.existsSync(path)) {
|
||||
cmdPath = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdPath) {
|
||||
console.log(`Emergency: using ${cmdPath} ${alt.args.join(' ')}`);
|
||||
await execFileAsync(cmdPath, alt.args);
|
||||
return; // Exit if successful
|
||||
} else {
|
||||
// Try using PATH
|
||||
console.log(`Emergency: trying ${alt.cmd} via PATH`);
|
||||
await execAsync(`${alt.cmd} ${alt.args.join(' ')}`, {
|
||||
env: process.env
|
||||
});
|
||||
return; // Exit if successful
|
||||
}
|
||||
} catch (altError) {
|
||||
// Continue to next method
|
||||
}
|
||||
}
|
||||
|
||||
console.error('All emergency shutdown methods failed');
|
||||
}
|
||||
|
||||
// Stop monitoring after initiating emergency shutdown
|
||||
|
Loading…
x
Reference in New Issue
Block a user