دیروز تونل کار می‌کرد، و امروز صبح unit با Failed at step EXEC spawning /usr/local/bin/vpnclient در ژورنال مرده است. اعتبارنامه‌ها، مسیرها و حساب هرگز به آن جمله راه پیدا نمی‌کنند. systemd مقدار ExecStart را به یک مسیر تبدیل کرد، از کرنل خواست آن را اجرا کند، و کرنل رد کرد، بیشتر به این دلیل که چیزی در آن مسیر وجود ندارد یا فایل اجراپذیر نیست. تا زمانی که فرمان تجزیه‌شده خلافش را ثابت کند، این خطا را یک مسئله فایل و argv بدانید.

چرا EXEC پیش از آغاز احراز هویت شکست می‌خورد

systemd مقدار ExecStart را از طریق پوسته اجرا نمی‌کند. یک fork می‌سازد، از آن خط بردار آرگومان می‌سازد و execve را روی عنصر اول فراخوانی می‌کند. اگر آن فراخوانی شکست بخورد، سرویس هرگز به کد خودِ کلاینت VPN نمی‌رسد، پس هیچ ویرایش پروفایل یا بررسی اعتبارنامه‌ای کمکی نمی‌کند.

کد وضعیت این خانواده را از خرابی‌های معمولی جدا می‌کند. unitی که با status=203/EXEC خارج می‌شود هرگز فرآیندی آغاز نکرده است؛ unitی که آغاز می‌شود و سپس با status=1 خارج می‌شود ممکن است در احراز هویت، DNS یا مسیریابی شکست خورده باشد. پیش از خواندن گزارش کلاینت، کد را بخوانید، زیرا در حالت اول گزارش کلاینت خالی خواهد بود.

مسیر موجود در ژورنال همان است که 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 باید از آن پیروی کند. اگر اسکریپت فروشنده مالک آن باشد، همان قاعده برای پیشوند فروشنده اعمال می‌شود. کپی کردن فایل دودویی میان پیشوندها دو نسخه می‌سازد که در به‌روزرسانی بعدی از هم جدا می‌شوند.

وقتی فرمانی داخل گیومه به نام فایل تبدیل می‌شود

حالت شکست دومی پشت همان خط ژورنال پنهان است. کسی این را در 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 پیش از راه‌اندازی مجدد یک مسیر گمشده یا غیرقابل‌اجرا را می‌گیرد، و فرمان وضعیت پس از راه‌اندازی مجدد می‌گوید آیا فرآیند اکنون وجود دارد.

اگر unit آغاز شود و سپس شکست بخورد، خانوادهٔ EXEC را ترک کرده‌اید و به مدیریت خطای خودِ کلاینت وارد شده‌اید. در ژورنال پیام کلاینت را بجویید، نه یک شکست execve دیگر. تفاوت unit و تفاوت sudoers را با هم در سابقهٔ تغییر نگه دارید تا به‌روزرسانی بعدی مسیر دوپاره را بی‌صدا بازنگرداند.

مقالات مرتبط