A runit működése

A Unix/Linux szerverek üzemeltetése wikiből
(Változatok közti eltérés)
a (run scriptek írása: apache2-template frissítve)
a (Logoló run script: a tryto mindenképpen jó bele, nem baj, ha nem konfigurálható ki)
724. sor: 724. sor:
 
ROTSIZE=50000
 
ROTSIZE=50000
 
ROTNUM=50
 
ROTNUM=50
POSTPROC="tryto -pP gzip -9"
+
POSTPROC="gzip -9"
   
 
# rendszerszintű svlogd-defaultok
 
# rendszerszintű svlogd-defaultok
741. sor: 741. sor:
 
s$ROTSIZE
 
s$ROTSIZE
 
n$ROTNUM
 
n$ROTNUM
!$POSTPROC
+
!tryto -pP $POSTPROC
 
EOF
 
EOF
 
fi
 
fi

A lap 2007. október 25., 21:55-kori változata

A daemontools szabad reimplementációja; majdnem minden, ami igaz a runitre, igaz a daemontoolsra is, és fordítva. Az ötlet az, hogy juttassuk érvényre a Unix-filozófiát: egy program egy dolgot csináljon, lehetőleg jól és hatékonyan, de legyen képes más programokkal együttműködni. A runit csomag egy-egy programja tipikusan kevesebb, mint ezer sor C-kódból áll, és sosem nő a memóriafoglalása. Ha a runitot statikusan linkeljük a dietlibc-hez, akkor az 1-es processzként futó program csak kb. 20KB-ot vesz igénybe. A BSD-licenchez hasonló licenc alatt érhető el.

Az initet és az initscripteket váltja ki, valamint segít a naplózás megszervezésében. Be lehet vezetni kicsit, nagyon, és teljesen.

Ha kicsit vezetjük be, akkor megmarad a System V init, de mellette kapunk:

  • Szolgáltatásmenedzsmentet. A szolgáltatások
    • tiszta környezettel indulnak
    • újraindulnak, ha kilépnek
    • opcionálisan megbízható naplózást kapnak
      • naplóüzenet nem vész el naplózás közben
      • méret alapján rotált logok
      • utófeldolgozás lehetősége
      • akkor is működik, ha a szolgáltatás chrootban fut
      • a logolás és a szolgáltatás egymástól függetlenül újraindítható/leállítható
    • kiválasztott felhasználók sudo nélkül is menedzselhetik őket
    • könnyedén lekérdezhető a státuszuk
    • könnyedén küldhető nekik signal
    • könnyedén biztosíthatjuk ezeket a tulajdonságokat a felhasználók saját "szolgáltatásainak" - pl.:
      • screen, benne irssi
      • eggdrop bot (sajnos át kell írni, mert mindenképpen a háttérbe akarja rakni magát -- update 2007. január: úgy tűnik, ez szerencsére már nem igaz, az újabb verzióknak van olyan kapcsolója, amitől az előtérben maradnak)
      • fetchmail
      • signify
      • akár apache vagy egyéb

Ha nagyon bevezetjük, akkor emellett még az initet is kiváltjuk, de a rendszerindulást továbbra is a hagyományos initscriptjeink vezénylik le, csak az inittab válik feleslegessé. Emellett gyorsabbá válik az indítás és a leállítás, mert sokmindent egyszerre fog csinálni a runit (azért nem kell azzal foglalkoznia, hogy minek mi után kell jönnie, mert ezt az intelligenciát nekünk kell megavlósítanunk a run scriptekben - l. később).

Ha teljesen bevezetjük, akkor az rcS után már csak a runit által menedzselt szolgáltatásaink futnak, vagyis az "rc 2" már nem kell, hogy lefusson.

Tartalomjegyzék

1 Architektúra

  • Minden szolgáltatáshoz tartozik egy könyvtár (directory), amiben:
    • Megvan az a program (általában script), ami az adott szolgáltatást elindítja és futtatja (és run névre hallgat); addig nem léphet ki, amíg a szolgáltatás nem lépett ki
    • A runit létrehoz egy supervise könyvtárat - l. később
    • Lehet egy down nevű file, ami azt jelzi, hogy a szolgáltatás normális állapota az, hogy nem fut
    • Lehet egy log nevű könyvtár, ha a szolgáltatáshoz naplózást is szeretnénk - l. később
    • Lehet egy check nevű program, ami 0-ás visszatérési értékkel lép ki, ha a szolgáltatás működik (ami nem azonos azzal, hogy fut), egyébként nullától különbözővel
    • Lehet egy finish nevű program, amit abban az esetben kell lefuttatni, ha a run kilép

Ezeket az ún. service directorykat általában a /service alá symlinkeljük be, ha az adott szolgáltatást futtatni akarjuk.

A /service könyvtárat figyeli a runsvdir, és minden alkönyvtárra indít egy runsv folyamatot. Erről rövidsen lesz szó.

2 Telepítés

  • Ajánlott verzió: legalább 1.6 (bár a korábbiak is használhatók)
  • Debianhoz és Ubuntuhoz van szép runit csomag, ez csak felrakja a runitot, de nem cseréli le az initet - mindenféle körültekintés nélkül telepíthetjük
    • Van runit-run csomag is, ami le is cseréli az initet - csak óvatosan: reboot után, ha nem vigyázunk, csak a konzolról fogunk tudni belépni, mert nem indul el az sshd
  • Nyilván fel lehet rakni forrásból is
    • Alapból a djb-féle /package, /command könyvtárstruktúrát használja, aminek ugyan van előnye, de azért eléggé eltér az FHS-től

3 A runit részei

A runit csomag számos programot tartalmaz; mindegyik valamilyen viszonylag kicsi részfeladatot lát el, és emiatt egyszerű a kódja.

Tekintsük át ezeket felülről lefelé haladva a processz-fában, amely az init lecserélése esetén pl. így nézhet ki:

runit-+-events/0
[...]
      |-runsvdir-+-runsv-+-dnscache
      |          |       `-svlogd
      |          |-runsv-+-run---sleep
      |          |       `-svlogd
      |          |-runsv-+-qmail-send-+-qmail-clean
      |          |       |            |-qmail-lspawn
      |          |       |            `-qmail-rspawn
      |          |       `-svlogd
      |          |-4*[runsv-+-socklog]
      |          |          `-svlogd]
      |          |-runsv---dd
      |          |-runsv---klogd
      |          |-runsv---cron
      |          |-runsv-+-munin-node
      |          |       `-svlogd
      |          |-8*[runsv---getty]
      |          |-runsv-+-ntpd
      |          |       `-svlogd
      |          |-runsv---sshd---sshd---zsh---screen
      |          |-runsv-+-smartd
      |          |       `-svlogd
      |          |-runsv---mdadm
      |          |-runsv---pound---pound---2*[{pound}]
      |          `-runsv-+-svlogd
      |                  `-tcpserver
[...]

3.1 runit-init

Ez a rendkívül egyszerű program, amely a /sbin/initet váltja ki, mindössze 76 sor hosszú.

Ha 1-es a process ID-ja (vagyis ő az init), akkor elindítja (execve() hívással) a runitot.

Ha más a process ID-ja, akkor úgy viselkedik, mint a telinit, tehát mintha runlevelt akarnánk váltani, de csak a 0-ás és a 6-os runlevelt ismeri.

  • 0: rendszerleállítás.
    1. Létrehozza a /etc/runit/stopit file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
    2. Futtathatóvá teszi
    3. Elveszi a jogokat a /etc/runit/reboot file-ról
    4. CONT signalt küld az 1-es processznek (ami remélhetőleg a runit, és ettől majd elkezd leállni)
  • 6: újraindítás
    1. Létrehozza a /etc/runit/stopit file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
    2. Futtathatóvá teszi
    3. Létrehozza a /etc/runit/reboot file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
    4. Futtathatóvá teszi
    5. CONT signalt küld az 1-es processznek (ami remélhetőleg a runit, és ettől majd elkezd leállni)

3.2 runit

Ez a program való arra, hogy 1-es PID-del fusson és az init feladatait ellássa, vagyis:

  • elindítsa a rendszert,
  • leállítsa a rendszert és
  • a kettő között gondoskodjon a szolgáltatások futtatásáról.

Ha init helyett a runit indul, akkor a /etc/runit/1 nevű scriptet indítja el először. Ennek a tartalma lehet pl. ez (a runit-run csomagban is ilyesmi van):

#!/bin/sh
# system one time tasks

PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/bin/X11
export PATH

/etc/init.d/rcS
/etc/init.d/makedev start
/etc/init.d/systune start
/etc/init.d/ud start
/etc/firewall/firewall start
/etc/init.d/rmnologin start
/etc/init.d/xend start
/etc/init.d/xendomains start

touch /etc/runit/stopit
chmod 0 /etc/runit/stopit

Az 1-es script tartalmazza a rendszerindításkor kiadandó parancsok listáját, emiatt nem hordozható korlátozás nélkül.

Ne feledjük, hogy a beállított és exportált környezeti változókat a script gyermekfolyamatai örökölni fogják.

Vegyük észre, hogy a fenti scriptben használhattunk volna rc.d mechanizmust is a szolgáltatások elindítására, ami

  • kicsit nehezebben átlátható,
  • egy picit lassúbb, cserébe
  • modulárisabb és
  • könnyebb automatizálni a karbantartását - de erre nem biztos, hogy van igény.

Konkrétan írhattuk volna akár azt is, hogy

[...]
/etc/init.d/rcS
/etc/init.d/rc 2
[...]

A /etc/runit/1 után - micsoda meglepetés - a /etc/runit/2 jön:

#!/bin/sh

PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin

exec env - PATH=$PATH \
runsvdir -P /var/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'

Innentől, amig a runsvdir ki nem lép, monitorozza a szolgáltatásainkat és fut a rendszer.

A -P kapcsoló hatására saját processzcsoportban indít el minden szolgáltatást. A "log: sok pont" egy olyan "helyet" hoz létre, ahová a szolgáltatások naplóüzeneteket írhatnak akkor is, ha sem a diszk, sem a hálózat nem elérhető - nevezetesen a runsvdir processz nevébe.

Láthatjuk, hogy a 2-es script lényegében semmilyen rendszerspecifikus dolgot nem tartalmaz, hordozható.

Ha kilép a runsvdir, kilép a /etc/runit/2 is; ha a visszatérési értéke 0 volt, elindul a /etc/runit/3, ami a leállításért felelős.

Ha nem 0 volt a 2-es script visszatérési értéke, akkor a runit újra elindítja.

A 3-as script tartalma például a következő lehet:

#!/bin/sh
exec 2>&1

PATH=/sbin:/bin:/usr/sbin:/usr/bin

LAST=0
test -x /etc/runit/reboot && LAST=6

echo 'Waiting for services to stop...'
sv -w196 force-stop /var/service/*
sv exit /var/service/*

echo 'Shutdown...'
/etc/init.d/rc $LAST

Elvileg a 3-as script is lehetne rendszerfüggő, mivel a rendszer leállításával kapcsolatos parancsokat kell benne felsorolnunk, azonban gyakran nem az.

Ha

  • megnyomjuk a ctrl-alt-delt (és a kernelt úgy állítottuk be, hogy erről értesítse az initet), vagy
  • INT signalt küldünk az 1-es processznek, akkor

megnézi, létezik és futtatható-e a /etc/runit/ctrlaltdel script; ha igen

  • lefuttatja, majd
  • küld magának egy CONT signalt.

Ha létezik és végrehajtható a /etc/runit/stopit file, akkor a runit a CONT signal hatására leállítja a rendszert. Ha a /etc/runit/reboot is létezik és végrehajtható, akkor leállítás helyett újraindítja a gépet.

A /etc/runit/ctrlaltdel script tartalma pl.:

#!/bin/sh

PATH=/bin:/usr/bin
MSG="System is going down in 14 seconds..."

# echo 'disabled.' ; exit
touch /etc/runit/stopit
chmod 100 /etc/runit/stopit && echo "$MSG" | wall
/bin/sleep 14

3.3 runvsdir

A runsvdir feladata a runsv-folyamatok menedzselése.

Normális körülmények között a /etc/runit/2 indítja:

exec env - PATH=$PATH \
runsvdir -P /var/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'

A runsvdir odavált a megadott könyvtárba (itt /var/service, amire mutató symlinket a daemontools-hoz szokott rendszergazda létrehoz a gyökérben), és minden (legfeljebb 1000) itt található alkönyvtárhoz és alkönyvtárra mutató symlinkhez indít egy runsv-t. A ponttal kezdődő nevű könyvtárakat kihagyja. Ehhez a runsv-nek benne kell lennie a PATH-ban. Ha valamelyik runsv kilép, a runsvdir újraindítja.

Ötmásodpercenként megnézi, megváltozott-e a paraméterként kapott könyvtár (mtime, inode, device). Ha megváltozott, akkor megnézi, van-e benne új alkönyvtár (ha igen, indít hozzá runsv-t), vagy megszűnt-e régi (ekkor leállítja az adott runsv-t és a szolgáltatást, ami hozzá tartozott).

A "log: sokpont" helyre a runsvdir stderr-je irányítódik; amit amúgy a standard errorra írna ki, azt helyette beleírja a saját processznevébe, amiben a pontok egy körpuffert képeznek. Negyedóránként mindenképpen kiír ide egy pontot, hogy a régi üzenetek elmúlhassanak, ill. megbecsülhető legyen egy üzenet kora.

3.4 runsv

A runsv feladata egy szolgáltatás (és esetleg egy hozzá tartozó naplózószolgáltatás) monitorozása, elindítása, leállítása.

Egyetlen paramétert kap indításkor, egy könyvtár nevét. Tevékenysége így foglalható össze:

  1. Induláskor odavált, és lefuttatja a ./run programot.
  2. Ha ez kilép, és létezik a ./finish, lefuttatja azt.
  3. Ha a ./finish nem létezik, vagy kilép, újra elindítja a ./run-t.

Ha a scriptek "azonnal" kilépnek, a runsv egy másodpercet vár az újraindítási kísérletek között.

Ha létezik a ./down nevű file, a runsv nem indítja el azonnal a ./run-t, csak akkor, ha erre külön megkérjük.

A létezik a ./log könyvtár, akkor a runsv nyit egy pipe-ot, a ./run és a ./finish standard outputját abba irányítja (ezt megteheti, hiszen az ő gyermekfolyamatai, vagyis tőle öröklik az stdout filedeszkriptort), és a ./log könyvtárban található run és finish programokkal pontosan úgy jár el, mint a szolgáltatás hasonló nevű file-jaival. A log/run abból a pipe-ból fog olvasni, amiben a ./run ír, tehát a szolgáltatás standard outputja megjelenik a logoló alszolgáltatás standard inputján.

A runsv létrehoz egy ./supervise (és esetleg ./log/supervise, ha van ./log) könyvtárat. A supervise könyvtár tartalma:

  • status: bináris file, ami a szolgáltatás állapotával kapcsolatos információkat tartalmazza; kompatibilis a daemontools supervise programjának status file-jával
  • stat: szövegfile. Tartalma lehet:
    • "run": a szolgáltatás fut
    • "down": a szolgáltatás nem fut
    • "finish": a szolgáltatáshoz tartozó finish program fut
    • a fentiek még kiegészülhetnek a következőkkel:
      • ", paused": a szolgáltatást felfüggesztettük
      • ", got TERM": a szolgáltatásnak küldtünk TERM signalt, de még nem lépett ki
      • ", want down": a szolgáltatást le szeretnénk állítani, de még nem állt le
      • ", want exit": a runsv-t is le szeretnénk állítani, de ehhez előbb a szolgáltatást kell, az pedig még nem állt le
  • pid: a szolgáltatáshoz tartozó éppen futó program (run vagy finish) process ID-ja
  • control: egy fifo, amibe parancsokat írhatunk (ezt csinálja az sv(8) program, l. később):
    • u: "up". Ha a szolgáltatás nem fut, elindítja; amikor kilép, újraindítja.
    • d: "down". Ha a szolgáltatás fut, kap egy TERM, majd egy CONT signalt (hogy a TERMre akkor is tudjon reagálni, ha fel volt függesztve). Ha a ./run kilép, lefut a ./finish; amikor az is kilép, a runsv nem indítja újra a szolgáltatást.
    • o: "once". Ha a szolgáltatás nem fut, elindítja. Amikor kilép, nem indítja újra (de a finish lefut).
    • p: "pause". Ha a szolgáltatás fut, felfüggeszti (STOP signalt küld neki).
    • c: "continue". Ha a szolgáltatás fut, folytatja (CONT signalt küld).
    • h: "hangup". Ha a szolgáltatás fut, HUP signalt küld neki.
    • a: "alarm". ALARM signal.
    • i: "interrupt". INT signal.
    • q: "quit". QUIT signal.
    • 1: USR1 signal.
    • 2: USR2 signal.
    • t: "terminate". TERM signal. Ha a szolgáltatás "up" állapotban volt, és a TERM hatására kilép, akkor ez lényegében egy újraindítással egyenértékű, mert a runsv újra el fogja indítani.
    • k: "kill". KILL signal.
    • x: "exit":
      • Ha a szolgáltatás fut, kap egy TERM, majd egy CONT signalt.
      • Amikor kilép, lefut a finish.
      • Ha nincs log alszolgáltatás, a runsv kilép.
      • Ha van log, a runsv lezárja a log standard inputját, és megvárja, amíg a log/run is kilép.
      • Lefut a log/finish.
      • A runsv kilép.
      • A log szolgáltatásnak nem lehet exit parancsot küldeni (ill. lehet, de ignorálja).

Fontos, hogy a parancsok pontosan egy karakterből állnak, sorvége-jel sem kell utánuk; tehát ha nem az sv programmal, hanem kézzel akarjuk kiadni őket, és precízek akarunk lenni, akkor echo -n-t használjunk. Ha nem akarunk precízek lenni, az sem baj, mert a runsv nem törődik az ismeretlen parancskarakterekkel.

Gondoljunk arra, hogy ha nem fut a runsv (és így nincs, aki elvegye a fifoba írt karaktert), akkor az echo blokkolódni fog.

Ha a runsv TERM signalt kap, úgy tesz, mintha "x" parancsot kapott volna.

3.4.1 A parancsok hatásának testreszabása

Megtehetjük, hogy a fenti parancsok hatását megváltoztatjuk.

Ehhez létre kell hoznunk a szolgáltatás könyvtárában egy control nevű alkönyvtárat, amiben a parancsokról elnevezett programokat helyezhetünk el. Ha olyan parancsot adunk ki, amelyhez tartozó control program létezik és futtatható, akkor a runsv

  • lefuttatja a control/parancs programot, és megvárja, amíg kilép;
  • ha a visszatérési érték sikeres, a runsv nem kézbesíti a kért signalt a szolgáltatásnak;
  • ha sikertelen, akkor kézbesíti.

A logoló alszolgáltatás vezérlése nem testreszabható.

3.5 sv

Az sv feladata az, hogy információt szolgáltasson a runsv által menedzselt szolgáltatások állapotáról ill. hogy parancsokat adjon a runsv-nek. Képes továbbá korlátozottan emulálni egy SystemV initscript működését.

Indítása:

# sv [-v] [-w sec] command services

# /etc/init.d/service [-w sec] command

A services helyén felsorolhatunk egy vagy több szolgáltatást, amikre hatni szeretnénk; a hozzájuk tartozó runsv-s directory nevét kell megadni.

A command lehet a runsv által ismert egykarakteres parancsok valamelyike (de kiírhatjuk a teljes parancsot is, mert csak az első karaktert nézi), valamint a következők, amelyek a SystemV-initscript-emulációt segítik:

  • start: mint az "up", de megvárja, hogy a szolgáltatás el is induljon (alapértelmezés szerint 7 másodpercet). A szolgáltatáshoz tartozó check scripttel ellenőrzi, hogy elindult-e, vagy, ha ilyen nincs, akkor azt várja meg, hogy epszilonnál hosszabb ideje fusson. Ha 7 (vagy sec) mp után nincs eredmény, kilép, a szolgáltatás pedig továbbra is próbál elindulni.
  • stop: mint a "down", de megvárja, hogy a szolgáltatás le is állon (mint fent).
  • restart: mint egy "down", majd egy "up" egymás után.
  • shutdown: mint az "exit", de megvárja (timeout mint fent), hogy a runsv is kilépjen.
  • force-stop: mint a "stop", de időtúllépés esetén KILL signalt küld a szolgáltatásnak.
  • force-reload: mint egy "term" és egy "continue", de ha nem áll le időben, kap egy KILL signalt is.
  • force-restart: mint a "restart", de ha nem áll le időben, kap egy KILL signalt is.
  • force-shutdown: mint a "shutdown", de ... you know the rest. :)

Emellett van még egy check parancs, ami azt vizsgálja meg, hogy a szolgáltatás az általunk utoljára kért állapotban van-e, ill. ha nincs, akkor a megadott ideig vár arra, hogy oda eljusson.

Az initscript-emuláció értelme az, hogy ha pl. van egy apache szolgáltatásunk, akkor megcsinálhatjuk ezt (debianos példa):

# dpkg-divert --local --rename /etc/init.d/apache
# ln -s /usr/bin/sv /etc/init.d/apache

Így a /etc/init.d/apache segítségével majdnem a szokásos módon kezelhetjük az apache-t: működik a stop, a restart, a start és társai, mert az sv megnézi, milyen néven hívtuk meg, és az olyan nevű szolgáltatást próbálja menedzselni. Sajnos pl. "reload" akciót nem tudunk csinálni, ezt az sv egyelőre nem támogatja - pedig a check mintájára nem lenne nehéz. Ez nyilván nem jelenti azt, hogy nem tudjuk újraolvastatni az apache-val a konfigurációját, ha az apache amúgy képes erre (mellesleg képes), csak azt, hogy a runit ehhez nem nyújt nekünk segítséget.

3.6 Runlevel-emuláció: runsvchdir

Ha nagyon a szívünkhöz nőttek a runlevelek, runit mellett is használhatjuk őket; ráadásul tetszőlegesen sok lehet belőlük, és szabadon adhatunk nekik neveket.

Az elgondolás lényege az, hogy több /var/service-szerű könyvtárat csinálunk (minden runlevelhez egyet), más-más szolgáltatáskönyvtárak symlinkjeivel, és a runsvdirt rábeszéljük, hogy váltson át egy másikba, és értelemszerűen állítsa le azokat a szolgáltatásokat, amelyeknek az új könyvtárban nincs symlinkje, azokat pedig, amelyeknek az előzőben nem volt, de itt van, indítsa el.

Ehhez

  1. létre kell hoznunk valahol (alapértelmezés szerint a /etc/runit/runsvdir/ könyvtárban) egy-egy ilyen runlevel-könyvtárat
  2. ebben létrehozni egy "current" nevű symlinket, ami az aktuális runlevelre mutat
  3. a /var/service-t (vagy ami a /etc/runit/2-ben a runsvdir paramétere) le kell cserélnünk egy symlinkre, ami erre a current nevű symlinkre mutat.

Ezután a current symlink átállítása runlevel-váltást eredményez. Ezt csinálja meg a runsvchdir parancs, és ráadásul egy az éppen elhagyott runlevelre mutató symlinket is létrehoz "previous" néven. Ez azért jó, mert attól, hogy a runsv processzek TERM signalt küldtek a lelövendő szolgáltatásoknak, még nem biztos, hogy mindegyik le is állt; a "previous" symlinken keresztül követhetjük nyomon a legegyszerűbben, hogy az előző runlevelben futó szolgáltatások státusza hogyan alakul.

3.7 chpst

A chpst feladata az, hogy valamilyen környezetet beállítson, és abban elindítson egy gyermekfolyamatot. Ennek megfelelően a unixos folyamatok állapotterének jelentős részét tetszésünk szerint beállíthatjuk:

  • UID
  • GID
  • supplementary groups ("kiegészítő csoporttagságok")
  • környezeti változók (a daemontools envdir-jéhez hasonló módon; l. lejjebb)
  • chroot
  • nice
  • kölcsönös kizárás (a megadott program csak egy példányban futhasson)
  • erőforráskorlátok:
    • memória (vagy az összes, vagy csak az adatszegmens)
    • megnyitható file-ok száma
    • processzek száma
    • létrehozható file-ok maximális mérete
    • core file max. mérete
  • új process group létrehozása a program elindítása előtt
  • standard filedeszkriptorok bezárása (input, output, error szelektíven) a program elindítása előtt

Ami hiányzik, pedig nem lenne rossz:

  • scheduler beállítása (lehetne pl. realtime, bár ez nem hordozható)
  • capability-k beállítása (szintén nem hordozható)
  • umask
  • munkakönyvtár
  • max. CPU-idő
  • hard erőforráskorlátok (csak a soft limiteket állítja, de nem tudja őket nagyobbra rakni a hard limiteknél, vagyis ha van valamilyen default hard limit, annál nagyobb softlimitet a chpst nem tud beállítani; előtte ulimittel kell módosítani a hard limitet)
  • supplementary groupok megtartása (eldobja őket)

A környezeti változók beállítása úgy történik, hogy létrehozunk egy könyvtárat, amiben minden file egy beállítandó környezeti változóról van elnevezve. A változó értéke az adott file tartalmának első sora lesz. A nullás karaktereket újsorrá alakítja, a sorvégi szóközöket és tabulátorokat pedig lenyeli. Ha a file nulla byte hosszú, akkor a róla elnevezett változót a chpst törli a környezetből.

A kölcsönös kizárás megvalósításához a chpst megpróbál lockolni egy file-t; ha sikerül, akkor elindítja a megadott programot úgy, hogy az megkapja a lockolt file-t is. Ha az elindított program bezárja az öröklött file-deszkriptort, akkor a kölcsönös kizárás nem biztosított; szerencsére kevés program csinálja ezt.

3.8 svlogd

Az svlogd feladata az, hogy a standard inputon érkező sorokat automatikusan rotált naplófile-okba írja.

A naplózó alszolgáltatások általában az svlogd-t futtatják; az svlogd sokkal okosabb, mint a daemontools multilogja.

A naplózás a következőképpen működik:

  • Az svlogd kilép, ha a standard inputon file végét olvas.
  • Egy svlogd-példány egy vagy több könyvtárba logol.
  • Egy könyvtárba csak egy svlogd-példány logolhat (az apache2-ben versenyhelyzet van ezzel kapcsolatban, ha nem az előtérben futtatjuk és pipe-ba logolunk).
  • Minden könyvtárnak saját konfigurációja van, ami eldöntheti, hogy az adott könyvtárba a bemeneten olvasott sorok közül melyeket kell naplózni és melyeket nem.
  • Egy sor több könyvtárba is naplózódhat.
  • A könyvtárakban a következő file-ok találhatóak:
    • current: az aktuális logfile, amibe éppen ír az svlogd
    • @4000000044b600e22b73217c.s jellegű file-ok: korábbi naplófile-ok, amelyeknek az utófeldolgozása sikeres volt; a nevük a rotáció időpontja tai64n formátumban
    • @4000000044b600e22b73217c.u jellegű file-ok: mint fent, de nem történt utófeldolgozás
    • az utófeldolgozás közben még megjelenhetnek rövid életű .t kiterjesztésű file-ok és egy previous nevű file.
    • config: az svlogd adott könyvtárra vonatkozó konfigurációját tartalmazza. A formátum egyszerű; egykarakteres, sor eleji parancsok, majd a hozzájuk tartozó argumentumok:
      • sméret: a naplófile-okat rotálni kell, ha elérték a megadott méretet. Ha nulla, nincs méret alapján történő rotáció.
      • ndarab: a megadott darabszámú korábbi logfile-t tartunk meg. Ha 0, nem töröl régi logokat.
      • Ndarab: legalább a megadott darabszámú korábbi logfile-t tartunk meg. Ha elfogyott a hely, és ennél több régi log van, az svlogd törli a legrégebbit.
      • tmásodperc: ha az aktuális napló elérte a megadott kort, és nem üres, akkor rotálunk. Ha 0, nincs idő-alapú rotáció.
      •  !posztprocesszor: rotációkor a naplót a megadott program (pl. egy tömörítő vagy egy elemző) dolgozza fel. Alapértelmezés szerint nincs utófeldolgozás.
      • ua.b.c.d[:port]: a megadott IP-cím megadott portjára is elküldi az üzenetet (udp-vel) (valójában csak az első hossz byte-ot, a hossz megadható a parancssorban.
      • Ua.b.c.d[:port]: mint a kis u, de csak udp-n logol, a helyi logfile-ba nem írja bele az üzenetet.
      • pprefix: a megadott prefixet odailleszti az összes naplózott üzenet elé. Ez akkor jó, ha udp-n logolunk, és a fogadóoldalon könnyen szét akarjuk válogatni az üzeneteket; a prefix alapján lesz a legegyszerűbb.
      • -minta: a mintára illeszkedő sorokat nem naplózza (l. lejjebb).
      • +minta: a mintára illeszkedő sorokat mégis naplózza.
      • eminta: a mintára illeszkedő sorokat kiírja a standard errorra (l. lejjebb).
      • Eminta: a mintára illeszkedő sorokat mégsem írja ki a standard errorra.
    • Van még néhány egyéb file (state, newstate, lock), amiket az svlogd saját céljaira használ.

A posztprocesszort az svlogd a háttérben futtatja, úgyhogy közben megy tovább a logolás. Ha viszont a következő rotációkor még fut az előző processzor, az svlogd blokkolódik, amíg ki nem lép; ez blokkolhatja az svlogd-be logoló szolgáltatást is! A posztprocesszort érdemes lehet a tryto felügyelete alatt futtatni; ez néhányszor megpróbálja, de ha egyszer sem sikerül adott idő alatt befejezni, akkor inkább lemond a feldolgozásról és feldolgozás nélkül menti el a logot. A tryto a socklog csomagban található.

A mintaillesztés két metakaraktert ismer:

  • A csillag jelentése: tetszőleges sztring, kivéve a csillagot követő karaktert;
  • A plusz jelentése: a pluszt követő karakterből egy vagy tetszőleges számú.

Vegyük észre, hogy ez sokkal gyengébb, mint a reguláris kifejezések, cserébe viszont sokkal gyorsabb az illesztés, és az esetek döntő részében még a pluszra sincs szükség.

Pl. az a minta, hogy "*PAM_unix[*]: (ssh)*" illeszkedik minden olyan sztringre, amelyben az első nagy P betű után az jön, hogy "AM_unix[", majd egy tetszőleges sztring a következő "]" jelig, amelyet a ": (ssh)" sztring követ, ezután pedig még lehet bármi a sorban. Az a sor, hogy "Péntek: PAM_unix[1234]: (ssh) session opened for user root" nem illeszkedik, mert a "Péntek" P-betűje után nem az jön, hogy "AM_unix[".

A minták segítségével könnyen szétdobálhatjuk pl. a sokféle "daemon" üzenetet aszerint, hogy melyik szolgáltatás naplózta; vagy pl. naplózhatjuk egy könyvtárba az összes, az ssh-val kapcsolatos üzenetet attól függetlenül, hogy "auth" vagy "daemon" syslog facility-vel érkezett-e.

Az svlogd parancssori opcióival megadhatjuk, hogy

  • minden sor elejére illesszen egy tai64n időbélyeget (-t)
  • minden sor elejére illesszen egy lexikografikusan rendezhető, ember számára olvasható UTC timestampet (-tt)
  • cserélje a nem nyomtatható karaktereket valami másra (-r)
  • egyéb karaktereket is cseréljen (pl. szóközöket és tabokat válogatás nélkül aláhúzásra)
  • mekkora puffermérettel dolgozzon. Ezt nagyon nagy log-forgalom esetén érdemes az alapértelmezés szerinti 1024 byte-ról 4, vagy akár 16-64 kilobyte-ra is emelni, ha azt tapasztaljuk, hogy a szolgáltatás arra vár, hogy logolhasson; a legkönnyebben úgy jöhetünk rá, hogy emiatt lassú a naplózás (és így a szolgáltatás), ha strace-eljük az svlogd-t, és azt látjuk, hogy mindig teli pufferrel tér vissza a read() rendszerhívása. Normális esetben több karaktert próbál olvasni, mint amennyi van.

Az svlogd néhány signalra speciálisan reagál:

  • HUP: az összes log-könyvtár konfigurációját újraolvassa, a log-könyvtárakat újra megnyitja; ha valamelyik megszűnt, azt eldobja
  • TERM: feldolgozza a pufferében levő adatokat (eldönti, hova kell logolni az adott sorokat és kiírja őket), megvárja, hogy véget érjenek az esetleg futó posztprocesszorok, majd kilép
  • ALRM: rotálja az összes nem üres logot.

A standard errorra való logolást pl. arra lehet használni, hogy bizonyos üzenetekről emailt kapjunk. Erről részletesebben a socklog környékén lesz szó.

3.9 utmpset

Kis segédprogram, a getty jellegű szolgáltatások finish scriptjéből kell meghívni; a bejelentkezési naplóban rögzíti, hogy az adott terminálon véget ért a login session. Ezt amúgy az init csinálná, de a runitban nincs benne az ezzel kapcsolatos kód.

4 A nagy egész

Hogyan is érdemes egy runitos rendszert felépíteni?

Törekedjünk arra, hogy minden szolgáltatásunkat a runit monitorozza, és lehetőleg a standard outputra logoljanak, így mindegyiknek egyéni svlogdje lehessen, és ne függjenek egy rendszerszintű naplózószolgáltatástól. Az svlogd-k naplófile-jai külön logikai köteten legyenek, hogy semmi ne használhassa el előlük a helyet. Ez azért fontos, mert önmagukban az svlogd logjainak méretére (ha sehol sem nulla sem az n, sem az s érték) mindig van érvényes felső becslésünk, tehát ha más nem ír oda, és elég helyet csináltunk, akkor a hely biztosan nem fogy el. Ha elfogy a hely, az azért baj, mert blokkolódhatnak a szolgáltatásaink.

A rendszerszintű naplózást célszerű a socklogra bízni (erről később lesz szó).

Próbáljunk gondoskodni arról, hogy a szolgáltatásaink, ha valami bajuk van, akkor ne megálljanak, hanem kilépjenek, hogy a runit újraindíthassa őket.

Mindig törekedjünk a kód és a konfiguráció szétválasztására, hogy a scriptjeink hordozhatóak legyenek.

4.1 run scriptek írása

Egy szolgáltatást futtató run script alapreceptje a következő:

  1. standard error átirányítása a standard outputra (hogy a logban jelenjen meg);
  2. a szolgáltatás elindítása az exec paranccsal olyan módon, hogy a meghívott parancssor csak akkor térjen vissza, amikor kilép a szolgáltatás.

Egy minimális run script valahogy így fest (rinetd):

#!/bin/sh
exec 2>&1
exec rinetd -f

Ha egy új run scriptet írunk, akkor kiindulhatunk a szolgáltatás initscriptjéből. Nézzünk egy példát, mondjuk az apache2-t. Az initscript a következő (csak az indításért felelős részt hagytam benne):

#!/bin/bash -e
#
# apache2               This init.d script is used to start apache2.
#                       It basically just calls apache2ctl.

ENV="env -i LANG=C PATH=/usr/local/bin:/usr/bin:/bin"

#edit /etc/default/apache2 to change this.
NO_START=0

set -e
if [ -x /usr/sbin/apache2 ] ; then
        HAVE_APACHE2=1
else
        exit 0
fi

. /lib/lsb/init-functions

test -f /etc/default/rcS && . /etc/default/rcS
test -f /etc/default/apache2 && . /etc/default/apache2
if [ "$NO_START" != "0" -a "$1" != "stop" ]; then 
        [ "$VERBOSE" != "no" ] && log_warning_msg "Not starting apache2 - edit /etc/default/apache2 and change NO_START to be 0.";
        exit 0;
fi

APACHE2="$ENV /usr/sbin/apache2"
APACHE2CTL="$ENV /usr/sbin/apache2ctl"

# Stupid hack to keep lintian happy. (Warrk! Stupidhack!).
case $1 in
        start)
                [ -f /etc/apache2/httpd.conf ] || touch /etc/apache2/httpd.conf
                # ssl_scache shouldn't be here if we're just starting up.
                [ -f /var/run/apache2/ssl_scache ] && rm -f /var/run/apache2/*ssl_scache*
                # /var/run and /var/lock could be on a tmpfs
                [ ! -d /var/run/apache2 ] && mkdir /var/run/apache2
                [ ! -d /var/lock/apache2 ] && mkdir /var/lock/apache2
                # Make sure /var/lock/apache2 has the correct permissions
                chown www-data /var/lock/apache2

                log_begin_msg "Starting apache 2.0 web server..."
                if $APACHE2CTL startssl; then
                        log_end_msg 0
                else
                        log_end_msg 1
                fi

Ugye emlékszünk, milyen hosszú volt a leállításért felelős rész - mindazt nem kell megírnunk, a runit elintézi (kivéve, ha nagyon buta dolgokat csinál a szolgáltatásunk - pl. a szülőfolyamat ki tud lépni anélkül, hogy az összes gyermekét kiléptetné).

Csak érdekességképpen nézzük meg, mit is importálunk a /lib/lsb/init-functions file-ból (nem másolom be mind a 290 sort):

  • start_daemon() - egy wrapper a start-stop-daemon köré
  • pidofproc() - megpróbálja megtippelni egy szolgáltatás PIDjét - de pl. vakon bízik a pidfile tartalmában...
  • killproc() - mint a pidofproc, de még meg is próbálja kilőni a megtalált processzt
  • log_use_fancy_output() - a csicsaképességet detektálja; a többi függvény gyakran hívogatja, ez viszont külső programot is hív => nesze neked, sebesség
  • log_success_msg(): echo "$@" - emiatt aztán érdemes volt külön függvényt írni - idővel majd bizonyára ez is csicsa lesz
  • log_warning_msg() - csicsa
  • get_lsb_header_val() - az initscriptben elhelyezett kommentekből információt kibányászó segédfüggvény pl. komplikált függőségkezeléshez
  • log_daemon_msg() - csicsa annak érdekében, hogy ugyanaz az initscript futhasson különböző disztribúciók alatt, és az outputja igazodjon az adott disztribúció initscript-look'n'feeljéhez. Hja kérem, fontos dolgok...
  • log_progress_msg() - csicsa
  • log_end_msg() - csicsa
  • log_action_msg(): echo "$@." - csicsa-placeholder
  • log_action_begin_msg(): echo -n "$@..." - dettó
  • log_action_cont_msg(): echo -n "$@..." - dettó
  • log_action_end_msg() - csicsa

Mérleg: 10 db csicsázófüggvény, 2 db esetleges PID-tippelő, 1 db értelmesnek nevezhető függvény. Az apache2 elindításához szigorúan véve egyiknek sincs köze.

Az a bizonyos /usr/sbin/apache2ctl, amit az initscript meghív, szintén egy shellscript. 103 sor hosszú, de "start" paraméter esetén a tevékenysége így foglalható össze:

/usr/sbin/apache2 -k $@

"startssl" esetén ezt csinálja:

/usr/sbin/apache2 -k start -DSSL

Írjunk első közelítésben egy egyszerű run scriptet a fentiek alapján:

#!/bin/bash
exec 2>&1

#edit /etc/default/apache2 to change this.
NO_START=0
MAXFILES=1024

test -f /etc/default/apache2 && . /etc/default/apache2 # beolvassuk a Debian-féle konfigot a kompatibilitás érdekében

# adjunk jogokat az apacheadmin csoportnak a szolgáltatás menedzselésére:
chown :apacheadmin ./supervise ./supervise/control
chmod g+w ./supervise/control
chmod g+x ./supervise 

[ -x /usr/sbin/apache2 ] || exec sv down .	# ha nincs apache bináris, leállítjuk magunkat
[ "$NO_START" != "0" ] && exec sv down .	# ha a configfile szerint nem kell futnunk, akkor lépjünk ki

[ -f /etc/apache2/httpd.conf ] || touch /etc/apache2/httpd.conf	# a debianos initscript létrehozta ezt a file-t, ha nem létezett, tegyünk hát így mi is
[ -f /var/run/apache2/ssl_scache ] && rm -f /var/run/apache2/*ssl_scache*	# a debianos scriptből átemelve

[ ! -d /var/run/apache2 ] && mkdir /var/run/apache2				# szintén
[ ! -d /var/lock/apache2 ] && mkdir /var/lock/apache2				# szintén

# Make sure /var/lock/apache2 has the correct permissions
chown www-data /var/lock/apache2						# szintén

ulimit -H -n $MAXFILES								# a chpst csak a softlimiteket állítja, a hardot nekünk kell
exec chpst -o $MAXFILES /usr/sbin/apache2 -k start -DSSL -DNO_DETACH		# ezt már az apache2ctl-ből puskáztuk

Ez így már működik, de nem túl hordozható. Bele van drótozva a www-data user, az apacheadmin group, és egy csomó könyvtár helye. Nem választottuk szét a kódot és a konfigurációt.

Próbáljuk meg még egyszer, kicsit általánosabban:

#!/bin/bash
exec 2>&1

SVNAME=$(basename $(pwd))	# találjuk ki, hogy hívják a szolgáltatást, amiben futunk (pl. apache2-svn)
SVCONFIG=/etc/default/$SVNAME	# ez lesz az erre a példányra vonatkozó configfile

# néhány default:
MAXFILES=1024
RUNASUSER=www-data
SERVERROOT=/etc/$SVNAME
APACHECONFIGFILE=$SERVERROOT/apache2.conf
ADMINGROUP=apacheadmin
VARRUNGROUP=root
VARRUNMODE=700
SSL="-DSSL"

# betöltjük a debian-configot; ez is felülbírálhatja a fentieket, mondjuk gépszinten:
[ -r /etc/default/apache2 ] && . /etc/default/apache2
# betöltjük a specifikus configot (ez átírhatja a fenti defaultokat):
[ -r $SVCONFIG ] && . $SVCONFIG

chown :$ADMINGROUP ./supervise ./supervise/control
chmod g+rw ./supervise/control
chmod g+x ./supervise 

[ -x /usr/sbin/apache2 ] || exec sv down .
[ "$NO_START" != "0" ] && exec sv down . # A debian-konfiguráció használ egy NO_START változót, támogassuk mi is

[ -f $SERVERROOT/httpd.conf ] || touch $SERVERROOT/httpd.conf
rm -f /var/run/$SVNAME/*ssl_scache* 2>/dev/null # minek megnézni, hogy létezik-e? anélkül is törölhetjük

# A /var alatti könyvtárakat konfigurálható jogokkal hozzuk létre:
mkdir -p /var/run/$SVNAME /var/lock/$SVNAME
chown $RUNASUSER:$VARRUNGROUP /var/run/$SVNAME /var/lock/$SVNAME
chmod $VARRUNMODE /var/run/$SVNAME /var/lock/$SVNAME

# Állítsuk be a hard erőforráskorlátot, mert azt a chpst nem tudja:
ulimit -H -n $MAXFILES

# Csúf, de az apache2 esetében sajnos szükség lehet rá;
# mentségünk, hogy csak az adott userként futó apache2-ket lőjük le,
# ilyen userként pedig akkor csak azok fussanak, amik hozzánk tartoznak.
pkill -u $RUNASUSER apache2

exec chpst -o $MAXFILES \
	sudo -u $RUNASUSER \
	/usr/sbin/apache2 -d "$SERVERROOT" \
	$SSL \
	-DNO_DETACH \
	-f "$APACHECONFIGFILE"

# azért sudo, mert az belép a megfelelő supplementary groupokba, a chpst pedig eldobja őket;
# nekem a shadow csoporttagság miatt kellett

Ha ezt a run scriptet bemásoljuk egy apache2-svn nevű könyvtárba, és azt belinkeljük a /var/service-be, akkor a script, amikor elindul, az apache2-svn-re jellemző konfigurációt fogja beolvasni a /etc/default/apache2-svn-ből. A kód és a konfiguráció jól elkülönül; a script vándorolhat gépről gépre anélkül, hogy módosítani kellene. Elég a /etc/default/apache2-* file-okat módosítani.

Vegyük észre, hogy itt a sudo miatt eleve nem rootként indul el az apache2, tehát nem fog tudni 1024 alatti porton figyelni, hacsak ezt valahogyan nem engedélyeztük a RUNASUSER számára.

Az ilyen template-szerű run-scripteket úgy ugyan nem használhatjuk, hogy ugyanazt a könyvtárat több példányban, csak mindig más néven belinkeljük a /var/service alá, mivel mindegyikhez tartozó runsv ugyanazt a supervise alkönyvtárat akarná használni; annak viszont természetesen nincs akadálya, hogy a run scriptet az egyes példányok saját könyvtáraiban egy a template run-scriptre mutató symlinkkel helyettesítsük. Így a run scriptet könnyedén tarthatjuk pl. subversion repositoryban.

4.2 Logoló run script

Egy minimális, svlogd-t indító run script valahogy így fest:

#!/bin/sh
exec chpst -u log svlogd /var/log/sv/szolgaltatasneve

Ezzel az a baj, hogy ha a szolgáltatást egy új gépre másoljuk, ott még esetleg nincs meg ez a könyvtár, és ebben az esetben az svlogd csődöt mond. Egy kicsit feltupírozott template logolóscript festhet pl. így:

#!/bin/sh
cd ..
SVNAME=$(basename $(pwd))
LOGDIR=/var/log/sv/$SVNAME
LOGDIRMODE=700
LOGDIRGROUP=root
LOGUSER=log
CONFIG=/etc/default/$SVNAME
SVLOGDOPTS="-t"
ROTSIZE=50000
ROTNUM=50
POSTPROC="gzip -9"

# rendszerszintű svlogd-defaultok
[[ -r /etc/default/svlogd ]] && . /etc/default/svlogd
# erre a konkrét szolgáltatásra vonatkozó beállítások:
[[ -r $CONFIG ]] && . $CONFIG

mkdir -p $LOGDIR

chown $LOGUSER:$LOGDIRGROUP $LOGDIR
chmod $LOGDIRMODE $LOGDIR

cd $LOGDIR || exit 1
if [[ ! -f config ]]; then
        cat <<EOF >config
s$ROTSIZE
n$ROTNUM
!tryto -pP $POSTPROC
EOF
fi

exec chpst -u $LOGUSER svlogd $SVLOGDOPTS $LOGDIR

5 Ajánlott irodalom

6 Potenciális zh-kérdések

  • Mik az előnyei a runitnak a hagyományos System V inittel szemben? Mik a hátrányai?
  • Milyen feltételeket kell teljesítenie egy minimális runitos run-scriptnek?
  • Milyen lehetőségeket biztosít az svlogd a logok által elfoglalt hely mennyiségének menedzselésére?
  • Milyen esetekben rotálja a logokat az svlogd?
  • Mi alapján tudja az svlogd szétválogatni a naplóüzeneteket?
  • Hogyan valósíthatunk meg runlevel-váltást runitos rendszerben?
    • A runsvdirnek indításkor megadunk egy directoryt, amiben levő szolgáltatásokat futtatnia kell. Ha a directory megváltozik alatta, azt észreveszi, és az új directoryban levő szolgáltatásokat fogja futtatni; a régiek közül azokat, amik az új directoryban nincsenek benne, lelövi.
    • A runlevel-váltáshoz az kell, hogy a runsvdirnek megadott directory egy symlink legyen, ami az egyik runlevel directoryjára mutat.
    • Ha menet közben átállítjuk a symlinket egy másik runlevel directoryjára, akkor a runsvdir új directoryt lát maga alatt, és elkezd zsonglőrködni a szolgáltatásokkal, ahogy kell.
    • Ezt csinálja meg a runsvchdir, és pluszban csinál még egy previous nevű symlinket, ami az éppen elhagyott (előző) runlevel directoryjára mutat. Ez azért jó, mert ezen a symlinken keresztül egységes módon tudjuk követni, hogy az elhagyott runlevelben levő szolgáltatások mit csinálnak (pl. leálltak-e már). Ha nem lenne previous symlinkünk, akkor csak az előző runlevel neve alapján tudnánk ezt csinálni, az meg ugye attól függően más és más, hogy melyik runlevelt hagytuk el.
  • Hová naplózhatjuk a runitos szolgáltatásaink naplózásával kapcsolatos hibaüzeneteket? Hogyan?
  • Mire jó, hogy az svlogd bizonyos naplóüzeneteket szelektíven a standard errorra is ki tud írni?
  • Mire kell odafigyelni, ha posztprocesszort írunk az svlogd-hez?
  • Hogyan működik a runitban a System V initscriptek emulációja?
  • Hogyan adhatók meg a runitban a szolgáltatások egymástól való függései? (Hogyan biztosítható, hogy a függő szolgáltatás csak a szükséges szolgáltatás után indulhasson el?)

A zh második részéhez (ahol egy a4-es lapnyi segédeszköz használható):

  • Adott az alábbi System V initscript és az általa indított szolgáltatás dokumentációjának releváns része. Írjon ez alapján olyan run-scriptet, amely ugyanezt a szolgáltatást a runit számára menedzselhető módon indítja el! Az eredeti script sorszámozott sorait nem kell újra leírnia, elég rájuk szám szerint hivatkozni.
  • Adott az alábbi run-script. Mit rontottunk el benne?
  • Talán segít:
    • Nem igazán segít; ez csak egy sed/regex-összefoglaló. --Korn András 2006. november 14., 11:46:13 (CET)
    • s/a/b/ = sa\aaba = a-t kicseréli b-re.
    • [A-Z][A-Z][A-Z]- [1-9][1-9][1-9]
    • . –tetszőleges karakter
    • a* atomi kifejezés ismételten
    • a+=aa*
    • ^ - sor eleje
    • $ - sor vége
    • [^a-f] = nem a-f
    • ([abc]1);([cde][3-9]) = vagy abc1 vagy …
    • ([A-F]b[c-z])\1\1 - \1 – az előző lépések ismételtetése
    • [A-Z]{2,3} – A-Z kétszer, vagy 3x ismétlődik
    • s/a/b/gi – i miatt A = a
    • s/\([A-Z]{3}\)-\([0-9]{3}\)/\2-\1/ - hivatkozások, jelen esetben a rendszám megfordítása.
Személyes eszközök