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, или трябва да остане ръчна диагностика, защото втори фонов процес на тунел създава конкурентен собственик.
Модул, който отпечатва правилния изход при поискване, е добър първи етап. Той още не е тунел и не бива да се представя за такъв.