locking használata script "ráindítás ellen"

Fórumok

volt egy korábbi téma, de nem jutottam dűlőre

Van egy /etc/cron.hourly/cronjob ami szeretném, ha kihagyna ha már van lockfile-a.
A megoldások amikkel próbálkoztam az volt a gond, hogy vagy ott hagyták a lock-t, de ráindultak vagy a lock kezelés jó volt, de ettől még elindult a script
Van erre működő alkalmazás vagy kezeljem le "kézi vezérléssel"?

A script maga egy rakat file műveletből áll (cp, mv, rsync) és némelyik közt JAVA alkalmazást futtat

Kösz, az építő ötleteket!

Hozzászólások

[ `pgrep -u "$USER" -cx "$PROGNAME"` -gt 1 ] && exit 1

Vagy valami ilyesmi.

Szerk.: lock file ott hagyást el lehet úgy intézni, hogy ha létezik a lock file, felolvasod belőle a pid-et, s utána megvizsgálod, azzal a pid-del fut-e program, s ha igen, annak az-e a neve, mint ami a script-edé. Ha nem, akkor felülírhatod a lock file-t a saját pid-eddel - vagy létrehozod, de ez ugyanaz -, s futhatsz tovább. Ha igen, akkor meg vársz, csak legyen sleep a ciklusban, vagy visszatérsz hibakóddal, attól függően, mi a cél.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

man flock

automatan felszabaditja a lockot, ha a script befejezodott vagy kilotte valami (=lezartak a lock fajlt)

--
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

Ezért még kapot a pofámba, de:
man systemd.unit (hely, minden unitra érvényes direktívák etc.)
man systemd.service (a valszeg simple vagy one-shot service)
man systemd.timer (az időzítőhöz)

Garantáltan egy példány fog belőle futni (ha a service már egyébként is fut, akkor a timer hatástalan), megmarad a lehetőséged, hogy az időzítőtől függetlenül (mondjuk systemctl-el, vagy bármi más függőségeként - pl. a default targetre, ha újraindítás után azonnal kéne futnia) futtasd garantáltan ugyanazzal a környezettel, etc.

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

dotlockfile - azt irja a manual, hogy NFS-en is jo

altalaban shell scriptnel:


LOCKFILE=/path/to/lock.file

if [ -f $LOCKFILE ]; then echo "mar futok, szevasz" ; exit 1; fi

date > $LOCKFILE

function finish {
rm -f $LOCKFILE
}

trap finish EXIT

# itt van, amit a script csinal
# ....

--
"nem tárgyszerűen nézem a dolgot, hanem a vádló szerepéből. Sok bosszúságot okoztak, örülnék ha megbüntetnék őket - tudom gyarló dolog, de hát nem vagyok tökéletes." (BehringerZoltan)

Elsőre én is ezt mondanám, de két baj van vele. Az EXIT (másnéven 0) trap SIGKILL esetén nem fog lefutni - és processzt még a kernel is lőhet le sigkill-lel. Másrészt kissé shell megvalósítás függő a dolog. (És értem én, hogy itt most korlátozva vagyunk, mert Linux-ról van szó, meg *valószínűleg* bash-ról, de a Debian bash -> dash váltása anno jól jelezte, hogy nem biztos, hogy érdemes nagyon mély Linuxizm és bashizm dolgokat beleépíteni egy megoldásba.) Pl. a pdksh már sima SIGINT (^C) esetén nem hajtja végre az EXIT trap-et. A FreeBSD-féle sh (ami ash-n alapul) szintén. Ezzel együtt is, ez a ráindulási problémát mindenképp megelőzi, hisz ha nem törlöd, akkor a lock miatt nem indul el :-)

A másik a race-condition, azt pl. bash esetén egy kicsit okosabb lokcfile létrehozással meg lehet oldani:


set -o noclobber
echo $PID > $LOCKFILE || { # ha már van, a noclobber miatt hiba
echo mar futok >&2
exit 1
}

Sajnos a noclobber (amit amúgy a C-shell-ben használtak először) nem standard, ash, dash, stb *tudtommal* nem támogatja (ksh tudja, mind a két féle)

Persze, elvileg hibás. Ugyanakkor az mkdir, lockfile parancsok már atomikusak, de a másik, hogy ha a feladat az, hogy cronból indított script-et kell figyelni, ott a versenyhelyzet 1 óra különbséggel áll fenn, tehát a probléma csupán elméleti marad.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Nagyon sokat szívtam ezzel annó. Még 1 órás különbségre is nehéz olyan jó megoldást adni, ahol egy ki tudja miért beragadt script-nél jól fogja vizsgálni hogy van-e már versenytárs - éppen azért, mert bárhol lehet a hiba.

Csak azt mondom hogy "jó" megoldást nehéz adni shellből önmagából.

én ezt úgy oldottam meg, hogy nagyjából tudom mennyi idő alatt kell végezzen, egy egyszerű dameon fut ami nézi a kritikus scripteket, hogy mióta futnak és ha elérnek egy kritikus időt (vártnál jóval tovább fut) akkor dob egy figyelmeztető levelet nekem, hogy ez és ez van. stdout/err-t célszerű loggolni, ezt probléma esetén dobja ftpre nálam levél után ... nyilván ez sem tökéletes megoldás, nekem amire kell bevált és megy :)

Azért az igényesség kedvéért tegyük hozzá, hogy több fent írt megoldás nem 100%-os, csak mondjuk 99.999% :). Lockolásnál két dolog számítana, egyrészt atomi legyen a lock művelet, másrészt automatikusan takarodjon el a lock bármilyen esetben. Tehát a "megnézem, hogy fut-e, és ha nem, elindítom" módszerben race condition van, és szerintem létezik olyan súlyos hiba, amikor mondjuk egy trap nem fut le a scriptben (SEGV/KILL?). Értem én, hogy ezeknek nagyon kicsi az esélye, de ha production rendszerről van szó, és nem akarod X hónap után azt látni, hogy már 17 példányban fut, vagy hetek óta nem indult el, akkor érdemes biztosra menni.

--

létezik olyan súlyos hiba, amikor mondjuk egy trap nem fut le a scriptben (SEGV/KILL?)

kerdes, hogy egy ilyen hibanak mi a valoszinusege, ill. egyaltalan mi okozza, tovabba megfelelo monitorozassal + beavatkozassal elkerulheto-e?

--
"nem tárgyszerűen nézem a dolgot, hanem a vádló szerepéből. Sok bosszúságot okoztak, örülnék ha megbüntetnék őket - tudom gyarló dolog, de hát nem vagyok tökéletes." (BehringerZoltan)

Számomra kérdés, hogy akkor nem egyszerűbb-e már azonnal az ezekre a problémákra kulcsrakész megoldást adó systemd-t használni (plusz egy service, hogy hogyan értesítsen és egy OnFailure direktíva a service-hez).

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

nem mindenhol van systemd, ill. itt most cron job-rol volt szo, nem service-rol, amihez meglep, hogy koze van(?) a systemd-nek

--
"nem tárgyszerűen nézem a dolgot, hanem a vádló szerepéből. Sok bosszúságot okoztak, örülnék ha megbüntetnék őket - tudom gyarló dolog, de hát nem vagyok tökéletes." (BehringerZoltan)

Annyiból van köze, hogy tud timer unitot is kezelni, amivel részben ki lehet váltani a cron-t. Értelme pedig annyi mindenképp van, hogy így ugyanazokkal az eszközökkel tudod lekorlátozni, hogy egy-egy cronból futó feladat mit érhet el, kinek a nevében fusson stb., mint a többi rendes service-nél.

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

hattooo.... ertem, hogy a systemd lassan mar a bongeszot, meg a kernelt is ki tudja valtani, de en maradnek a jo oreg Dillon cron-nal...

--
"nem tárgyszerűen nézem a dolgot, hanem a vádló szerepéből. Sok bosszúságot okoztak, örülnék ha megbüntetnék őket - tudom gyarló dolog, de hát nem vagyok tökéletes." (BehringerZoltan)

hat pedig ha jol tudom akkor flock epp atomic. (kiveve a lock convertalast). a lock pedig automatan felszabadul ha a leirot lezarjak (close hivas vagy a system egy kill utan)

es most kiprobaltam trustyn a man oldalon levo test scryptet, jol mukodott (a fajl ugyan ott marad, de az nem is lenyeg)

--
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

Ezért szeretek jobban mikrokontrollerre assembly-ben programozni. Azt, hogy valami atomikus legyen, könnyebben tudom biztosítani, pontosan tudható, mi az, s mi nem, mert én csinálom.

Mi erre a teljesen korrekt megoldás? Láttam a linkelt blogban az

if mkdir "/tmp/$LOCKDIR"; then

kezdetű megoldást. Az mkdir atomikus? Különben tetszik, mert ha az, akkor jó is lehet, nem előbb vizsgálunk, majd ha nincs lock, utána létrehozzuk azt, mert ez így nyilván nem atomikus. Az mkdir hibával jön vissza, vagy létrehozza a $LOCKDIR-t. Egyrészt nem tudom, mikor válthat taszkot a kernel, másrészt több magvas gépen egyszerre futhatnak kódok, harmadrészt nem tudom, a filerendszerben hogyan managelik az efféle ütközéseket, hogyan állnak ezek sorba.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Igen, ezt tetszőlegesen lehet bonyolítani, és 99, 99.9, 99.99, 99.999... százalékosan garantálni, hogy tényleg működik. De annak a fényében, hogy ez általános értelemben egy bonyolult, viszont legtöbb esetben kernel szinten már megoldott dolog, kb. fölöslegesen minden más próbálkozás, mert így szinte ingyen kapsz sok 9-est. Persze abba is bele lehet kötni, hogy a kernel sem 100%-os, de ott már akkor más bajok és vannak, és általában ezt praktikusabb figyelmen kívül hagyni.

--

lockfile -r 0 /var/run/azenjokislockfileom || exit 0
...
rm -f /var/run/azenjokislockfileom

/etc/rc.local -ból esetleg le lehet törölni de szerintem a /var/run -t törli rebootkor.
esetleg lehet a /tmp -be is pakolni

--
Gábriel Ákos
http://ixenit.com

Ott az rm a script végén, szóval jó esetben törli.
Akkor lenne gáz, ha elindul a script, van egy lockfile majd reboot és a lockfile ottmarad, akkor sose többet nem indul el a script.

Bár azzal még ki lehet egészíteni hogy mondjuk 4 óránként ránéz egy másik cronjob, ha talál régi lockfile-t (mondjuk 2 óránál régebbit) akkor a beragadt programot pusztítja és a lockfile-t is törli. Esetleg ír egy levelet az adminnak.

--
Gábriel Ákos
http://ixenit.com

Az rm-et láttam, de az SIGKILL, SIGSEGV ellen nem véd! :) Valami olyasmit érdemes, hogy a lockfile-ban pid is van, azt névre ellenőrizni, valóban fut-e a process, ami már lényegesen nagyobb valószínűséggel ad helyes eredményt, de ez sem 100 %-os.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE


exec 200<"$0"; flock -n 200 || { echo "$0 már fut"; exit 1; }

lehetne, de nem valószínű hogy 1-nél több program lesz egy gépen amit védeni kellene az újbóli ráindítástól, de ha mégis, akkor sem lehet nehéz feladat megjegyezni 2 számot :)

a másik, hogy mivel saját magát nyitja meg olvasásra, ami ugye biztos hogy létező, és olvasható file, mivel egyszer már megnyílt amikor elindult az sh-ban, nincs szükség kölön .lck file-ra amit vagy létre tudsz hozni, vagy nem, - jogosultsági problémák lehetnek stb, stb.. - vagy törlődik a script lefutása után, vagy nem. -pl hiba esetén stb stb.. -

Egyszer már leírtam: tfh a programod neve "lo". Ha ez a kis programocska olyan helyen van, amelyik könyvtár a PATH-ban is szerepel, és elérési útvonal nélkül indítod, akkor a $0-ban csak annyi van, hogy "lo" - márpedig ezt az átirányításnál a shell az aktuális munkakönyvtárban szereplő "lo" nevű fájlnak fogja érteni - következésképp nem biztos hogy létezik, vagy van hozzáférési jogod.

"bash scriptnév" esetben ez nem létező probléma, mert ilyenkor a shell nem keres a PATH-ban, tahát ahol ő megtalálja a fájlt, ott a program is megtalálja önmagát.
A bashizm jó ötlet, kár, hogy FreeBSD alatt kipróbálva a gyári sh és a csomagból telepített pdksh, ksh93 és bash is ugyanígy működik. (Ellenőrzéshez beletettem egy "ps -o command= $$" sort az echo elé, és így szépen látszik, hogy milyen shell fut (#! /usr/local/bin/ksh93 formában van a sheebang, és ez alapján lassan kezdem érteni és azt hinni, hogy ennek tényleg így kell lennie :-( )

Közben megnéztem, és nem bash-feature, hanem a shebang-sort interpretáló OS kód csinálja, hiszen annak kell paraméterként átadnia a futtató bináris számára, hogy hol is van a script, amit futtatnia kell. Itt pedig nyilván nem működne az, hogy "majd az a program interpretálja a PATH-t", mivel az egy mezei közönséges paraméterként kapja meg a script nevét.

Szóval egyetértünk: ez így jó, így van kitalálva, máshogy nem is tudna működni.