Ghi chú thiết kế

Pomelo 登入管理員(pomelo-dm + pomelo-greeter)

Đi kèm Pomelo 0.32 Đây là ghi chú thiết kế kỹ thuật của nhà phát triển. Để dùng hằng ngày, hãy xem hướng dẫn sử dụng.

Hiện ghi chú thiết kế chỉ có tiếng Trung phồn thể.

最後更新:2026-10-19(0.26)

為什麼自己做

LightDM+slick-greeter GDM Pomelo
常駐程式 lightdm(GLib、D-Bus、AccountsService) gdm+gnome-shell(完整 GNOME) pomelo-dm(只靠 libc+libpam)
登入後還在跑的 lightdm + 每個工作階段一個 lightdm 子行程 gdm + 每個工作階段一個 gdm-session-worker pomelo-dm(約 2 MB)+ 每個工作階段一個子行程
登入畫面 GTK gnome-shell pomelo-greeter(Xlib+cairo,約 22 MB,登入後就結束)

測試環境量到的數字(1440×900):pomelo-dm 常駐 2.3 MB;登入畫面 22 MB、閒置時 CPU 0%。

架構

systemd ─ pomelo-dm(root)
            ├─ Xorg :0 vt7 -auth /run/pomelo-dm/:0.auth -nolisten tcp
            ├─ pomelo-greeter(系統帳號 pomelo-greeter,fd 3 = 跟 pomelo-dm 之間的 socket)
            └─ 工作階段子行程(root):PAM 驗證 → setcred → open_session(pam_systemd 登記 logind)
                  └─ 使用者的工作階段(已切換身分):sh 讀 /etc/profile、~/.profile 後 exec pomelo-session

一輪的流程:

  1. 產生新的 MIT-MAGIC-COOKIE(16 個亂數位元組),寫成 X 的授權檔,啟動 X,等 X 送 SIGUSR1 表示準備好 (只認這個 X 自己送的訊號;上一輪留下的會先清掉)。
  2. 以 pomelo-greeter 帳號啟動登入畫面;它只拿得到 X 的 cookie,碰不到密碼檔。
  3. 登入畫面送 LOGIN 使用者 密碼 工作階段。pomelo-dm 先確認帳號可以登入(不是 root、shell 不是 nologin), 再開一個子行程做 PAM 驗證(失敗至少等 1.5 秒)。成功就回 OK,登入畫面淡出結束; pomelo-dm 把登入畫面的行程群組和 pomelo-greeter 帳號的所有行程都結束掉, 再關掉這個 X、用新的 cookie 開一個全新的 X 給使用者(登入畫面用過的連線、留下的視窗、它知道的 cookie 全部作廢)。
  4. 子行程收到新 cookie 後 setcred、open_session(common-session 裡的 pam_systemd 向 logind 登記:XDG_SESSION_TYPE=x11、 XDG_SEAT=seat0、XDG_VTNR=7),把使用者的 cookie 寫到 $XDG_RUNTIME_DIR/pomelo-xauth,切換成使用者, 執行 /usr/share/xsessions 裡選的工作階段(預設 pomelo)。
  5. 工作階段結束(登出)→ close_session → pomelo-dm 關掉 X,回到第 1 步。 每個使用者登出後都換一個全新的 X 和 cookie,上一位的程式不可能殘留在下一個登入畫面上。

登入有多快(0.19)

0.18 以前登入成功後會把 X 整個關掉再重開(為了讓登入畫面留下的東西全部作廢),實體機器上 Xorg 關閉+啟動要好幾秒, 畫面會黑一下。0.19 改成:先把新 cookie 寫進 X 的授權檔、結束登入畫面的所有行程,X 沒有任何連線就會自己「重設」 (清掉所有視窗與資源、重新讀授權檔,舊 cookie 立刻失效),一樣乾淨但只要幾百毫秒;萬一 2 秒內沒有重設才退回重開 X。 另外修正登入畫面淡出後還多等一秒才結束、把複製桌布移到工作階段開始之後。測試環境量到:按 Enter 到工作階段開始 從約 1.5 秒(不含 Xorg 重開)降到約 0.87 秒,其中 0.35 秒是淡出動畫。開 POMELO_DM_DEBUG=1 時日誌會印各階段的毫秒數。

0.20:工作階段開始時 X 不再重設

0.19 在實機(Intel RKL、modesetting)上量到登入後還要約 9 秒桌面才出現。看 /var/log/Xorg.0.log,登入後 X 又初始化了 8 次 (每次約 0.5 秒,畫面清空):X 的預設是「最後一個連線離開就重設」,pomelo-session 在視窗管理員連上之前會先跑 xrdb、xinput、setxkbmap、xrandr 等短命的小程式,每個結束時 X 就重設一次。GDM 啟動 X 時加了 -noreset 所以沒有這問題。 0.20 在登入畫面結束、X 重設完之後,由 pomelo-dm 對 X 保持一條什麼都不做的連線直到登出,之後就不會再重設 (登入畫面期間不保持,因為要靠那次重設清掉登入畫面)。測試環境:按 Enter 到選單列出現從 3.2 秒降到 1.0 秒。 POMELO_DM_DEBUG=1 時若工作階段中 X 又重設,日誌會記「X 在工作階段中重設了」。

剩下的時間:開機後第一次登入時,使用者的 systemd(user@.service)要先啟動完成,pam_systemd 會等它 (這台實測約 3 秒,大多是剛開機時系統還忙);這跟用哪個登入管理員無關,之後再登入就不用等。

登入畫面

  • 上方:日期(10月19日 週一)與大時鐘;右上角電量。
  • 下方:選到的使用者在正中間(大頭像、名字、圓角密碼框),其他使用者小頭像排在左右;點頭像或 ← → 切換。
  • 密碼框:打字顯示圓點,右邊出現 → 按鈕;驗證時轉圈;錯誤時左右晃動、框變紅、顯示原因;⇪ 大寫鎖定提示。
  • 最下面:睡眠、重新開機、關機。左下角:工作階段(裝了兩個以上才出現)。
  • 背景:該使用者上次用的桌布(登入時由 pomelo-dm 以「使用者身分」讀取、只收 PNG/JPEG,存到 /var/lib/pomelo-dm/users/<名字>.wallpaper);沒設桌布就是跟桌面一樣的內建漸層。頭像用 ~/.face 或 AccountsService 的圖示,都沒有就畫一個灰色的人像。
  • 語言跟系統(/etc/default/locale),13 種翻譯都有。

設定:/etc/pomelo/dm.conf

鍵 說明
autologin-user 開機自動登入的帳號(用 /etc/pam.d/pomelo-dm-autologin,不問密碼;只在開機第一次)
autologin-session 自動登入的工作階段
default-session 預設工作階段(pomelo)
hide-users 不出現在登入畫面的帳號(逗號分開)
show-other-user 顯示「其他使用者」(手動輸入帳號)
background 背景:user(使用者自己的桌布,預設)、image(/var/lib/pomelo-dm/background)、gradient(內建漸層)
clock 時鐘:24h、12h、off
text-size 文字大小:normal、large(放大 18%)
user-list true 顯示使用者清單;false 只有「名稱與密碼」兩個欄位
power-buttons 顯示睡眠、重新開機、關機按鈕
message 顯示在登入畫面下方的一段話(300 位元組以內),例如失物招領的電話
vt X 跑在第幾個 VT(7)
xserver 自訂 X 指令

用「系統設定 → 登入畫面」調整(0.21)

不用自己編輯 dm.conf:系統設定裡的「登入畫面」頁可以選背景(使用者自己的桌布/指定圖片/漸層; 指定圖片時列出目前的桌布、/usr/share/backgrounds 與「圖片」資料夾的縮圖,也可以輸入路徑)、時鐘、文字大小、 登入方式(使用者清單或名稱與密碼)、「其他使用者」、不顯示的帳號、自動登入、登入畫面訊息、電源按鈕。

  • 預覽:用目前(還沒套用)的設定開 pomelo-greeter --preview,按 Esc 離開,不用登出就能看到樣子。
  • 套用:透過 pkexec 執行 pomelo-dm --set key=value … [--set-background 圖片],要輸入管理員密碼。 pomelo-dm 會檢查每個鍵與值(帳號要存在、工作階段要裝了、不能有控制字元…),保留 dm.conf 裡的註解與其他行, 寫到暫存檔再改名(不會寫一半)。背景圖一樣在「已切換成呼叫者身分」的子行程裡讀(不能藉此讀到別人的檔案), 只收 PNG/JPEG、16384 像素、25 MB 以內,存成 /var/lib/pomelo-dm/background。
  • 下次出現登入畫面(登出或重新開機)就會套用,不用重新啟動 pomelo-dm。

安全設計

0.17 發佈前做過一次獨立的安全審查,找到的問題都已修正(下面每一項都是)。

  • 登入畫面以沒有權限的系統帳號執行;確認 setuid(0) 失敗才繼續。
  • root 不能從登入畫面登入(PAM 的 pam_succeed_if user != root,程式裡也擋)。
  • PAM 對話只回答第一個密碼問題;還有其他問題(例如一次性密碼)就拒絕,不會拿密碼亂答。
  • 不存在的帳號也會等 2 秒才回答,不能用回應時間猜帳號。
  • 使用者的桌布、頭像一定在「已切換成使用者」的子行程裡讀(使用者可以把桌布路徑設成 /etc/shadow), 只讀一般檔案(FIFO、裝置檔不讀),5 秒讀不完就強制結束;root 這邊檢查是 PNG/JPEG、圖片寬高 (頭像 4096、桌布 16384 以內)與檔案大小(2 MB/25 MB),寫檔用 O_EXCL|O_NOFOLLOW, 存成 root:pomelo-greeter 0640(只有登入畫面讀得到;家目錄可能是別人不能讀的)。
  • libpomelo 的圖片解碼也有尺寸上限並檢查記憶體配置,尺寸異常的圖不會讓程式當掉; 萬一登入畫面還是連續當掉,pomelo-dm 會改成不載入任何使用者圖片的模式,避免所有人都登不進來。
  • pomelo-dm 自己也檢查帳號能不能登入(登入畫面沒有權限、不能信任),不存在與不能登入的帳號回應時間相同。
  • 睡眠、重新開機、關機:還有其他人登入(例如 ssh)時拒絕;用 systemctl --check-inhibitors=yes, 系統更新等程式要求「暫時不要關機」時也會拒絕。
  • 工作階段的輸出寫到 ~/.xsession-errors;開不了(例如被換成捷徑)就丟掉,不會寫進系統日誌。
  • 結束 pomelo-dm(例如關機)時,工作階段會收到 SIGTERM,6 秒內不結束就強制結束,一定會關閉 PAM session。
  • X 只開 UNIX socket(-nolisten tcp),沒有 cookie 的程式連不上。
  • 每次登入前與登出後都關掉 X、換新的 cookie。
  • systemd 單元用 ExecCondition 判斷是不是目前的登入管理員;不擋開機畫面(plymouth)結束, 文字登入(Ctrl+Alt+F3)這條退路一直可以用。

安裝與切換(重要:先試用)

sudo apt install ./pomelo-greeter_*.deb ./pomelo-dm_*.deb
sudo pomelo-dm-switch try     # 在 Ctrl+Alt+F7 開一個 Pomelo 登入畫面試用,不改任何設定
sudo pomelo-dm-switch on      # 確定沒問題再改成每次開機都用
sudo pomelo-dm-switch off     # 換回原本的(GDM/LightDM),記得是哪一個

萬一登不進去:按 Ctrl+Alt+F3 用文字登入,執行 sudo pomelo-dm-switch off,再 sudo reboot。 移除 pomelo-dm 時如果它正是目前的登入管理員,會自動先換回原本的。

Fedora、Rocky(0.26)

  • PAM 用 Red Hat 系的寫法(pomelo-dm.pam.redhat:password-auth、postlogin、pam_selinux close/open、pam_namespace, 參考 Fedora 上 LightDM 與 GDM 的設定)。這些系統沒有 pam-auth-update,鑰匙圈的自動解鎖直接寫在 pomelo-dm 的 PAM 設定裡 (-auth/-session optional pam_pomelo_keyring.so,沒裝鑰匙圈時略過)。
  • SELinux:安裝時用 semanage fcontext 把 /usr/sbin/pomelo-dm 標成 xdm_exec_t、/var/lib/pomelo-dm 標成 xdm_var_lib_t、 /run/pomelo-dm 標成 xdm_var_run_t,讓 pomelo-dm 跟 GDM、LightDM 一樣跑在 xdm_t,登入後 pam_selinux 才能把工作階段切到 使用者的安全標籤。沒有 semanage 時會提醒安裝 policycoreutils-python-utils。移除時刪掉這些規則。
  • 系統帳號用 useradd -r 建立;系統語言讀 /etc/locale.conf。
  • pomelo-dm-switch:這些系統沒有 /etc/X11/default-display-manager,改從 display-manager.service 連到的服務檔讀 ExecStart 判斷目前的登入管理員;換回時認得 /usr/sbin/gdm。用模擬的目錄結構(POMELO_DM_SWITCH_ROOT)測過 status/on/off。
  • 還沒在實體的 Fedora、Rocky 上測過(特別是 SELinux enforcing 時的登入流程),使用手冊裡請使用者先用 try。

測試(不需要 root)

pomelo-dm --test --display :9 --xserver "Xvfb -screen 0 1440x900x24" \
          --pam-dir 測試用PAM目錄 --state 目錄 --sessions 工作階段目錄
pomelo-greeter --preview        # 只看登入畫面,密碼 demo 會「成功」

測試模式以目前的使用者執行(不切換身分、不登記 logind、電源按鈕只印訊息)。已在這個模式驗證: 錯誤密碼(約 1.5 秒後框變紅並顯示原因)、正確密碼登入、登入前換新 X 與 cookie 後工作階段仍能連上、 工作階段拿到的環境變數、登出後回到登入畫面、自動登入、登入畫面當掉後自動重開與連續當掉時的安全模式、 工作階段中結束 pomelo-dm 會把工作階段、X、登入畫面全部收掉、尺寸異常的 JPEG/PNG(65000×65000) 不會讓登入畫面當掉、電源按鈕的提示、「其他使用者」、工作階段選單、多語言、1280×720~1920×1080 的排版。 「還有其他人登入」的判斷用這台主機真的 loginctl 測過(有 ssh 時拒絕、closing 的不算)。

還沒驗證、只能在實體機器上測的:以 root 執行時的身分切換、pam_systemd/logind 登記 (音量、外接碟等權限)、真正的 Xorg 與 VT 7、pomelo-keyring 用登入密碼自動解鎖、關機/重新開機。

還沒做

  • 在登入狀態下「切換使用者」(同時兩個人登入)。
  • 螢幕鎖定(pomelo-lock)時從登入畫面切到別的使用者。
  • 遠端桌面(xrdp 有自己的登入流程,不受影響)。
  • Wayland。