Sziasztok, szeretnék egy kis pythnon scriptet csinálni, ami indít egy basht, monitoroz egy processzt, ami ha leáll, kilövi a bash-t. Sajnos elég furán viselkedik nekem, ami valószínű pont az elvárt viselkedés olyannak, aki ért a témához.
from subprocess import * import signal import time def main(): while True: time.sleep(1) ret = run("/usr/bin/bash --norc -i", shell=True) if ret.returncode == 0 or ret.returncode == signal.SIGINT: break else: print("Restart") main()
Normál kilépésnél végezne a script, de ha kívülről adok neki egy SIGTERM-et, akkor nagyon fura dolgok történnek.
így néz ki a processz tree:
xx 58365 0.0 0.0 8548 4992 pts/3 Ss 18:27 0:00 /bin/bash xx 59044 0.1 0.0 18148 10112 pts/3 S 18:33 0:00 \_ python3 pyterm.py xx 59045 0.0 0.0 2580 1536 pts/3 S 18:33 0:00 \_ /bin/sh -c /usr/bin/bash --norc -i xx 59046 0.0 0.0 7228 3840 pts/3 S+ 18:33 0:00 \_ /usr/bin/bash --norc -i
Itt ha másik terminálból kiadok egy kill -SIGTERM 59045 parancsot, a processz leáll, viszont az újonnan elinduló bash stdin/stdout-ja már nem megy, mintha teljesen a háttérben futna.
Bár érdekelne hogy lehet szépen visszavenni az stdin és stdout felett az irányítást, bármi ötletnek örülnék arra vonatkozóan, hogy lehetne ezt szépen kezelni. Fontos lenne hogy scriptből indítom az új bash-t mert a paraméterezése az minden esetben más, a monitorozott processz függvénye.
- 390 megtekintés
Hozzászólások
Jótanács: Ha pájtonban írod, akkro írd pájtonban, ha shell scriptet írsz, akkor azt írj.
- A hozzászóláshoz be kell jelentkezni
Én is pont ezt akartam írni. Simán Bash-ban írnám, ha már Bash. Amiért nem tudok segíteni neki, az az, hogy nem áll össze, hogy melyik folyamatot figyeli, az elindított Bash-t, vagy valami másikat? Erről az enotty-ról sem tudom micsoda, hogy jön a képbe.
Illetve azt se értem, hogy elindít egy interaktív Bash shellt, de azzal mire megy? Magában fut, de az nem futtat semmit, ha nem tty-on, vagy eleve valami másmilyen konzolban fut, hanem pl. X/Wayland alatt, akkor semmi nem látszik belőle, mert a háttérben fut, nem egy terminálban. Sokkal részletesebb leírás kéne, hogy mi a cél.
“Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”
- A hozzászóláshoz be kell jelentkezni
Bocsi, újraolvasva tényleg hülyén fogalmaztam meg a működést. Úgy nézne ki a program, hogy:
1. Indítok egy bash-t (pythonból), nevezzük mondjuk CHILD_BASH-nek
2. A python script fut tovább a háttérben, és monitoroz egy harmadik processzt, ami már fut, legyen ez TARGET
3. User az dolgozgat a CHILD_BASH-ben, abban van mindenféle előre beállított env változók, mount namespacek, stb. tehát elő van készítve
4. Ha TARGET leáll, kilövöm CHILD_BASH-t. TARGET teljesen független tőlünk, nincs ráhatásunk
5. Ha TARGET újraindul, új CHILD_BASH-t indítok a usernak, amiben dolgozhat
- A hozzászóláshoz be kell jelentkezni
Szeretném reprodukálni, de nem tudom. Milyen python verzió ez?
Most hirtelen 3.12-n és 3.6-on nézem, de valamiért egyiken sem látom megjelenni a középső 'sh -c' -t, miközben meg látszik, hogy használja a shell=True-t, mert ha falsera kapcsolom, akkor tök jogosan közli, hogy nem jó az a filenév.
Most kissé késő van, de van a subprocess.py-ban egy rész, ami mintha ezt implementálná újra, hogy ha valami magic detector _USE_POSIX_SPAWN nem igaz, lehet nálam valamiért ez történik, cserébe a bash amit indít, pont leszarja a sigtermet. Sigkillre cserében a szülő python process stopped állapotba kerül, ami egyébként hasonló azzal, amit te tapasztalsz a szülő shell ki is irja szépen, hogy stopped, az fg előhozza. Ahhoz most késő van, hogy feltúrjam, hogy hogy kéne ehhez beparaméterezni a POPENT.
---
Illetve elárulnád, hogy a topic címben szereplő ENOTTY hol jön?
---
Az is szinte biztos, hogy a SIGINT kezelés így nem jó, mert "A negative value -N indicates that the child was terminated by signal N (POSIX only)." te meg nem negatívot nézel.
---
Továbbá XY probléma szagot érzek, egyrészt valóban nem annyira jó ötlet shellezni pythonból, ha nem muszáj, legalább azt meg lehetne oldani, hogy direktben hívd a valódi processzt, másrészt ha muszáj is abból, amit írsz, valójában nincs itt szükség interakcióra az stdin/stdouttal, szóval erősen gyanús, hogy ha ezeket szépen elteszed PIPEal, és nem interaktív basht futtatsz, akkor ezek nem fognak előjönni.
Szóval mi is a feladat?
- A hozzászóláshoz be kell jelentkezni
Köszi a tippeket főleg a negatív signal értékes bug ami fölött nagyon könnyen átsiklottam.
Az ENOTTY az strace-ből jön, ott látok ilyen errorokat, mikor indítja a bash-t.
Ahogy fentebb is meg te is javasoltad, kipróbáltam bashből indított bash-el. Ez sokkal jobb, visszakapom az stdin-t. Az strace kimenete alapján itt mikor kilépek a gyerek bash-ből, fcntl-el meg ioctl-ekkel elkezdi piszkálni a 0-ás fd-t, tehát az stdinnel történik valami.
Ami biztos, hogy akár python akár bash, ez egy interaktív shell kéne legyen, nem háttérben futó bash script. Ott elég lenne PIPE-olgatni a ki és bemenetet, de nem ez kellene.
Amit meg kellene oldanom, az egy interaktív shell egy network/process névtérben, ami megszűnhet miközben dolgozok benne. Például mintha interaktív konzolban lennék egy futó docker konténerben, amit kirántanak alólam, de ha újraindul akkor visszacsatlakoznék (tudom hogy nem túl életszerű példa).
- A hozzászóláshoz be kell jelentkezni
Mazsoláztam egyéb hszekből is :)
Köszi a tippeket főleg a negatív signal értékes bug ami fölött nagyon könnyen átsiklottam.
Én még megnézném, hogy egyáltalán az lesz-e a return kód, vagy itt is a bash nyer a 128+signo return valueval
Ahogy fentebb is meg te is javasoltad, kipróbáltam bashből indított bash-el.
Én nem ezt javasoltam. Vagy legalábbis nem volt célom. :) Nekem a te kódoddal a következő történik:
$ pstree -ap 2706
yakuake,2706
├─fish,32631
│ └─python3,743140 ./hup.py
│ └─bash,743149 --norc -i
Szóval nincs meg a köztes sh -c.
A kill 743149 hatására nem történik semmi, kill -9-el
bash-5.2$ Restart
fish: Job 1, './hup.py' has stopped
fg előhozza.
Ami biztos, hogy akár python akár bash, ez egy interaktív shell kéne legyen,
Az interkatívot értem, a mindegy hogy python vagy basht nem. Mire kell az interaktivitás (mármint lehet, hogy érdemes megnézni hogy van-e ilyen interkatív funkció pythonban)
Amit meg kellene oldanom, az egy interaktív shell egy network/process névtérben, ami megszűnhet miközben dolgozok benne. Például mintha interaktív konzolban lennék egy futó docker konténerben, amit kirántanak alólam, de ha újraindul akkor visszacsatlakoznék (tudom hogy nem túl életszerű példa).
Nem tiszta, hogy mi szűnhet meg. Mármint ha megszűnik a network/process namesace, akkor abba te már nem fogsz visszacsatlakozni, mert megszűnt, és szerintem visz magával mindent is.
Ezzel működik, azzal a kitétellel, hogy SIGHUP-al kell bezárnom a gyerek bash-t. Minden más signalt ignorál (ezt szépen leírja a manpage) vagy ami rosszabb, a gyerek ignorálja de a szülő megkapja.
Azt is leírja a manpage, hogy megkapja a szülő signal handlerjeit a futtatott processz.
A python script fut tovább a háttérben,
Lehet ez megvan máshol, de az a run blokkolni fog, szóval még kell ezen valamit csinálnod, és nem biztos, hogy nagyon pretty lesz egy interaktív terminállal.
Illetve azt is nézegesd meg, hogy megfelelő signal handlerek híján a python processz csapkodása nem hagy e zombikat széjjel (szerintem nem, de pl kb az összes random bash entrypoint dockerben tud ilyet)
- A hozzászóláshoz be kell jelentkezni
Ahogy látom nálad fish van, nem bash. Ez nem tudom lényeges különbség-e, másképp viselkedik a shell=True ha a python-t bash-ből indítod?
Az interaktivitás nem korlátozódik semmire, minden kellene amit egy sima bash-ben, akár SSH-ban végre tudsz hajtani. Esetleg azt lehetne, hogy MINDENT ami inputban jön, küldöd be Popenbe és mint ami kiesik belőle azt vissza stdout-ra. Ettől kicsit félek, pl. egy screen vagy ncruses alkalmazás tuti kinyírná ezt a megoldást.
Lehet ez megvan máshol, de az a run blokkolni fog, szóval még kell ezen valamit csinálnod, és nem biztos, hogy nagyon pretty lesz egy interaktív terminállal.
Igen, van egy thread ami ezt csinálja, az nem ír se nem olvas terminálból így azzal nincs gond. Szóval a run blokkol, de háttérben egy thread van ami kilövi a run-ban futó bash-t ha a monitorozott processz lehal.
Nem tiszta, hogy mi szűnhet meg. Mármint ha megszűnik a network/process namesace, akkor abba te már nem fogsz visszacsatlakozni, mert megszűnt, és szerintem visz magával mindent is.
Persze, az kuka és ha abba fut a bash az is kuka. Ezt akarom megcsinálni. A visszacsatlakozás nem konkréten ugyan abba történik (mások lesznek a namespace ID-k, stb.) hanem ugyan azzal a névvel de más namespacebe. Egész konkrétan egy mininet nevű hálózatemulátor virtuális hosztjaira lépek be ezzel és a mininet van olyan rendes, hogy ha van egy router r1 névvel, azt mindig annak fogja elnevezni is processz listában is meg lehet találni mi a processz ID-ja (amiből meg egyértelmű milyen namespaceket mountol).
Végül az alábbi kód nekem good enough, ha mininet újraindul, akkor nyit új bash-t és tudom folytatni a munkát. Ez lényegében a signal maskos kis kódrszlet kiegészítve a mininet figyeléssel:
- A hozzászóláshoz be kell jelentkezni
A singnalos vonalon elindulva keresgéltem és olvasgattam. Már majdnem működött a dolog, csak az történt amit írtál, a szülő process backgroundba kerül, de elindítja az új bash-t. Ez ha jól értem azért történik, mert kap bash-től egy SIGTTOU és SIGTTIN signalt, mert háttér processz ugyan azt a tty-t szeretné használni, mint a pythonos szülő. Egy nagyon jó poszt erről: http://curiousthing.org/sigttin-sigttou-deep-dive-linux
Így néz ki most a kód:
from subprocess import *
import signal
import time
def main():
signal.signal(signal.SIGTTOU, signal.SIG_IGN)
signal.signal(signal.SIGTTIN, signal.SIG_IGN)
while True:
time.sleep(1)
ret = run("/usr/bin/bash --norc -i -m", shell=True)
if ret.returncode == 0 or ret.returncode == -signal.SIGINT:
break
else:
print("Restart")
main()
Ezzel működik, azzal a kitétellel, hogy SIGHUP-al kell bezárnom a gyerek bash-t. Minden más signalt ignorál (ezt szépen leírja a manpage) vagy ami rosszabb, a gyerek ignorálja de a szülő megkapja.
- A hozzászóláshoz be kell jelentkezni