Készítettem egy scriptet, ami az aktuális mappában létrehoz egy lock fájlt, és egy -TERM szignálig nyitva is tartja, majd lezárja és letörli. Az elindított script megpróbálja létrehozni a lock fájl. Ha sikerül neki, kiírja a PID értékét, amit a kill parancsnak meg kell adni a zárolás feloldásához, és visszatér 0 értékkel. Ha TIMEOUT másodpercig nem sikerül, akkor visszatér 1 értékkel.
Parancssorból futtatva jól működik, de egy másik bash scriptből hívva a scipt futását megállítja. Pontosabban, ha úgy hívom meg, hogy a visszaadott PID értékét elteszem egy változóba, akkor fagy csak be. Azaz V=$(script) vagy V=`script` soroknál. A lock fájl ugyan létrejön, de a script futása megáll legelső futtatásra is.
#!/bin/bash
TIMEOUT=3
LOCKFILE="./file.lock"
function wait_for_signal() {
trap "rm $LOCKFILE" SIGTERM
until false ; do
sleep 1
done
}
touch $LOCKFILE
exec {FD}<>$LOCKFILE
if ! flock -x -w $TIMEOUT $FD; then
exit 1
else # Lock acquired
wait_for_signal &
echo $!
exit 0
fi
Megírtam ez a scriptet perl-ben is, a perl saját lock-jával és forkjával, és pont ugyanígy működik.
Ezek szerint nem értem pontosan a lock és/vagy a fork működését, vagy valamit nem tudok.
Tudtok segíteni benne, mi a hiba, mitől fagy be egy scriptbe ágyazva a programom, és hogyan tudnám elérni, hogy scriptből hívva ugyanúgy jól működjön, mint parancssorból?
( Kérem az AI-copy válaszok mellőzését, azon már túl vagyok. )
Hozzászólások
Annyit még megpróbáltam, hogy C-ben is elkészítettem ugyanezt a kódot ... és abban is ugyanígy viselkedik.
Ez az FD ez micsoda benne, hol van definiálva? Valahogy nem értem. Ilyen V=$(script) sort se látok benne.
Amire gondolni tudok, hogy az if feltétel mindig igaz, és nem hajtódik végre az else ág, vagy az a gond, hogy a lockfile-nál relatív utat adtál meg, nem abszolútat. Elsőre megnézném, hogy milyen hibakóddal lép ki a script, mikor azonnal megáll.
“The world runs on Excel spreadsheets.” (Dylan Beattie)
Az FD a fájlazonosító létrehozása - gondolom. Bash-ban nem vagyok olyan jó, egy példascriptben így volt. Más nyelveken ugye ott a fájl azonosító változója szerepel.
A V= pedig már ennek a scriptnek (mondjuk lock.sh) a felhasználásakor jelenik meg. Ha egy scriptben a "V=`lock.sh`" vagy "V=$(lock.sh)" sor szerepel, akkor a script lefagy - gondolom, vár valamire.
Érdekes módon, ha a felhasználó scriptem egy fájlba irányítja a PID-et (azaz a lock.sh script kimenetét), akkor rendben lefut. Vagyis ezzel a kóddal nincs baj:
Ilyen formában rendben lefut.
Szerintem kicsi {fd} akar lenni, bash 4.1 -től feature (nyit egy új file descriptort amibe tudsz írni, olvasni, de csak emlékeim vannak róla, soha nem próbáltam :D)
// Happy debugging, suckers
#define true (rand() > 10)
Bash script debugoláshoz hasznos lehet, ha a sor elején nem szimplán /bin/bash áll, hanem /bin/bash -x
En úgy emlékszem hogy bash-ban amikor sub-shellt indítasz átadja/másolja a nyitott FD-ket és ez viszi magával a lock-ot is. De már régen kellett ilyet nyomoznom és nem is biztos hogy most releváns.
A lock file-t ne te hozd létre kézzel ha FD-re lockolsz és nem állomány névre.
Én valami ilyesmivel próbálkoznék, csak fejből írom mert sietnem kell:
Szerintem érdemes lenne megnézni a másik bash scriptet is ahonnan próbálod ezt hívni. Problémás lehet még a path is. És persze triviális a dolog, de a jogosultságok rendben vannak?
Egyébként várom zahy professzor urat, hogy elmagyarázza mi is történik. Imádom a fazont! Hihetetlen okos ember!
― Philip K. Dick
Pontosabban, ha úgy hívom meg, hogy a visszaadott PID értékét elteszem egy változóba, akkor fagy csak be. Azaz V=$(script) vagy V=`script` soroknál.
Itt pontosan ezt most igy hogy? Elinditasz valamit a hatterben, akkor annak a PID-je a $!-ban lesz. Azt egy x=$! hivassal le tudod menteni izibe. Nem kell $(...) vagy `...`. Vagy azzal mit mentesz le? Ezutobbiak kulon processzt inditanak, ami ugye be tud kavarni mint azt feljebb is irtak a tobbiek.
Egy egyszerű minta script, ami a hibátlanul fut le:
Ugyanez úgy, ahogy szeretném használni, de ahogyan már befagy:
Természetesen ennél összetettebb kódban szeretném használni, de ez a legegyszerűbb környezet, ahol jelentkezik a hiba.
Á! Ertem. Az első esetben a ./lock.sh a mintaszkripted (parent) subprocesse (child), a masodik esetben meg egy suprocessz subprocesse. Vsz itt tortenik valami furmanysag. Ezelobbi meg okes, ezutobbi mar tul sok reteg egymason.
Szerk: illetve az is van itten hogy a wait_for_signal fuggveny az ugye szinten subprocessz lesz, ami ugye fut a hatterben miutan a ./lock.sh lefutotott. A ./lock.sh az ugye leall, de mivel a wait_for_signal az orokolte az FD-ket a ./lock.sh-tol ezert ezutobbi leallasa utan az meg ugy ott marad valahogy. Ha ekoze bekerul meg egy parent-child szint a `...` vagy $(...) miatt akkor ott mar nincs ki aki megtartsa azt a processzt igy az feltehetoen zombiva valik. Ezt is probald meg kinyomozni (sima `ps -A xuf` vagy hasonlo modon nezd meg a process tree-t).
A megoldas valami ilyesmi lehet:
( wait_for_signal </dev/null 2>&/dev/null & ) &
csak ugye ekkor a neked nem a gyerekprocessz pid-je kell hanem a gyerekprocesz gyerekenek a pid-je.
Barhogyis, ez a "demonizalas" (teljes levalasztas a futo terminaltol) eleve onmagaban egy erdekes/bonyolult dolog. Ugye ahhoz hogy tenyleg a hatterbe keruljon ez a processz, ahhoz le kell hogy szakadjon mindentol - de pont azaltal nem tud(sz) leszakadni hogy az ilyen stdin/stdout jellegu kapcsolatokat megsem szakitod meg mert informaciot akarsz azon keresztul kinyerni (esetedben az `echo $!`felhasznalasaval).
Kipróbáltam, de "kétértelmű átirányítás" hibaüzenettel visszautasította. Mivel az átirányításokkal volt baja, azokat kihagytam. Úgy lefut hiba nélkül, de ugye rossz PID értéket ír ki.
Módosítottam a sort:
Így már jó PID értéket ír ki, és magában jól is működik, de a scriptből történő meghívás esetén továbbra is befagy. :(
Amúgy meg nem értem: a szülő processz elindítja a wait_for_signal gyermek ágat, kiírja a PID-jét, és kiszáll.
Innentől ez a processz már nincs a memóriában. Miért baj, ha ezt a szülő processzt egy másik processz hívja meg?
Kipróbáltam, de "kétértelmű átirányítás" hibaüzenettel visszautasította.
Ah, igen vsz gyorsan irtam: `</dev/null >/dev/null 2>&1` kell... ez lenyel mindent (stdout, stderr) es nem is ad semmit at (stdin).
Innentől ez a processz már nincs a memóriában. Miért baj, ha ezt a szülő processzt egy másik processz hívja meg?
A gond az informaciocsere: ha valamilyen processzt ugy igazan a hatterbe akarsz tenni akkor teljesen le kell valasztanod a szulotol. Nyilvan minden processznek van egy szuloje, amitol orokli az fd-ket. A hatterben az tortenik hogy a kernel forkolja a szulot, lesz egy szulo + gyerek, megosztott/kozos fd-kkel. Ezutan, de meg az execve() hivasa elott (ezutobbi csereli le a gyerek processz image-jet egy masik image-re) mind a szulo, mind a gyerek "gatyaba razza" az fd-ket, azaz lezarja azokat amik biztos nem kellenek neki. Itt a szulo-gyerek kapcsolatban lehetnek pipe-ok, lehetnek atiranyitasok meg orokolheti a terminalt is meg ugy atlaban mindent. Szoval a fork() elott sok open(), dup2(), pipe() hivas is tortenik, a fork() utan pedig mind a szulo, mind a gyerek lezarja (close()) azokat az fd-ket amikre nincs szuksege. Idealisan, mikor valamit a "nagyon a hatterbe teszel", minden kapcsolatnak meg kell szakadnia, azaz a gyerek csak olyan fd-t tarthat nyitva amit, illetve aminek a masik veget(!) nem fogja a szulo[*]. Raadasul a processz tree-t is meg kell szakitanod, ezert mind a fenti hatterbe vevesnel amit irtam, mind pl a daemon() hivasnal ket fork() is van egymas utan, es valojaban nem a gyerek hanem az "unoka" kerul hatterbe (ugyhogy a gyerek az teljesen meghal, leall, igy az init processz gyereke lesz a processz tree-ben). Lasd `man 3 daemon`, ott leirja a folyamatot eleg jol.
Viszont ha minden kapcsolat megszakad akkor hogyan is adod at azt az infot hogy "mi is a PID-je" a gyereknek? A pidfile egyebkent pont erre (lenne) jo, vsz nem veltetlen hogy ez az egyik sztenderd megoldas. Es akkor a filerendszer tolti be az interprocessz kommunikacios felulet szerepet (merthogy a sztenderd UNIX fd-alapu dolgok nem mukodnek). Egyebkent csinalhatod akar ezt is, mmint pidfile-t. Akar a lockfile is tartalmazhatja a PID-et, es akkor ugy(?). Nem tudom, nyilvan ez a sajat problemadtol, feladatodtol fugg hogy mit is szeretnel valojaban megoldani. En egy kicsit konzervativabban allnek egyebkent hozza, azaz a lock acquire illetve lock release ugyanabban scope-ban (ezesetben bash szkriptben) tortenjen. Ez azert egyszerusiti is a dolgokat.
[*] szerk: ez lehet hogy nem teljesen igaz, mert vegulis ahogy egy socket()-et utolag is meg tudsz nyitni ket fuggetlen processz kozott (lasd: internet, localhost, unix domain, stb), ugy akar egy socketpair() ket veget orokolheti a szulo meg az unoka. Csak az eleg ritka use case hogy daemon()izalnal egy processzt, nagyon a hatterbe teve, process tree-ben levalasztva, stb, de meg elotte azert fenntartasz egy kapcsolatot a szulovel. Ami egyebkent barmikor leallhat. Ilyet meg nem lattam elesben sehol sem.
Még most sem sikerült jobban végiggondolnom, de egy szimpla nohup a háttérprocessz indításakor, és utána egy disown nem segít a helyzeten?
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?
Köszönöm, úgy tűnik, meglett a megoldás.
Próbálgattam a nohup és disown parancsokat, és egyszerre csak az elvárásoknak megfelelően működött. Ekkor elkezdtem kitörölgetni részeket, hogy kiderüljön, mitől javult meg. Így végül sem a nohup, sem a disown parancs nem kellett.
A lényeg, hogy a gyermekfolyamat indításánál mind az input, mind az output át legyen irányítva a /dev/null-ra. (Gondolom, bármilyen fájl jó lenne itt.)
Az elvárásoknak megfelelőnek tűnő működés még nem volt az igazi, mivel a SIGTERM-re a fájl ugyan törlődött, de a processz nem zárult le, így az "rm $LOCKFILE" után még bekerült egy exit is.
Így most jónak tűnik. A lock.sh script jelenleg így néz ki:
A tesztelő script pedig nem változott:
Köszönöm a segítségeket!
(Majd ha ráérek, ránézek újra - bár azt látom, hogy apal elindult valami használható irányba. Próbálom kitalálni, hogy én hogy csinálnám.)
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?
Feltennék egy ünneprontó pótkérdést: Mi volt a feladat, amit meg kellett oldani? (Csak a saját szavaiddal!)
Számomra az, hogy lefusson beágyazva is a zárolás. Ehhez szerettem volna megtudni, hogy miért nem fut le. Most már tudom: az átirányítások hiánya miatt. Erre voltál kíváncsi?
Talán, bár nekem olyan bonyolultnak tűnt, hogy nem is mertem beleszólni. ;) A flock-ot használom szorgalmasan. Ideírom, hátha ötletet adok:
A példa sok fork exkluzív lockolását oldja meg. A lockon belül a commands fér hozzá a közös objektum(ok) hoz, és ez a része a programnak mindig forkolva fut.
Ehhez van egy $LOCKFILE, amit megnyitunk egy FD-vel - hogy a továbbiakban tudjuk mi az FD értéke. Ebben a megoldásban minden művelet/objektum (csoporthoz) kell egy lockfile. A vázlatosan ábrázolt függvény nálam tobb száz példányban futhat.