給公司的伺服器做一個“核按鈕”

Sneezry發表於2017-08-05

起因是這樣的,某天刷微博看到果殼網轉發了一個很有意思的微博,有網友製作了一個連線到電腦上的按鈕,按下後自動執行rm -rf /*

enter image description here

原po沒有在微博上解釋製作過程和執行原理,不過看著這麼有趣的小玩意,不折騰會死星人表示必須自己實現一個!下面就是我製作的按鈕。

enter image description here

機械開關

這個按鈕在網上很容易買得到,關鍵字搜尋“急停按鈕”就可以。我買的這個不超過20塊錢。這個按鈕是一個機械開關,一共有4個觸點,兩個一組,共兩組,一組為常開開關,一組為常閉開關,可以根據實際情況選擇接線。我選擇使用的是常閉那一組。

開發板

由於需要把機械開關的物理訊號轉換為數字訊號傳給電腦,要使用開發板進行轉換。由於這個功能非常初級,所有支援GPIO擴充套件的開發板都可以。我手上有NodeMCU的ESP8266模組就直接用了,這個模組本身體積小,能夠塞進按鈕下面的倉盒內方便封裝。如果你手上沒有開發板,可以去網上買一個,價格也不貴,NodeMCU ESP8266的價格也不超過20塊。

enter image description here

電路圖

電路圖也簡單得要死,開關斷開時GPIO連到GND,輸入低電平,開關閉合時GPIO輸入高電平。如果使用NodeMCU ESP8266,VDD使用3.3v,因為這個板子沒有5v,其他板子有5v的使用5v就可以。R1選擇一個比較大的電阻,避免短路,通常10k左右就可以。沒有10k的用個差不多的就行,我手上沒有10k的,我選用了一個6.8k的電阻。R2選擇小一些的電阻,比如100歐姆的。如果使用NodeMCU ESP8266,GPIO不要使用1、2、15,boot的時候會卡住。我使用的是4,對應的Pin是D2。另外請忽略白板上面的氣泡,懶癌患者表示不想處理……

enter image description here

開發板程式碼

本來當時我選擇ESP8266的原因還有一點就是可以連線WiFi。我最開始的想法是將開關訊號發到網上,電腦再從網際網路拉取資料。不過首先因為ESP8266資源有限,連線HTTPS有很大問題。其次這樣會產生明顯的延遲,同時還要維護一個網際網路服務。所以最後偷懶我直接用串列埠傳資料,所以這也不再是物聯網示例了。

那麼程式碼邏輯就是定義Pin模式為INPUT,迴圈讀取GPIO電平狀態,將結果輸出到串列埠。下面是Arduino原始碼:

void setup() {
    Serial.begin(115200);
    pinMode(D2, INPUT);
}

void loop() {
    if (digitalRead(D2) == HIGH)
    {
        Serial.write(1);
    }
    else
    {
        Serial.write(0);
    }
    delay(200);
}

有關Arduino IDE、如何在Arduino IDE配置ESP8266開發板,可以參見https://github.com/esp8266/Arduino

電腦端指令碼

電腦端指令碼首先要能讀取到串列埠資料。指令碼我使用了比較熟悉的Node編寫,Node讀取串列埠資料可以使用serialport這個包。讀取出來的資料是Buffer型別的,需要先轉換為整型。

最後一步就是根據串列埠資料執行相應命令,比如rm -rf /*。Node有一個很有意思的包叫robot,它可以模擬鍵盤和滑鼠等行為。我們可以使用這個包來模擬輸入命令並執行。

下面是完整的電腦端指令碼,同樣邏輯非常簡單:

var SerialPort = require('serialport');
var robot = require("robotjs");

var port = new SerialPort('COM16', { autoOpen: false, baudRate: 115200 });
var status = 0;
var lastStatus = 0;

port.open(function (err) {
    if (err) {
        return console.log('Error opening port: ', err.message);
    }
});

port.on('data', function (data) {
    lastStatus = status;
    status = data.readUIntBE(0, 1);
    if (status !== lastStatus && status === 1) {
        robot.typeString('rm -rf /*');
        robot.keyTap('enter');
    }
});

port.on('readable', function () {
    port.read();
});

指令碼里建立SerialPort例項時傳入的第一個引數是串列埠名,Windows下是COM<n>,n的具體數值要到裝置管理器中確認。Linux和macOS下第一個引數是/dev/tty*,具體串列埠路徑可以使用ls /dev/tty*檢視。

如果你使用的是Windows,同時希望執行Linux命令,比如rm,可以通過安裝Git BASH實現。如果你使用的是Windows 10,則可以直接安裝Windows Linux Subsystem。

演示

最終我所執行的不是rm -rf /*,因為視訊中的不是虛擬機器。視訊中實際的操作是移除git repo本地除master外的其它分支,這是一個非常有用的操作,但同時也是一項非常危險的操作。

視訊地址:http://v.youku.com/v_show/id_XMjk0NjA4OTI5Mg==.html

相關文章