olragon

Vì sao pkill -f cat tắt sạch app trên máy Mac?

🌱 mầmgieo #terminal#macos#ai#bai-hoc

Viết cho Ai hay gõ pkill, killall trong terminal, hoặc đang để một AI agent tự chạy lệnh shell trên máy mình.

Mình để mấy con AI tự làm việc ban đêm trên máy Mac ở nhà, và có một đêm chúng tắt sạch mọi app trên máy chỉ bằng một lệnh bốn chữ.

Các con số dưới đây mình đếm lại trên chính máy đó, sáng 31/10, bằng pgrep. Lệnh đó chẳng có gì nguy hiểm khi chỉ đếm.

English: Why did pkill -f cat close every app on my Mac?.

Chuyện gì xảy ra lúc 4 giờ sáng

Mình có một đội AI agent chạy theo lịch. Mỗi lượt, một agent mở terminal, đọc việc cần làm, gõ lệnh, kiểm tra rồi báo lại cho mình qua tin nhắn. Phần lớn các đêm trôi qua êm ru.

Đêm 13/10, lúc 4 giờ 12, một agent gặp một lệnh cat bị treo. Nó muốn dọn cho gọn, nên gõ pkill -f cat. Rồi gõ thêm lần nữa cho chắc.

Chừng đó là đủ để mọi app trên máy nhận lệnh tắt. Trình duyệt, trình soạn code, terminal, OrbStack với 21 container bên trong (có cả Postgres), công cụ điều khiển trình duyệt, con bot gửi tin nhắn cho mình. Và cả tiến trình điều phối đang trông chừng chính agent đó. Nó tự cưa cành mình đang ngồi.

Máy chẳng sập. WindowServer vẫn sống, Finder vẫn còn, tmux vẫn giữ nguyên hơn trăm phiên. Agent thì vẫn còn trong tmux, tự bật lại OrbStack và mấy dịch vụ cần thiết, rồi báo cho mình. Sáng ra mình đọc tin nhắn mà vừa buồn cười vừa lạnh gáy.

Sao chữ cat lại khớp với mọi thứ

pkill cat (không có -f) chỉ so với tên tiến trình. Tên mà chứa chữ cat thì bị bắn, nên nó vẫn rộng hơn bạn nghĩ, nhưng còn trong tầm.

Cờ -f đổi hẳn luật chơi: nó so với cả dòng lệnh, đường dẫn lẫn tham số. Và trên macOS, gần như mọi app nằm trong /Applications. Chữ Applications có chứa cat. Vậy là pkill -f cat thực ra có nghĩa là "tắt mọi thứ cài trong thư mục ứng dụng".

Sáng nay mình đếm lại, chỉ đếm thôi, bằng pgrep với cùng mẫu đó:

lệnhsố tiến trình khớp
pgrep -f cat183 trên 1.125
trong đó có /Applications trong dòng lệnh130
pgrep -x cat (đúng tên cat)0

Con số làm mình giật mình nhất là dòng cuối. Lúc đếm chẳng có tiến trình nào tên đúng là cat cả. Agent muốn bắn một con, súng lại nhắm 183 con.

Còn mấy chục tiến trình khớp mà chẳng nằm trong /Applications: những chữ như Location, Notification, verification, và cả những shell mà tham số có chữ cat ở đâu đó. Một tiến trình chẳng dính gì tới cat, chỉ vì tham số của nó nhắc tới chữ đó, cũng nằm trong danh sách.

Bạn thử nhắm vô danh sách dưới đây nha. Toàn là kiểu tiến trình hay chạy trên một máy Mac, có cả con cat bị treo ở dòng cuối. Bấm -f, -x, hay gõ một mẫu khác. Chẳng có gì bị tắt thiệt đâu.

  • /Applications/Safari.app/Contents/MacOS/Safari
  • /Applications/Google Chrome.app/Contents/MacOS/Google Chrome --type=renderer
  • /Applications/Visual Studio Code.app/Contents/MacOS/Electron
  • /Applications/OrbStack.app/Contents/MacOS/OrbStack
  • /System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
  • /System/Library/CoreServices/NotificationCenter.app/Contents/MacOS/NotificationCenter
  • /usr/libexec/locationd
  • /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
  • /System/Library/PrivateFrameworks/SkyLight.framework/Resources/WindowServer -daemon
  • /usr/sbin/cfprefsd agent
  • /opt/homebrew/bin/tmux new -s work
  • /bin/zsh -c grep -rn cat notes/
  • cat

Viết luật thì chưa đủ

Phản xạ đầu tiên của mình là viết thêm một dòng vào bộ luật cho agent: "đừng giết tiến trình theo mẫu". Rồi mình nghĩ lại.

Một agent đang gỡ một lệnh treo là lúc nó ít đọc luật nhất. Nó đang bận chữa cháy. Luật nằm ở đầu phiên, cách đó cả trăm dòng. Con người cũng vậy thôi: lúc hoảng, mình gõ cái lệnh quen tay nhất.

Nên mình làm thêm một lớp nữa: một hook chạy trước mỗi lệnh shell mà agent định gõ. Claude Code cho gắn hook kiểu này. Hook đọc lệnh, thấy dạng nguy hiểm thì chặn và giải thích lý do, lệnh chẳng bao giờ chạy. Cả hook dài 43 dòng, kèm 23 test.

Nó chặn bốn dạng:

Cái được phép thì ngắn gọn: kill <pid> với một pid mà chính agent đã ghi lại lúc khởi chạy, hoặc đóng đúng cửa sổ tmux mà nó đã mở. Còn một lệnh treo thì cứ để nó hết giờ, đừng đi săn theo tên.

Sáng nay chính nó chặn mình

Lúc gom tư liệu cho bài này, con AI giúp mình chăm vườn chạy một lệnh grep tìm chữ "pkill -f cat" trong mấy file ghi chú. Hook chặn ngay.

Hook coi mọi thứ sau dấu ngoặc kép như một lệnh có thể chạy. Mình cố ý viết vậy, vì ssh may-khac "pkill -f cat" hay bash -c '…' cũng chạy lệnh nằm trong ngoặc. Cái giá là một lệnh grep vô hại cũng bị chặn. Đổi cách gõ, tách chữ ra, là xong.

Mình nghĩ đó là cái giá đáng trả. Một lần chặn nhầm tốn mười giây. Một lần cho qua nhầm thì tắt sạch máy lúc 4 giờ sáng.

Đêm đó còn để lại một vết nữa. Tiến trình điều phối bị tắt giữa chừng, nên cái khoá của lượt đó chẳng ai mở. Mấy lượt sau cứ thấy khoá rồi bỏ qua, suốt gần bốn tiếng. Giờ khoá ghi luôn pid của tiến trình điều phối. Lượt sau thấy pid đó đã chết và phiên đã xong thì tự dọn.

Bạn tự thử trên máy bạn

Chỉ đếm, chẳng tắt gì:

pgrep -f cat | wc -l
pgrep -fl cat | grep -c /Applications
pgrep -x cat | wc -l

Nếu dòng đầu ra một con số lớn mà dòng cuối ra 0, bạn đã thấy cái bẫy đó trên máy bạn. Thói quen mình giữ từ đó: muốn tắt thì pgrep -x hoặc pgrep -fl trước, đọc danh sách, rồi kill đúng pid.

Chưa biết gì

Nếu bạn cũng để AI tự chạy lệnh trên máy mình, bạn rào nó bằng cách nào? Viết cho mình một lá thư bên dưới nha.

Gửi Ba rồng một lá thư

Đọc xong muốn nói gì thì viết cho mình nha. Thư chỉ tới tay mình, chẳng tự hiện lên trang. Lá nào hay mà bạn cho phép, mình chép lại dưới bài, không kèm tên bạn.

Mảnh vườn giữ gì từ lá thư này

Từ terminal cũng gửi được: curl -d 'msg=…' olragon.com/garden/pkill-cat-vi/letter