Sunday, April 11, 2021

使用 minicom 及 expect 自動化控制 TTY 設備

簡介
傳統來講,使用 expect script 可以幫助我們自動化一些繁瑣的工作,例如以互動方式登入 SSH,或者進行自動化軟件安裝及部署。那麼,如果我們想要用 expect 去控制 TTY 裝置(例如 UART to USB 開發板、數據機等等),又如何操作呢?以下介紹兩種方法,希望可以幫助大家,輕鬆透過 minicom,進行簡單的自動化。

有人會問,為什麼一定要用 expect 呢?我能先自行 echo command 去 /dev/ttyUSB0,然後再用一個 while loop 去觀察 /dev/ttyUSB0 的輸出,不就可以了嗎?但我發現,這種方法,將會製造一個「空窗期」:如果 tty 硬件十分快,它就有可能在 echo command 執行完成之後,以及在 while loop 開始執行之前,已經將結果輸出。這樣的話,之後才開始的 while loop,就會一直讀不到有 output,然後跌入 infinite loop 之中。理論上,我們可以透過 multi-thread,邊讀 TTY 內容,邊寫 TTY command 去解決這件事,但我覺得,這樣又實在太過小題大做。所以,使用 expect 就是最簡單的方法。

本來,使用 expect 去做 interactive scripting 是一件非常簡單的事情, 但奈何 expect 的文檔十分殘缺,而且上述問題又實在太多人忽略掉,單靠搜查文檔,其實很難去理解這件事。所以,我決定在此記下,希望對其他人也有所幫助。

---

方法一:使用 expect script 直接操控 minicom
這種方法最為直覺,你只需要使用 expect,就可以直接與 minicom 連接:

#!/usr/bin/expect
set timeout 10
exec bash -c "echo Hello World"
spawn minicom -D /dev/ttyUSB0 -b 9600 -C /tmp/minicom.log
send "ps"
expect "#"
send_to_user "completed ps command to ttyUSB0"

使用這種方法,expect 會持續監察 spawn 出來的 minicom 中的 stdout,並且會把 send 的內容傳送回 minicom 的 stdin 中,所以,它就能與 minicom 直接進行互動。然後,使用 exec 加上 bash,就能執行各種各樣非 expect 的 commands。這種情況,minicom 負責所有與 tty 裝置的內容收發。 

不過,如果你打算運行很多非 expect 的 commands 的話,這種方法就會變得很慢,並且難以理解。因為每次運行 bash commands,都要先 exec bash,然後再在 bash 中運行 command。

---

方法二:使用 bash 開啟 minicom,並透過查看 minicom 的 log 與 tty 裝置互動
這種方式較為間接,使用 bash 作為主力,expect 則成為其中一個 bash command:

#!/bin/bash
echo "Hello World"
minicom -D /dev/ttyUSB0 -b 9600 -C /tmp/minicom.log" &
PID_MINICOM=$#
expect -c '
    timeout 10
    spawn tail -Fn0 /tmp/minicom.log
    exec bash -c "echo ps > /dev/ttyUSB0"
    expect "#"
    send_to_user "completed ps command to ttyUSB0"
'
kill $PID_MINICOM

以上的代碼,bash 會先在背景開啟 minicom。接著,bash 會開啟一個 expect process,並且持續監察 minicom 的 log 輸出。如果 expect 想要與 tty 互動,由於 tail 的 stdin 並沒有任何作用,它將不能使用 send。所以,你需要把內容自行用 exec bash,再用 echo 把  command 內容 redirect (>) 進 /dev/ttyUSB0。

這種做法,雖然看起來比較複雜,但如果你打算使用 bash 作為為最主要的語言,它所產生的系統開銷會少很多(因為不需要頻繁開新 bash)。但反的來說,如果你的 script 打算主力運行 expect,並需要與 TTY 有很多互動的話,這種方法就比較緩慢:因為每一次 send TTY command,它也需要 exec 一個新的 bash。

---

小結
使用 expect 能使用非常短的語法,解決與 TTY device 互動所產生的各種問題。是次我分別介紹了以 expect 為主,以及用 bash 為主的兩種 expect script 寫法,以滿足不同情況的需要。

Sunday, April 4, 2021

安裝 WSL2 GUI

引言
Ubuntu 近年經過不斷的改良,已經十分好用。不過,由於 Linux 始終缺乏許多常用的辦公軟件(例如 Office、IxChariot、Zoom 等等),要把它當成取代 Windows 的日常作業系統,的確有點困難。就算 Linux 上可以用 Wine 去執行 Win32 API,反向支援 Windows Apps,但結果總是強差人意。不過,由於 Windows 實在缺少了很多 POSIX-compliant API、shell 支援、以及 automake 等開發工具,所以,用它來做日常開發,又總是令人覺得綁手綁腳。

有見及此,Microsoft 在 2019 年決定推出 Windows Subsystem for Linux (WSL)。這個輕量版的 Translation Layer(註:WSL2 為 Hyper-V VM),既能提供接近原生 Linux 系統的效能,又能幫助用家繼續沿用 Windows 作為主要 OS。不過唯一缺點是,Microsoft 直至 2021 年 4 月,仍然未推出對 WSL 的官方 GUI 支援。 

這次實作,我們決定自行用 VcXsrv 加上輕量版的 Xfce 介面,使 Windows Host 也能輸出 Ubuntu 的 GUI。事實上,整個安裝過程,也只是涉及數行 commands,難度並不高,特別適合 Linux 新手操作。

---

安裝步驟
假設你已經成功安裝 WSL2 (Ubuntu 20.04),你只需在 WSL2 的 Terminal 中進行以下步驟:

1. 執行以下命令,安裝 xfce4 介面及 GNOME 圖示:

sudo apt update
sudo apt upgrade
sudo apt install tasksel
sudo taskel install xubuntu-desktop
sudo apt install gtk2-engines
sudo apt install gnome-icon-theme-full

2. 在 ~/.bashrc 的最尾,加上以下內容:

export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
export LIBGL_ALWAYS_INDIRECT=1

3. 在 Windows Host 中安裝 VcXsrv https://sourceforge.net/projects/vcxsrv,並注意:

  • 安裝最新穩定版  vcxsrv-64.1.20.9.0
  • 安裝過程中,記得將防火牆 Allow

4. 安裝完成後,重新開啟 WSL2 Terminal,並在 Windows 中開啟 XLaunch。
(記得在 Extra Settings 中,需剔選 Disable Access Control)

5. 在 WSL2 Terminal 中安裝 Meld,並測試 Meld GUI 是否能投射到 VcXsrv 中:

sudo apt install meld
meld ./

---

演示圖片


---

小結
使用 WSL2,能在 Windows OS 上,以接近原生的效能,運行 Ubuntu 的 GUI Apps。
本文使用 X Server 及 Display Redirection,以非官方的做法,實現 GUI 的支援。

---

參考:

https://medium.com/@japheth.yates/the-complete-wsl2-gui-setup-2582828f4577


Wednesday, March 31, 2021

安裝 Gerrit Missed Event Playback

引言
不少公司都會採用 Gerrit 管理代碼,並且結合 Jenkins 作持續交付之用。例如,在 Jenkins 與 Gerrit 之間,其實可以透過 Gerrit Trigger Plugin 互連:當每次有新的 Code Review Event,Jenkins 就可以對新的 Code Patch 進行編譯、測試、然後 Tag Verified。這樣,Jenkins 就可以用作檢查代碼的穩定性,以及協助開發者快速找出有問題的代碼。

不過,因為不同原因(例如關機維護),有時候 Jenkins 與 Gerrit 會需要斷開。當兩者斷開之後,若果 Gerrit 出現的新 events,Jenkins 就會出現 event miss。即使 Jenkins 重啟之後,所有 missed events 在預設的狀態下,並不會自動同步。是次實作,我們會透過在 Gerrit 上安裝 Events-Log Plugin,使 Jenkins Gerrit Trigger 能自動同步(並執行)所有 missed events。

---

Step-by-Step 步驟

A. 手動編譯 events-log 插件

1. 從 Google Source 下載 events-log 的 git:
git clone https://gerrit.googlesource.com/plugins/events-log

2. 從 git 中 checkout 最新的穩定版本 (for example 3.1):

cd events-log/
git checkout stable-3.1

3. 安裝編譯需要的 dependencies:

sudo apt install openjdk-11-jdk
sudo apt install npm
npm install -g @bazel/bazelisk

4. 編譯 events-log:

bazel build events-log

5. 查看 build 輸出:

ls bazel-bin/events-log.jar

B. 打開 Gerrit 的 Remote Plugin Admin

1. 開啟  <gerrit-install-path>/etc/gerrit.conf 並加上以下兩行:

[plugins]
        allowRemoteAdmin = true

2. 重新啟動 Gerrit:

sudo /etc/init.d/gerrit restart

3. 登入 Gerrit 的 Web UI 並查看 Plugin Manager 是否存在:

http://<your gerrit url>/plugins/plugin-manager/static/index.html
C. 用 SSH 安裝插件

1. 安裝 events-log 插件到 Gerrit:

cd bazel-bin/
ssh -p 29418 localhost gerrit plugin install -n events-log.jar events-log.jar

2. 重新啟動 Gerrit Server:

sudo /etc/init.d/gerrit restart

3. 開啟 Gerrit Web UI,查看 enabled plugins 中是否含有 events-log:

http://<your gerrit server url>/admin/plugins
D. 設置 Jenkins Gerrit Trigger

1. 開啟 Jenkins 上的 Gerrit Trigger Server Configuration:

Manage Jenkins -> Gerrit Trigger -> Add new server / Server Settings 

2. 按 "Advanced...",找到 "REST API" 一項,並填上以下資料:

[v] 剔選 "Use REST API"
Gerrit HTTP Username:  <your jenkins username in Gerrit>
Gerrit HTTP Password:  <your jenkins HTTP Credentials>
[v] 剔選 "Enable Code-Review"
[v] 剔選 "Enable Verified"

3. 儲存,並重開 Gerrit 與 Jenkins 的連線。

---

小結
由於 event-log 並沒有已經預先編譯好的 jar 執行檔,所以是次實作,我們自行編譯它,並開啟 Gerrit 的插件安裝工具,最後把它順利安裝到 Gerrit 中。安裝完成後,Jenkins 內的 Gerrit Trigger Plugin 將能自動同步 event history,以減少不必要的手動 trigger 或 event miss。

---

參考:

[1] - https://plugins.jenkins.io/gerrit-trigger/ 

[2] - https://docs.bazel.build/versions/master/install-bazelisk.html

[3] - https://gerrit-review.googlesource.com/Documentation/dev-build-plugins.html

[4] - https://gerrit-review.googlesource.com/Documentation/cmd-plugin-install.html