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
|
# 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)
|
## 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.
|
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
|
# Make launcher script executable
|
||||||
chmod +x "$SCRIPT_DIR/bin/nupst"
|
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_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"
|
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
|
# Remove existing node_modules directory and package files
|
||||||
echo "Cleaning up existing installation..."
|
echo "Cleaning up existing installation..."
|
||||||
@ -278,12 +288,11 @@ echo '{
|
|||||||
|
|
||||||
# Install ONLY net-snmp
|
# Install ONLY net-snmp
|
||||||
echo "Installing ONLY net-snmp dependency..."
|
echo "Installing ONLY net-snmp dependency..."
|
||||||
echo "Using Node.js binary from: $NODE_BIN_DIR"
|
echo "Node version: $("$NODE_BIN" --version)"
|
||||||
echo "Node version: $(node --version)"
|
echo "NPM version: $("$NPM_BIN" --version)"
|
||||||
echo "NPM version: $(npm --version)"
|
|
||||||
|
|
||||||
# Use clean install to ensure only net-snmp is installed
|
# Use absolute paths to binaries to ensure we use our Node.js
|
||||||
npm --prefix "$SCRIPT_DIR" install --no-audit --no-fund
|
"$NPM_BIN" --prefix "$SCRIPT_DIR" install --no-audit --no-fund
|
||||||
|
|
||||||
INSTALL_STATUS=$?
|
INSTALL_STATUS=$?
|
||||||
if [ $INSTALL_STATUS -ne 0 ]; then
|
if [ $INSTALL_STATUS -ne 0 ]; then
|
||||||
@ -301,8 +310,7 @@ else
|
|||||||
rm -f "$SCRIPT_DIR/package.json.bak"
|
rm -f "$SCRIPT_DIR/package.json.bak"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restore the original PATH
|
# We didn't modify PATH, so no need to restore it
|
||||||
export PATH="$OLD_PATH"
|
|
||||||
|
|
||||||
echo "NUPST setup completed successfully."
|
echo "NUPST setup completed successfully."
|
||||||
echo "You can now run NUPST using: $SCRIPT_DIR/bin/nupst"
|
echo "You can now run NUPST using: $SCRIPT_DIR/bin/nupst"
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/nupst',
|
name: '@serve.zone/nupst',
|
||||||
version: '2.6.4',
|
version: '2.6.5',
|
||||||
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
|
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 fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { exec } from 'child_process';
|
import { exec, execFile } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { NupstSnmp } from './snmp/manager.js';
|
import { NupstSnmp } from './snmp/manager.js';
|
||||||
import type { ISnmpConfig } from './snmp/types.js';
|
import type { ISnmpConfig } from './snmp/types.js';
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration interface for the daemon
|
* Configuration interface for the daemon
|
||||||
@ -298,23 +299,101 @@ export class NupstDaemon {
|
|||||||
const shutdownDelayMinutes = 5;
|
const shutdownDelayMinutes = 5;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Execute shutdown command with delay to allow for VM graceful shutdown
|
// Find shutdown command in common system paths
|
||||||
const { stdout } = await execAsync(`shutdown -h +${shutdownDelayMinutes} "UPS battery critical, shutting down in ${shutdownDelayMinutes} minutes"`);
|
const shutdownPaths = [
|
||||||
console.log('Shutdown initiated:', stdout);
|
'/sbin/shutdown',
|
||||||
console.log(`Allowing ${shutdownDelayMinutes} minutes for VMs to shut down safely`);
|
'/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
|
// Monitor UPS during shutdown and force immediate shutdown if battery gets too low
|
||||||
console.log('Monitoring UPS during shutdown process...');
|
console.log('Monitoring UPS during shutdown process...');
|
||||||
await this.monitorDuringShutdown();
|
await this.monitorDuringShutdown();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to initiate shutdown:', error);
|
console.error('Failed to initiate shutdown:', error);
|
||||||
// Try a different method if first one fails
|
|
||||||
try {
|
// Try alternative shutdown methods
|
||||||
console.log('Trying alternative shutdown method...');
|
const alternatives = [
|
||||||
await execAsync('poweroff --force');
|
{ cmd: 'poweroff', args: ['--force'] },
|
||||||
} catch (innerError) {
|
{ cmd: 'halt', args: ['-p'] },
|
||||||
console.error('All shutdown methods failed:', innerError);
|
{ 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('└──────────────────────────────────────────┘');
|
console.log('└──────────────────────────────────────────┘');
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
console.error('Emergency shutdown failed, trying alternative method...');
|
console.error('Emergency shutdown failed, trying alternative methods...');
|
||||||
await execAsync('poweroff --force');
|
|
||||||
|
// 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
|
// Stop monitoring after initiating emergency shutdown
|
||||||
|
Loading…
x
Reference in New Issue
Block a user