เมื่อวานอุโมงค์ทำงานได้ แต่เช้านี้ unit ตายแล้ว โดยมี Failed at step EXEC spawning /usr/local/bin/vpnclient ใน journal ข้อมูลรับรอง เส้นทาง และบัญชีไม่เคยอยู่ในประโยคนั้น systemd แปลง ExecStart เป็นพาธ ขอให้เคอร์เนลเรียกใช้งาน และเคอร์เนลปฏิเสธ ส่วนใหญ่เพราะไม่มีอะไรอยู่ที่พาธนั้นหรือไฟล์ไม่สามารถเรียกใช้งานได้ ให้ถือว่าปัญหานี้เป็นปัญหาไฟล์และ argv จนกว่าคำสั่งที่วิเคราะห์แล้วจะพิสูจน์เป็นอย่างอื่น

ทำไม EXEC จึงล้มเหลวก่อนที่การพิสูจน์ตัวตนจะเริ่ม

systemd ไม่ได้รัน ExecStart ผ่าน shell มันทำ fork สร้างเวกเตอร์อาร์กิวเมนต์จากบรรทัด แล้วเรียก execve บนองค์ประกอบแรก หากการเรียกนั้นล้มเหลว บริการจะไม่มีทางไปถึงโค้ดของไคลเอนต์ VPN เลย ดังนั้นการแก้โปรไฟล์หรือตรวจสอบข้อมูลรับรองจึงช่วยไม่ได้

รหัสสถานะแยกตระกูลนี้ออกจากอาการล่มทั่วไป unit ที่จบด้วย status=203/EXEC ไม่เคยเริ่มโปรเซสเลย ส่วน unit ที่เริ่มแล้วจบด้วย status=1 อาจล้มเหลวในขั้นการพิสูจน์ตัวตน DNS หรือการกำหนดเส้นทาง ควรอ่านรหัสก่อนอ่านบันทึกของไคลเอนต์ เพราะในกรณีแรกบันทึกของไคลเอนต์จะว่างเปล่า

พาธใน journal คือพาธที่ systemd ใช้ ไม่จำเป็นต้องเป็นพาธที่คุณแก้ไข ไฟล์ drop-in และเอาต์พุตของตัวสร้างสามารถเขียนทับไฟล์ของผู้จำหน่ายได้ และ daemon-reload ที่ค้างอยู่จะทิ้งบรรทัดเก่าไว้ในหน่วยความจำ ดังนั้นการวินิจฉัยแรกคือถาม systemd ว่ามันคิดว่าคำสั่งคืออะไร แทนที่จะเปิดไฟล์ที่คุณจำได้ว่าแก้ไข

สามที่ที่พาธของไบนารีถูกบันทึกไว้

ไคลเอนต์ VPN ที่ติดตั้งจากแพ็กเกจดิสทริบิวชัน สคริปต์ของผู้จำหน่าย และ tarball ของรีลีสอาจไปอยู่ในสามคำนำหน้าที่ต่างกัน unit กฎสิทธิ์ และตัวติดตั้งแต่ละตัวบันทึกคำนำหน้าหนึ่งในสาม และความล้มเหลวจะปรากฏเมื่อสองในสามไม่ตรงกัน

RecordCommon valueHow to read it
Unit or drop-in/usr/local/bin/vpnclientsystemctl cat vpnclient.service
Installed binary/usr/bin/vpnclientcommand -v vpnclient, then ls -l the result
Sudoers grant/opt/vpnclient/bin/vpnclientsudo grep -r vpnclient /etc/sudoers.d/

ความไม่ตรงกันที่ชัดเจนมีลักษณะเช่นนี้ สคริปต์ของผู้จำหน่ายติดตั้งไปที่ /usr/local/bin tarball ถูกแตกไว้ใต้ /opt และ unit ยังระบุพาธเก่า /usr/bin จากแพ็กเกจก่อนหน้า มีเพียงไฟล์เดียวในสามไฟล์นั้นที่อยู่จริง และมักไม่ใช่ไฟล์ที่อยู่ใน ExecStart

ตัดสินว่าพาธใดเป็นมาตรฐานก่อนแก้ไขไฟล์ใดไฟล์หนึ่ง หากแพ็กเกจดิสทริบิวชันเป็นเจ้าของไบนารี พาธของแพ็กเกจคือมาตรฐานและ unit ควรตามพาธนั้น หากสคริปต์ของผู้จำหน่ายเป็นเจ้าของ กฎเดียวกันใช้กับคำนำหน้าของผู้จำหน่าย การคัดลอกไบนารีระหว่างคำนำหน้าจะสร้างสองสำเนาที่แตกต่างกันในการอัปเกรดครั้งถัดไป

เมื่อคำสั่งในเครื่องหมายคำพูดกลายเป็นชื่อไฟล์

รูปแบบความล้มเหลวที่สองซ่อนอยู่หลังบรรทัด journal เดียวกัน มีคนเขียนสิ่งนี้ลงใน unit และคาดว่าไคลเอนต์จะเชื่อมต่อระหว่างการบูต

ExecStart=/usr/bin/"vpnclient connect office"

systemd แยกค่าออกเป็นคำ คำแรกจึงกลายเป็น /usr/bin/vpnclient connect office โดยเครื่องหมายคำพูดถูกถอดออกในฐานะไวยากรณ์การจัดกลุ่ม จากนั้นเคอร์เนลจะมองหาไฟล์ที่ชื่อมีช่องว่าง ซึ่งไม่มีอยู่ ข้อผิดพลาดเหมือนกับไบนารีที่หายไป แม้ไบนารีจะติดตั้งอยู่แล้ว

รูปแบบที่รองรับคือไฟล์เรียกใช้งานจริงตามด้วยอาร์กิวเมนต์แยกกัน เช่น ExecStart=/usr/bin/vpnclient connect office หากเวิร์กโฟลว์จำเป็นต้องใช้ไปป์ไลน์หรือเชนจริง ๆ ให้ใส่ไว้ในสคริปต์และเรียกสคริปต์ เพราะ ExecStart ไม่มี && ไม่มีไปป์ และไม่มีการขยาย glob

แก้ unit ไม่ใช่ระบบไฟล์

การซ่อมที่ดูน่าดึงดูดคือลิงก์สัญลักษณ์จากพาธที่คาดไว้ไปยังไบนารีจริง มันใช้ได้จนกว่าแพ็กเกจจะอัปเดตและลิงก์ชี้ไปยังไฟล์ที่ถูกลบ หรือจนกว่าป้าย SELinux จะไม่ตรงกับเป้าหมายอีก ควรแก้ unit ผ่าน drop-in เพื่อให้ไฟล์ของผู้จำหน่ายไม่ถูกแตะต้องและการอัปเกรดยังคาดเดาได้

กฎสิทธิ์ต้องตรงกับพาธเดียวกันและคำสั่งย่อยเดียวกัน รายการ sudoers สำหรับ /usr/local/bin/vpnclient ไม่ได้ให้สิทธิ์กับ /usr/bin/vpnclient และรายการที่อนุญาตทุกอาร์กิวเมนต์ทำให้โปรเซสภายในใด ๆ กำหนดค่าหรือหยุดอุโมงค์ได้ ให้สิทธิ์สองคำกริยาที่ unit ต้องการ และเก็บการรื้อถอนออกไปหากการหยุดบริการปิดอินเทอร์เฟซอยู่แล้ว

ตรงนี้การเปรียบเทียบให้ผลตอบแทน unit ฐานข้อมูลแพ็กเกจ และ sudoers ควรระบุพาธเดียว เมื่อเป็นเช่นนั้น ข้อผิดพลาด EXEC จะหายไปโดยไม่ต้องรีบูตและไม่ต้องมีสำเนาไบนารีในที่ที่สาม

พิสูจน์คำสั่งที่วิเคราะห์แล้วก่อนรีบูต

sudo systemctl daemon-reload
sudo systemctl cat vpnclient.service
systemctl show -p ExecStart vpnclient.service
systemd-analyze verify /etc/systemd/system/vpnclient.service
sudo systemctl restart vpnclient.service
systemctl status vpnclient.service --no-pager

systemctl show คือเอาต์พุตที่ชี้ขาด เพราะมันพิมพ์ argv ที่ systemd วิเคราะห์จริง รวมถึง drop-in ใด ๆ systemd-analyze verify จับพาธที่หายไปหรือเรียกใช้ไม่ได้ก่อนที่คุณจะรีสตาร์ต และคำสั่ง status หลังรีสตาร์ตบอกว่ามีโปรเซสอยู่ตอนนี้หรือไม่

หาก unit เริ่มแล้วล้มเหลว แสดงว่าคุณออกจากตระกูล EXEC และเข้าสู่การจัดการข้อผิดพลาดของไคลเอนต์เอง มองหาใน journal ข้อความของไคลเอนต์ ไม่ใช่ความล้มเหลว execve อีกครั้ง เก็บ diff ของ unit และ diff ของ sudoers ไว้ด้วยกันในบันทึกการเปลี่ยนแปลง เพื่อให้การอัปเกรดครั้งถัดไปไม่นำพาธที่แยกออกกลับมาโดยไม่ส่งเสียง

บทความที่เกี่ยวข้อง