ExecStart یک shell نیست: اصلاح واحد کمکی VPN
ExecStart برنامهای را با آرگومانها اجرا میکند، نه خط لوله shell. از یک مفسر صریح یا اسکریپت واقعی استفاده کنید، سپس واحد را بررسی و ژورنال را بخوانید.
یک خط فرمان کارآمد را در ExecStart کپی کنید، بازبارگذاری کنید و واحد شکست میخورد یا بیصدا خارج میشود چون آرگومانها نام فایل تلقی میشوند. هیچ چیز در systemd خراب نیست. ExecStart فهرست آرگومان است، نه shell، بنابراین لولهها، تغییرمسیر و شرطها همان لحظهای که از ترمینال شما بیرون میروند معنای خود را از دست میدهند. تصمیم بگیرید shell کجا باید باشد، سپس واحدی بنویسید که دقیقاً بگوید چه چیزی باید اجرا شود.
چرا خط لوله در ExecStart کاری نمیکند
وقتی فرمانی در ترمینال تایپ میشود، shell نخست آن را تجزیه میکند. خط لوله میسازد، فایلها را برای تغییرمسیر باز میکند و متغیرها را پیش از شروع هر برنامهای گسترش میدهد. systemd هیچیک از این کارها را انجام نمیدهد: ExecStart را بر پایه قواعد خودش تقسیم میکند و برنامه حاصل را با همان آرگومانها اجرا میکند. از این رو > بهعنوان آرگومان تحتاللفظی میرسد و | به برنامه سپرده میشود، نه اینکه دو فرایند را به هم وصل کند. واحد بدرفتاری نمیکند؛ نحو معنای دیگری داشت.
این عمدی است نه بیدقتی، زیرا برخورد قابلپیشبینی با آرگومانها همان چیزی است که واحدها را قابلحسابرسی میکند. تنها کسانی را غافلگیر میکند که وجود shell را فرض میکنند.
واحدی بنویسید که دقیقاً بگوید چه چیزی اجرا میشود
با واحد کوچکی آغاز کنید که هدفش در ژورنال دیده میشود، تا مشکلات تجزیه از مشکلات تونل جدا بمانند. این یکی فقط نسخه یک باینری را گزارش میکند.
[Unit]
Description=Report the installed OpenVPN version
[Service]
Type=oneshot
ExecStart=/usr/sbin/openvpn --version
StandardOutput=journal
StandardError=journalمسیر یک نگهدارنده جای است، پس آن را با باینریای که واقعاً نصب شده جایگزین کنید. Type=oneshot برای فرمانی که تا پایان اجرا میشود مناسب است و تنظیمات ژورنال خروجی آن را جایی میگذارد که بدون ترمینال بخوانید. بخش نصب عمداً وجود ندارد، بنابراین واحد بهجای فعال شدن در بوت، دستی فراخوانی میشود. فایل را پیش از شروع بررسی کنید، زیرا غلط تایپی در نام یک دستور متفاوت از غلط در مسیر شکست میخورد.
systemd-analyze verify /etc/systemd/system/openvpn-version.service
systemctl start openvpn-version.serviceمنطق shell را در یک اسکریپت نگه دارید
وقتی کمکی واقعاً به منطق شرطی نیاز دارد، آن منطق را در یک اسکریپت بگذارید و آن را صریحاً فراخوانی کنید. اسکریپت به خط مفسر، اجازه اجرا و کدهای خروجی نیاز دارد که سرویس بتواند گزارش کند. اجرای /bin/sh -c با فرمانی در گیومه کار میکند، اما قواعد گیومه را داخل واحد پنهان میکند و بازبینی را سختتر. اسکریپت نامدار را میتوان دستی آزمود و بعداً بدون ویرایش واحد جایگزین کرد. به نمایه shell کاربر تعاملی تکیه نکنید، زیرا سرویس با محیط خودش آغاز میشود.
وقتی مسیرهای نسبی مهماند WorkingDirectory را تنظیم کنید و محیط را از طریق دستورهای پشتیبانیشده واحد منتقل کنید، نه اینکه آن را در ترمینال صادر کنید. رازها به فایلهای محافظتشده یا اعتبارنامهها تعلق دارند، نه به فهرست آرگومانهایی که ژورنال ممکن است ثبت کند.
واحدی را که واقعاً وجود دارد بررسی کنید
فایلهای drop-in رفتار را بدون دست زدن به فایلی که به یاد دارید ویرایش کردید تغییر میدهند. بخوانید چه چیزی مدیر بارگذاری کرده و آخرین اجرا چه گزارش داده است.
systemctl cat vpn-helper.service
journalctl -u vpn-helper.service -n 20 --no-pagerخروجی واحد فایلهای drop-in را به ترتیب بارگذاری شامل میشود و ExecStart غیرمنتظره معمولاً از همانجا میآید. پس از هر تغییر، مدیر را بازبارگذاری کنید و تأیید کنید که تعریف جدید همان چیزی است که systemctl cat چاپ میکند. ژورنال خط فرمان دقیق و وضعیت خروج را نشان میدهد، و این مشکل آرگومان را از باینری غایب یا خطای دسترسی جدا میکند. سپس تصمیم بگیرید که کمکی به چرخه عمر VPN موجود تعلق دارد یا باید تشخیص دستی بماند، زیرا فرایند تونل پسزمینه دوم یک مالک رقیب میسازد.
واحدی که بهموقع خروجی درست را چاپ میکند نخستین نقطه عطف خوبی است. هنوز تونل نیست و نباید خود را چنین جا بزند.