ExecStart to nie powłoka: naprawa jednostki pomocniczej VPN
ExecStart uruchamia program z argumentami, a nie potok powłoki. Użyj jawnego interpretera lub prawdziwego skryptu, potem zweryfikuj jednostkę i przeczytaj dziennik.
Skopiuj działającą linię poleceń do ExecStart, przeładuj, a jednostka zawodzi lub kończy się cicho, bo argumenty są traktowane jako nazwy plików. W systemd nic nie jest zepsute. ExecStart to lista argumentów, a nie powłoka, więc potoki, przekierowania i warunki tracą sens w chwili, gdy opuszczają twój terminal. Zdecyduj, gdzie należy powłoka, a potem napisz jednostkę, która dokładnie mówi, co ma zostać wykonane.
Dlaczego potok w ExecStart nic nie robi
Gdy polecenie jest wpisywane w terminalu, powłoka najpierw je analizuje. Buduje potok, otwiera pliki do przekierowania i rozwija zmienne, zanim wystartuje jakikolwiek program. systemd nie robi nic z tego: dzieli ExecStart według własnych reguł i wykonuje powstały program z tymi argumentami. Dlatego > trafia tam jako dosłowny argument, a | jest przekazywany programowi zamiast łączyć dwa procesy. Jednostka nie zachowuje się źle; składnia znaczyła coś innego.
To jest zamierzone, a nie niedbałe, bo przewidywalne traktowanie argumentów czyni jednostki audytowalnymi. Zaskakuje tylko tych, którzy zakładają, że powłoka jest obecna.
Napisz jednostkę, która dokładnie mówi, co się uruchamia
Zacznij od małej jednostki, której cel widać w dzienniku, aby problemy z analizą pozostały oddzielone od problemów z tunelem. Ta tylko raportuje wersję pliku binarnego.
[Unit]
Description=Report the installed OpenVPN version
[Service]
Type=oneshot
ExecStart=/usr/sbin/openvpn --version
StandardOutput=journal
StandardError=journalŚcieżka jest symbolem zastępczym, więc zastąp ją plikiem binarnym, który jest faktycznie zainstalowany. Type=oneshot pasuje do polecenia, które działa do końca, a ustawienia dziennika umieszczają jego wyjście tam, gdzie możesz je przeczytać bez terminala. Celowo nie ma sekcji instalacji, więc jednostka jest wywoływana ręcznie, a nie włączana przy starcie. Zweryfikuj plik przed uruchomieniem, bo literówka w nazwie dyrektywy zawodzi inaczej niż literówka w ścieżce.
systemd-analyze verify /etc/systemd/system/openvpn-version.service
systemctl start openvpn-version.serviceTrzymaj logikę powłoki w skrypcie
Gdy pomocnik naprawdę potrzebuje logiki warunkowej, umieść tę logikę w skrypcie i wywołaj go jawnie. Skrypt potrzebuje linii interpretera, prawa wykonania i kodów wyjścia, które usługa może zgłosić. Uruchamianie /bin/sh -c z poleceniem w cudzysłowie działa, ale ukrywa reguły cytowania wewnątrz jednostki i utrudnia przegląd. Nazwany skrypt można przetestować ręcznie i później zastąpić bez edycji jednostki. Nie polegaj na profilu powłoki użytkownika interaktywnego, bo usługa startuje z własnym środowiskiem.
Ustaw WorkingDirectory, gdy względne ścieżki mają znaczenie, a środowisko przekazuj przez obsługiwane dyrektywy jednostki zamiast eksportować je w terminalu. Sekrety należą do chronionych plików lub poświadczeń, a nie do list argumentów, które dziennik może zapisać.
Sprawdź jednostkę, która naprawdę istnieje
Pliki drop-in zmieniają zachowanie bez dotykania pliku, który pamiętasz, że edytowałeś. Przeczytaj, co załadował menedżer i co zgłosiło ostatnie uruchomienie.
systemctl cat vpn-helper.service
journalctl -u vpn-helper.service -n 20 --no-pagerZrzut jednostki obejmuje pliki drop-in w kolejności ładowania i właśnie stamtąd zwykle pochodzi nieoczekiwany ExecStart. Po każdej zmianie przeładuj menedżera i potwierdź, że nowa definicja jest tym, co wypisuje systemctl cat. Dziennik pokazuje dokładną linię poleceń i status wyjścia, co oddziela problem z argumentami od brakującego pliku binarnego lub błędu uprawnień. Potem zdecyduj, czy pomocnik należy do istniejącego cyklu życia VPN, czy powinien pozostać ręczną diagnostyką, bo drugi proces tunelu w tle tworzy konkurencyjnego właściciela.
Jednostka, która na żądanie wypisuje właściwe wyjście, to dobry pierwszy kamień milowy. To jeszcze nie tunel i nie powinna udawać, że nim jest.