Igår fungerade tunneln, och i morse är uniten död med Failed at step EXEC spawning /usr/local/bin/vpnclient i journalen. Inloggningsuppgifter, rutter och kontot förekommer aldrig i den meningen. systemd löste upp ExecStart till en sökväg, bad kärnan köra den, och kärnan vägrade, oftast för att det inte finns något på den sökvägen eller för att filen inte är körbar. Behandla felet som ett fil- och argv-problem tills det parsade kommandot bevisar motsatsen.

Varför EXEC misslyckas innan autentiseringen börjar

systemd kör inte ExecStart genom ett skal. Det gör en fork, bygger en argumentvektor från raden och anropar execve på det första elementet. Om det anropet misslyckas når tjänsten aldrig VPN-klientens egen kod, så varken profilredigering eller kontroll av inloggningsuppgifter kan hjälpa.

Statuskoden skiljer den här familjen från vanliga krascher. En unit som avslutas med status=203/EXEC har aldrig startat en process; en unit som startar och sedan avslutas med status=1 kan ha misslyckats med autentisering, DNS eller routing. Läs koden före klientloggen, för i det första fallet blir klientloggen tom.

Sökvägen i journalen är den som systemd använde, inte nödvändigtvis den du redigerade. Drop-ins och generatorutdata kan åsidosätta leverantörens fil, och en inaktuell daemon-reload lämnar den gamla raden i minnet. Därför är den första diagnosen att fråga systemd vad den anser vara kommandot, i stället för att öppna filen du minns att du ändrade.

Tre ställen där binärfilens sökväg står skriven

En VPN-klient som installerats från ett distributionspaket, ett leverantörsskript och en tarball från en utgåva kan hamna i tre olika prefix. Uniten, privilegieregeln och installationsprogrammet registrerade var sitt av dessa prefix, och felet uppträder när två av tre inte stämmer.

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/

En konkret avvikelse ser ut så här: leverantörsskriptet installerar till /usr/local/bin, tarballen packades upp under /opt, och uniten nämner fortfarande den gamla sökvägen /usr/bin från ett tidigare paket. Bara en av dessa filer finns, och det är sällan den i ExecStart.

Avgör vilken sökväg som är kanonisk innan du ändrar någon av filerna. Om distributionspaketet äger binärfilen är paketets sökväg kanonisk och uniten ska följa den. Om leverantörsskriptet äger den gäller samma regel för leverantörens prefix. Att kopiera en binärfil mellan prefix skapar två kopior som glider isär vid nästa uppgradering.

När ett kommando inom citattecken blir ett filnamn

Ett andra felläge gömmer sig bakom samma journallinje. Någon skriver detta i uniten och förväntar sig att klienten ansluter under uppstarten:

ExecStart=/usr/bin/"vpnclient connect office"

systemd delar upp värdet i ord, så det första ordet blir /usr/bin/vpnclient connect office, samtidigt som citattecknen tas bort som grupperingssyntax. Sedan letar kärnan efter en fil vars namn innehåller mellanslag, och det finns ingen. Felet är identiskt med en saknad binärfil, trots att binärfilen är installerad.

Den form som stöds är en riktig körbar fil följd av separata argument, till exempel ExecStart=/usr/bin/vpnclient connect office. Om arbetsflödet verkligen behöver en pipeline eller en kedja, lägg den i ett skript och anropa skriptet, eftersom ExecStart inte har &&, inga rör och ingen glob-expansion.

Reparera uniten, inte filsystemet

Den frestande reparationen är en symbolisk länk från den förväntade sökvägen till den riktiga binärfilen. Den fungerar tills paketet uppdateras och länken pekar på en borttagen fil, eller tills en SELinux-etikett inte längre matchar målet. Föredra att redigera uniten via en drop-in, så att leverantörens fil förblir orörd och uppgraderingar förblir förutsägbara.

Privilegieregeln måste matcha samma sökväg och samma underkommandon. En sudoers-post för /usr/local/bin/vpnclient ger inte tillstånd till /usr/bin/vpnclient, och en post som tillåter alla argument låter vilken lokal process som helst konfigurera om eller stoppa tunneln. Ge de två verb som uniten behöver och håll nedmonteringen utanför om stopp av tjänsten redan stänger gränssnittet.

Det är här jämförelsen lönar sig: uniten, paketdatabasen och sudoers bör alla ange en sökväg. När de gör det försvinner EXEC-felet utan omstart och utan en kopia av binärfilen på en tredje plats.

Bevisa det parsade kommandot före omstart

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 är det avgörande utdatat eftersom det skriver ut den argv som systemd faktiskt parsade, inklusive eventuell drop-in. systemd-analyze verify fångar en saknad eller icke-körbar sökväg innan du startar om, och statuskommandot efter omstarten säger om processen nu finns.

Om uniten startar och sedan misslyckas har du lämnat EXEC-familjen och gått in i klientens egen felhantering. Leta i journalen efter klientens meddelande, inte efter ännu ett execve-fel. Håll unit-diffen och sudoers-diffen tillsammans i ändringsloggen, så att nästa uppgradering inte tyst återinför den delade sökvägen.

Relaterade artiklar