Sao chép một dòng lệnh đang chạy được vào ExecStart, tải lại, và unit thất bại hoặc thoát im lặng vì các đối số bị coi là tên tệp. Không có gì hỏng trong systemd. ExecStart là danh sách đối số, không phải shell, nên ống dẫn, chuyển hướng và điều kiện mất ý nghĩa ngay khi rời khỏi terminal của bạn. Hãy quyết định shell thuộc về đâu, rồi viết một unit nói chính xác điều gì sẽ được thực thi.

Vì sao đường ống trong ExecStart không làm gì cả

Khi một lệnh được gõ trong terminal, shell phân tích nó trước. Nó dựng một đường ống, mở tệp để chuyển hướng và mở rộng biến trước khi bất kỳ chương trình nào khởi động. systemd không làm bất cứ điều đó: nó tách ExecStart theo quy tắc riêng và chạy chương trình thu được với các đối số đó. Vì vậy > đến như một đối số nguyên văn, và | được giao cho chương trình thay vì nối hai tiến trình. Unit không hành xử sai; cú pháp vốn mang nghĩa khác.

Đó là chủ ý chứ không phải cẩu thả, vì việc xử lý đối số có thể đoán trước là điều khiến unit có thể kiểm toán. Nó chỉ làm bất ngờ những ai cho rằng có một shell.

Viết một unit nói chính xác điều gì chạy

Hãy bắt đầu bằng một unit nhỏ mà mục đích hiện rõ trong nhật ký, để các vấn đề phân tích tách khỏi vấn đề đường hầm. Unit này chỉ báo cáo phiên bản của một tệp nhị phân.

[Unit]
Description=Report the installed OpenVPN version

[Service]
Type=oneshot
ExecStart=/usr/sbin/openvpn --version
StandardOutput=journal
StandardError=journal

Đường dẫn là một chỗ giữ chỗ, nên hãy thay bằng tệp nhị phân thực sự được cài đặt. Type=oneshot phù hợp với một lệnh chạy đến khi hoàn tất, và thiết lập nhật ký đặt đầu ra ở nơi bạn đọc được mà không cần terminal. Cố ý không có phần cài đặt, nên unit được gọi thủ công thay vì bật khi khởi động. Hãy kiểm tra tệp trước khi chạy, vì lỗi gõ trong tên chỉ thị thất bại khác với lỗi trong đường dẫn.

systemd-analyze verify /etc/systemd/system/openvpn-version.service
systemctl start openvpn-version.service

Giữ logic shell trong một script

Khi phần phụ trợ thực sự cần logic có điều kiện, hãy đặt logic đó vào một script và gọi nó một cách rõ ràng. Script cần dòng thông dịch, quyền thực thi và mã thoát mà dịch vụ có thể báo cáo. /bin/sh -c với một lệnh trong dấu ngoặc kép thì có tác dụng, nhưng nó giấu quy tắc trích dẫn bên trong unit và khiến việc rà soát khó hơn. Một script có tên có thể được thử bằng tay và thay thế sau mà không phải sửa unit. Đừng dựa vào hồ sơ shell của người dùng tương tác, vì dịch vụ khởi động với môi trường riêng.

Khi đường dẫn tương đối quan trọng, WorkingDirectory hãy đặt, và truyền môi trường qua các chỉ thị được unit hỗ trợ thay vì xuất nó trong terminal. Bí mật thuộc về tệp được bảo vệ hoặc thông tin xác thực, không phải danh sách đối số mà nhật ký có thể ghi lại.

Kiểm tra unit thực sự tồn tại

Tệp drop-in thay đổi hành vi mà không chạm vào tệp bạn nhớ là đã sửa. Hãy đọc những gì trình quản lý đã nạp và những gì lần chạy cuối báo cáo.

systemctl cat vpn-helper.service
journalctl -u vpn-helper.service -n 20 --no-pager

Bản kết xuất unit bao gồm các tệp drop-in theo thứ tự nạp, và đó thường là nơi một ExecStart bất ngờ đến từ. Sau mỗi thay đổi, hãy tải lại trình quản lý và xác nhận rằng định nghĩa mới đúng là thứ mà systemctl cat in ra. Nhật ký hiển thị dòng lệnh chính xác và trạng thái thoát, điều này tách vấn đề đối số khỏi một tệp nhị phân thiếu hoặc lỗi quyền. Sau đó quyết định xem phần phụ trợ thuộc vòng đời VPN hiện có hay nên ở lại như một chẩn đoán thủ công, vì tiến trình đường hầm nền thứ hai tạo ra một chủ sở hữu cạnh tranh.

Một unit in đúng đầu ra khi được yêu cầu là cột mốc đầu tiên tốt. Nó chưa phải đường hầm, và không nên giả vờ là đường hầm.

Bài viết liên quan