UPS 向局域网设备群发关机信号(伪)

发布于 2024-09-24  798 次阅读


背景

内网有好几台计算机和 NAS,但是家里的 UPS 只能向一台设备发送关机信号,不能向内网所有设备发送(没钱买智能 UPS)。但是确实有这个需求,忽然想到既然不能在发送端解决,但是可以在接收端解决啊,毕竟接收端都是智能设备,不是 Windows 就是 Linux,完全可以通过小脚本实现这个功能。

原理

首先将 UPS 连接到监控机(建议 NAS 或者是相对重要的机器),然后在内网其余设备各挂一个小脚本,小脚本的作用是监测监控机状态,如果监控机离线,则自动关机。

实现

这里我们通过 ping 来实现监控机状态的监测,以下提供 GO 和 Java 的实例,基本能覆盖 90% 的智能设备。

GO(Windows)

package main
import (
	"fmt"
	"os/exec"
	"time"
)
func ping(ip string) bool {
	cmd := exec.Command("ping", "-n", "1", ip)
	err := cmd.Run()
	return err == nil
}
func shutdown() {
	cmd := exec.Command("shutdown", "/s", "/t", "0")
	err := cmd.Run()
	if err != nil {
		fmt.Println("Failed to shutdown the system:", err)
	} else {
		fmt.Println("System is shutting down...")
	}
}
func main() {
	ip := "192.168.1.1"     // 192.168.1.1 修改为监控机的实际 IP
	var timeVar int
	for {
		if ping(ip) {
			timeVar = 0
			fmt.Println("Ping successful.")
		} else {
			timeVar++
			fmt.Printf("Ping failed. time = %d\n", timeVar)
		}
		if timeVar >= 3 {     // 连续 3 次 ping 失败,自动关机,根据自己需要修改
			shutdown()
			break
		}
		time.Sleep(10 * time.Second)     // 10 秒 ping 一次,根据自己需要修改
	}
}

GO(Linux)

// Linux 下关机需要提权
package main
import (
	"fmt"
	"os/exec"
	"time"
)
func ping(ip string) bool {
	cmd := exec.Command("ping", "-c", "1", ip)
	err := cmd.Run()
	return err == nil
}
func shutdown() {
	cmd := exec.Command("shutdown", "-h", "now")     // 其实就是关机命令和 Windows 不同
	err := cmd.Run()
	if err != nil {
		fmt.Printf("Failed to shutdown: %v\n", err)
	} else {
		fmt.Println("Shutting down the system...")
	}
}
func main() {
	ip := "192.168.1.1"     // 192.168.1.1 修改为监控机的实际 IP
	var timeVar int
	for {
		if ping(ip) {
			timeVar = 0
			fmt.Println("Ping successful")
		} else {
			timeVar++
			fmt.Printf("Ping failed. time = %d\n", timeVar)
		}
		if timeVar >= 3 {     // 连续 3 次 ping 失败,自动关机,根据自己需要修改
			shutdown()
			break
		}
		time.Sleep(10 * time.Second)     // 10 秒 ping 一次,根据自己需要修改
	}
}

Java

// Linux 下关机需要提权
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;
public class PingTask {
    private static int time = 0;
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask pingTask = new TimerTask() {
            @Override
            public void run() {
                try {
                    Process process = Runtime.getRuntime().exec("ping -c 1 192.168.1.1");     // 192.168.1.1 修改为监控机的实际 IP
                    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                    String line;
                    boolean isPingSuccess = false;
                    while ((line = reader.readLine()) != null) {
                        if (line.contains("1 received")) {     // 1 received 为 Linux 下的 ping 返回内容,Windows 做对应更换就行
                            isPingSuccess = true;
                            break;
                        }
                    }
                    if (isPingSuccess) {
                        time = 0;
                    } else {
                        time++;
                    }
                    System.out.println("Current time: " + time);
                    if (time >= 3) {     // 连续 3 次 ping 失败,自动关机,根据自己需要修改
                        System.out.println("Shutting down...");
                        Runtime.getRuntime().exec("systemctl poweroff");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        timer.schedule(pingTask, 0, 10000);     // 10 秒(10000 毫秒) ping 一次,根据自己需要修改
    }
}

使用脚本

GO 的话可以直接编译成二进制使用;Java 的话需要有 Java 环境,搭建完成后可直接运行 .jar 文件,测试完没问题后加个开机自启就行。