Вчера тунелът работеше, а тази сутрин unit е мъртъв с Failed at step EXEC spawning /usr/local/bin/vpnclient в журнала. Данните за достъп, маршрутите и акаунтът никога не влизат в това изречение. systemd превърна ExecStart в път, поиска от ядрото да го изпълни и ядрото отказа — най-често защото на този път нищо не съществува или файлът не е изпълним. Отнасяйте се към проблема като към проблем с файл и argv, докато разчетената команда не докаже обратното.

Защо EXEC се проваля преди да започне удостоверяването

systemd не стартира ExecStart през shell. Той прави 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. Пазете diff-а на unit и diff-а на sudoers заедно в записа за промяната, за да не върне следващото обновяване тихо разделения път.

Подобни статии