deepin-terminal改造風雲再起

運維筆記發表於2021-08-31

1. 創作背景

使用deepin-terminal的時候,我發現一些小的問題。

在論壇的帖子(https://bbs.deepin.org/zh/post/224502)也總結反饋了這些問題

  • 終端標籤變色
  • 遠端管理標籤標題跟右鍵選項顯示異常
  • 截圖傳送到微信之後,再切換到終端時,雙擊複製無法將內容置入剪下板

經過我虛擬機器全新安裝deepin20.2.3後,幾輪測試下來發現

終端標籤變色的問題,應該只是個特性,發生在如果標籤1有工作正在進行,或者有更新,比如sudo apt update或者ssh 連線超時被close時,而此時你正在標籤2工作,那麼這個變色會提醒你,標籤1的工作結束了,或者有更新

截圖傳送到微信,然後切換到終端,雙擊複製,概率性無法複製的問題,應該是個穩定的bug。這個bug我也反饋給官方了,不清楚是否後續會解決。

今天來說的是遠端管理

2. 遠端管理的bug描述

deepin-terminal提供的遠端管理,一般會要求我們輸入名稱,地址 ,帳號,密碼這些。正常情況下,如果這些都是正常的,遠端連線不會存在問題。連線上以後,標籤標題會變成遠端主機的使用者@主機名,而終端右鍵也出現“上傳檔案”和“下載檔案“的選項。

正常的情況我們就不說了,說說不正常的情況

2.1 點選遠端連線時,突然不想連線了,或者密碼錯誤了,ctrl+c終止連線失敗

這種情況發生在,比如管理員重新設定了遠端連線的密碼,而你的遠端連線並未更新,那麼你使用這個連線遠端主機時,必然會提示你輸入密碼,3此輸入不正確,那麼就無法登陸。如果你輸入1次,發現不正確,直接ctrl+c了,那麼此時標籤的標題已經被設定為遠端主機的主機名@主機地址了,而右鍵選項也多出了“上傳檔案”和“下載檔案”的選項。

在未成功登陸遠端主機的情況下,這種顯示的變化,顯然是個BUG。如何解決,且等下文分析

2.2 輸錯了伺服器的IP,因為網路路由不正確,連線失敗

如果你不小心直接輸錯了遠端主機的IP地址,而且點選登陸時發現根本沒法登陸,報一些比如No route to host的資訊,告訴你沒有到遠端主機的路由。而此時標籤的標題已經被設定為遠端主機的主機名@主機地址了,而右鍵選項也多出了“上傳檔案”和“下載檔案”的選項。

在未成功登陸遠端主機的情況下,這種顯示的變化,顯然是個BUG。如何解決,且等下文分析

2.3 修改了ssh配置檔案,比如修改了LogLevel導致退出遠端主機後顯示異常

如果你是一個不喜歡被繁雜的資訊打擾的人,修改了/etc/ssh/ssh_config裡面,把LogLevel設定為quit,那麼當你使用deepi-terminal的遠端管理登陸遠端主機時,標籤標題和右鍵選項均可以正常顯示,但是你工作結束,通過exit命令,logout命令或者直接ctrl+d登出時,你會發現你的標籤和右鍵選項還在。

在成功退出遠端主機後,終端標籤標題跟右鍵選項沒有變化,這顯然是個BUG。如何解決,且等下文分析

3. 如何解決

具體的解決,肯定是要修改程式碼的啦

3.1 先分析一下為何如此

分析deepin-terminal的原始碼得知,當你使用遠端管理登陸遠端節點時,deepi-terminal實際上載入和執行了一段標準的expect指令碼,該指令碼是一個免互動的指令碼。deepin-terminal會把遠端管理皮膚裡面的資訊同步到這段expect裡面,替換到設定的“巨集”,然後執行它們。

同時deepin-terminal啟動了一個定時器觸發,0.1s的時間,判斷這個遠端管理是否存在expect的程式資訊,如果有就把當前標籤的連線,設定為遠端標籤的格式,還有其他資訊,比如編碼,和右鍵選項。

試想,如果expect執行的ssh超時還沒有到,標籤已經變化,而ssh連線失敗後,變化的標籤會變回去嗎?

答案是不一定的。這就是上面的2.1和2.2描述的bug原因。

至於2.3的描述,就要看deepin-terminal退出遠端時,是如何把標籤重新設定為本地的。它是通過截獲收發資料的字串,判斷其是否包含"Connected to "和"closed."或者"Permission denied"來判斷終端斷開了遠端連線。

inline void TermWidget::onTermWidgetReceivedData(QString value)
{
    /******** Modify by ut000610 daizhengwen 2020-05-25: quit download****************/
    if (value.contains("Transfer incomplete")) {
        QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier);
        QApplication::sendEvent(focusWidget(), &keyPress);
    }    
    if (value.endsWith("\b \b #")) {                     // 結束的時候有亂碼的話,將它清除
        QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_U, Qt::ControlModifier);
        QApplication::sendEvent(focusWidget(), &keyPress);
    }    
    /********************* Modify by ut000610 daizhengwen End ************************/
    // 退出遠端後,設定成false
    if ((value.contains("Connection to") && value.contains(" closed.")) || value.contains("Permission denied"))
        QTimer::singleShot(100, this, &TermWidget::onExitRemoteServer);
}

而我修改了LogLevel為quit,這個配置在我登出遠端節點時,只顯示Logout或者中文“登出”,”登出“等字樣,並不存在”Connection to"的字樣,因此終端就認定這個操作不是退出遠端,自然它的標籤標題跟右鍵選單是不會變化的了。

3.2 再看看如何處理

直接看修改結果吧,對於2.1和2.2的bug描述,直接修改原始碼檔案:deepin-terminal/src/assets/other/ssh_login.sh

修改為:

# Spawn and expect
if { $authentication == "no" } {
eval spawn $ssh_cmd $ssh_opt -t $remote_command exec \\\$SHELL -l
} else {
eval spawn $ssh_cmd $ssh_opt -i $private_key -t $remote_command exec \\\$SHELL -l
}


#fix bug:修復在登陸過程中直接ctlr+c終止登陸時,終端標籤和右鍵選項變成遠端的現象.
trap {
    send_user "Connection to closed.\n"
    exit
} SIGINT

if { [string length $password] } {
    expect {
        timeout {send_user "ssh connection time out, please operate manually\n"}
        -nocase "yes/no" {send "yes\r"; exp_continue}
        -nocase -re "password:|enter passphrase for key" { send "$encoded_password\r" }
        "No route to host" { send "Connection to closed.\n";exp_continue} #修改遠端地址錯誤導致的終端標籤和右鍵選項變成遠端
        "Connection to closed." { exit }
    }
}

interact

大概意思就是通過expect捕獲2.1和2.2描述的bug或者crtl+c訊號,向終端丟擲"Connection to closed."字樣,讓終端捕獲這個字樣後,把當前標籤設定為本地標籤即可,就不會顯示為遠端的樣式了

而對於2.3的bug描述,還是修改/etc/ssh/ssh_config的LogLevel引數吧,修改為LogLevel=ERROR即可,另外一種動作比較大就是修改原始碼,把"登出",“登出”,"logout"等字樣包含在對終端退出遠端的判斷,不過這個成本比較大了,意義也不是很大。暫且不做了。

4. 總結

花了2天時間摸索和測試吧,總的來說,達到了驗證,修改和修復這些bug的目的。估計沒有多少人會觸發這樣的bug,但是至少可以學習deepin-terminal的工作原理不是,賺到了。

相關文章