გუშინ გვირაბი მუშაობდა, დღეს კი 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 გადატვირთვამდე იჭერს დაკარგულ ან არაშესრულებად გზას, ხოლო გადატვირთვის შემდეგ status ბრძანება გეუბნებათ, არსებობს თუ არა პროცესი ახლა.

თუ unit იწყება და შემდეგ ფარდება, თქვენ EXEC-ის ოჯახი დატოვეთ და კლიენტის საკუთარ შეცდომების დამუშავებაში შეხვედით. ჟურნალში მოძებნეთ კლიენტის შეტყობინება და არა კიდევ ერთი execve-ის ჩავარდნა. unit-ის diff და sudoers-ის diff ერთად შეინახეთ ცვლილების ჩანაწერში, რომ შემდეგმა განახლებამ გაყოფილი გზა ჩუმად არ დააბრუნოს.

დაკავშირებული სტატიები