Építsünk saját GNU/Linux rendszert! #3

Itt az elején felhívnám a visszatérő olvasók figyelmét is, hogy bővítettem az első részt, valamint apró javításokat eszközöltem a másodikban.

Miért épp pacman?

Bevezetésképpen érdemes elolvasni a csomagkezelés feladatáról és eszközeiről általában írt cikkemet.

A saját rendszerem építése során a pacman csomagkezelőt használtam. Elsősorban a következő képességek miatt választottam:

  • egyszerűség –– Relatív egyszerűen lehet csomagokat készíteni, és jól dokumentált a csomagkészítés. Ráadásul a csomagkezelő függőségeit sem túl nehéz ráhegeszteni a rendszerre.
  • privilégiummentes csomagépítés –– Sima userként lehet fakeroot segítségével csomagot építeni. Így kizárt, hogy csomagépítés közben véletlenül felülírjam, teleszemeteljem, vagy tönkretegyem a készülő rendszert. Az elkészült csomag tartalmát át lehet vizsgálni telepítés előtt, hogy megfelel-e a minőségi követelményeknek.
  • fájlkonfliktusok ellenőrzése –– A pacman csomagkezelő telepítéskor és frissítéskor ellenőrzi az esetlegesen előforduló fájlkonfliktusokat más csomagokkal és a rendszerben található fájlokkal.

Az utóbbi két képességgel garantálható, hogy a rendszerhez újonnan illesztett csomagok ne károsítsák a már lerakott rendszert. A

pacman -Qo 

rendszerben_lévő_fájl parancs kiírja, hogy a megadott fájl melyik telepített csomagban található. Egy kis „shell magic”-kel könnyen megtalálhatjuk az összes olyan fájlt, amelyek a rendszeren megtalálhatóak, de az elkészített csomagokból hiányoznak, vagy fordítva.

Tegyük fel, hogy az aktuális könyvtárban vannak a rendszerre feltelepített csomagok (fájlok). Ekkor a következő parancs lexikografikusan rendezve kiírja az összes csomagban lévő fájlok „unióját”:

( for pkg in *.pkg.tar.gz ; do tar -tf $pkg ; done ) | sed -e 's/\/$//' | sort | uniq

Tegyük fel, hogy az aktuális könyvtárba van mountolva a készülendő rendszer partíciója, és nincs benne semmilyen virtuális (proc, sysfs, tmpfs, …) vagy bindelt (pl. /dev) fájlrendszer mountolva. Ekkor a következő parancs kiírja a rendszerben ténylegesen megtalálható összes fájlt:

find . | sed -e 's/^.\///' | sort

A két parancs kimenetét egy-egy fájlba irányítva egy diff-fel (vagy vimdiff-fel) megtekinthetjük a különségeket. A sed paranccsal eltávolítjuk azokat a prefixumokat, illetve szuffixumokat, amelyek csak az egyik parancs kimenetében találhatóak meg; e nélkül karakterről karakterre nézve különbözőnek tűnnének lényegében azonos sorok. A sort parancsot azért használjuk, hogy ne okozzon látszólagos különbséget a sorok különböző sorrendje a két esetben. A uniq paranccsal pedig megakadályozzuk, hogy ugyanaz a könyvtár többször megjelenjen a listában.

A fenti funkciókkal elérhető, hogy mindig tisztában legyünk azzal, hogy mi van a rendszerünkben. Az egyetlen dolog, ami még jól jönne, az a checksum számítása a telepített fájlokra, aminek segítségével egy debsums-hoz hasonló programmal ellenőrizhető lenne, hogy a telepített fájlok sérültek-e (módosultak-e) a csomagban levő változathoz képest. Ennek hiányára nem készítettem semmilyen workaroundot, mivel általában nem a futó programok írják egymást felül, hanem a DESTDIR-t nem ismerő makefile-ok és a copy-paste után nem javított telepítési utasítások rondítanak bele a szép rendszerbe; ezek ellen meg véd a privilégiummentes buildelés.

Természetesen a pacman támogatja a konfigfájlok és az install szkriptek kezelését (ghost fájlokat nem), így rosszabb esetben több munkával, de közel teljes értékű csomagokat lehet vele készíteni, ami azért fontos a rendszer karbantarthatósága szempontjából.

A csomagkészítés folyamata pacman/makepkg használatával

Egy csomag elkészítéséhez egy PKGBUILD nevű fájlba kell a megírni az elkészítéshez szükséges receptet. Ebben meg kell adni egy csomó információt a csomagról, valamint el kell készíteni egy

build

függvényt, amely tartalmazza a program buildeléséhez szükséges utasításokat. A makepkg parancs a PKGBUILD alapján automatikusan letölti a szükséges forrásfájlokat, beállítás szerint a letöltött fájlokat eltárolhatja egy beállított könyvtárban, ellenőrzi a letöltött fájlok integritását (ha adtunk meg a PKGBUILD-ben ellenőrzőösszegeket), kibontja a tömörített archívumokat (kivéve ha explicit megkérjük, hogy ezt egyes forrásfájlok esetében ne tegye), majd meghívja a PKGBUILD-ben megadott

build

függvényt. A

build

függvény a

$srcdir

könyvtárban találhatja a forrásállományokat, és a lefordított programot a

$pkgdir

könyvtárba kell telepítenie. A

build

sikeres futása esetén beállítás szerint stripelheti a binárisokat és a library-ket, tömörítheti a man oldalakat, eltávolíthatja az üres könyvtárakat, stb. Ezek után pedig becsomagolja az egészet, hozzátéve az install szkriptet (ha van) és az alapvető csomaginformációkat, és elkészült a csomag.

Az a szolgáltatás, ami nekem gyakran hiányzott a makepkg-ből, az info fájlok automatikus kezelése. (Igazából ezeket annyiban tudja automatikusan kezelni, hogy ha beállítom, akkor automatikus törli őket a csomag összecsomagolása előtt.) Ugyan az info fájlok tömörítését a man oldalak tömörítéséhez hasonlóan egyszerűen megoldhatták volna, viszont ezeknél van egy

dir

fájl a rendszerben, amelybe be kell jegyeztetni a feltelepített info fájlokat. A korrekt automatizációhoz a csomagban talált info fájlok alapján kellene legenerálni néhány utasítást, amellyel az install szkriptet kell bővíteni. Én végül azt csináltam, hogy az info fájlokat esetlegesen tartalmazó csomagok PKGBUILD-jének a

build

függvényének a végére beillesztettem a

cd ${pkgdir}/usr/share/info && ( rm -f dir ; gzip --verbose --best * ) || true

sort, amely eltávolítja csomagkészítés közben létrejött haszontalan

dir

fájlt, és tömöríti az info fájlokat, de akkor sem okoz sikertelen buildelést, ha egyáltalán nincsenek info fájlok a csomagban. A

dir

fájl karbantartására pedig írtam egy szkriptet, ami újragenerálja az aktuális könyvtárban a

dir

fájlt, és ezt szkriptet hozzáadtam a texinfo csomaghoz:

#!/bin/sh

rm -f .dir.tmp
for file in * ; do
	install-info $file .dir.tmp 2>/dev/null
done
mv -f .dir.tmp dir

A PKGBUILD fájlokban számos dolgot meglehetősen unalmas és időigényes munka kitölteni. Hogy ezen segítsek, a PKGBUILD fájlok elejét (a

build

függvény előtti részét) általában az Arch Linux megfelelő PKGBUILD-jéből copy-paste-eltem, a

build

függvény tartalmát pedig általában az LFS könyvből, de azért megszívleltem az Archos PKGBUILD eredeti tartalmát, és a DIY Linux reference buildjét is. A függőségeket, a custom licenceket és néha még más dolgokat általában kitöröltem az PKGBUILD-ből, mert a függőségkezelést az LFS könyvben összeállított sorrend rendezi, a licencek meg nem nagyon érdekelnek.

Mivel könyv szerint az új rendszer építésének csak a „vége” felé kerül telepítésre a vim, ezért a PKGBUILD fájlok megírását a host rendszer editorjával végeztem, bele

cd

zve az új rendszer partíciójának megfelelő könyvtárába. A buildelést pedig chrootban végeztem a nobody felhasználó nevében, amelyet a könyv szerint a tényleges rendszer építésének elején hozunk létre az /etc/passwd fájl létrehozásakor. A parancs általában a következőképpen néz ki:

su-tools nobody -s /bin/bash -c 'makepkg -Lc'

Azért su-tools, mivel a /tools rendszer építésekor könyv szerint a su-t su-tools néven telepítettem. Később, amikor már a su telepítve van a végleges rendszerben, akkor már lehet a su-tools helyett simán su-t használni. Továbbá fontos, hogy nobodynek legyen joga az aktuális könyvtárban új fájlokat létrehozni. A következő parancs teljesen megfelelő erre célra:

chmod 1777 .

A makepkg a teljes build folyamatot fakerootban végzi, ami bizonyos esetekben problémát jelenthet. Például a GLibC fordítás rengeteg errort dobál, ha fakerootban történik, bár egyes levelezési listákon hozzászólók szerint ezek a hibák ignorálhatóak, hiszen „különben sem kellene, hogy azok a műveletek rootként fussanak” - mondják. Továbbá vannak olyan esetek, hogy a testsuite bizonyos részeit rootként, más részeit pedig sima userként kellene futtatni (pl. coreutils). Ilyenkor a

build

függvényt több részre darabolom, és maga a

build

csak a

$pkgdir

feltöltését tartalmazza. A makepkg támogatja a csak kicsomagolást és a csak buildelést, a kettő között pedig tetszőleges műveleteket hajthatunk végre. Tehát coreutils esetében pl. a hívások a következőképpen néznek ki:

su nobody -s /bin/bash -c 'makepkg -o'
su nobody -s /bin/bash -c \
	'startdir=$(pwd); srcdir=$startdir/src; pkgdir=$startdir/pkg; \
	. /etc/makepkg.conf; . PKGBUILD; _compile'
/bin/bash -c \
	'startdir=$(pwd); srcdir=$startdir/src; pkgdir=$startdir/pkg; \
	. /etc/makepkg.conf; . PKGBUILD; _test_root'
su nobody -s /bin/bash -c \
	'startdir=$(pwd); srcdir=$startdir/src; pkgdir=$startdir/pkg; \
	. /etc/makepkg.conf; . PKGBUILD; _test_user'
su nobody -s /bin/bash -c 'makepkg -e'

Amikor nem hívjuk meg a makepkg-t, akkor magunknak kell beállítani a

startdir

,

srcdir

és

pkgdir

környezeti változókat, továbbá a makepkg.conf-ot érdemes be

source

olni, mert abban vannak bizonyos alapbeállítások (pl. CFLAGS, MAKEFLAGS, …), a PKGBUILD-et pedig egyenesen muszáj, hiszen abban van a függvény, amit meghívunk. Amikor nem használok su-t, akkor pedig azért hívok új shellt, mert így amikor kilép a meghívott shellből, akkor visszatér az eredeti könyvtárba függetlenül attól, hogy a meghívott függvény hova

cd

zett be utoljára.

Na ezt kis összefoglalót a saját rendszeremben alkalmazott csomagkezelésről és csomagkészítésről igazából csak bevezetőnek szántam ehhez a (most már: a következő) részhez, de mivel ilyen hosszúra sikeredett, ezért inkább itt elvágom, és a folytatást a következő részben írom le.

Hozzászólások

ti ilyen sok csomagot szoktatok kesziteni?

Regi mar a topic, de mi van a 4. resszel? Innentol elvben a pkgsrc-vel hawaii van, nem?
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Megcsináltam még két részt, amit még nem írtam le. A negyedikben az alaprendszer letétele (GLibC-GCC-Binutils szentháromság), az ötödikben a mindenféle csomagok lefordítása (50+ csomag). Ezután jönne a bootolás elrendezése, és aztán jön a pkgsrc bootstrapelése.

De azóta, hogy elkezdődött a szorgalmi időszak, hozzá sem nyúltam az egészhez, mert nem volt rá időm. Tervezem, hogy nyáron újra leporolom a dolgot.

Csak hogy te is hasznalhasd:
Ugyan a bsdtar becsomagolni tovabbra sem tud lzma-ba, de kicsomagolni igen: --with-lzmadec --without-lzma. Persze az lzma cuccok kellenek neki ;-)
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Azóta már megjelent az xz-utils, amely szintén lzma tömörítést használ csak rendes fájlformátuma van. libarchive amennyire néztem már támogatja az .xz és a .lzma tömörítést. A GNU berkeiben elkészült egy lzip nevű tool is, amelyik szintén lzma tömörítést használ .lz kiterjesztéssel és saját fájlformátummal. Ezt amikor néztem, akkor még nem támogatta a libarchive, de még nincs is olyan elterjedtsége. (Lásd Slackware áttért .tgz-ről .txz-re!)

Hat en most kerultem a kukac rossz oldalara - multilibes rendszer helyett nem multilibest epitettem. :S
Ami biztos: en maradok a sima gz/lzma parosnal, megnezem a pacman cuccait, a manpage-ket/info-kat mindenkepp lzma-ba akarom taroltatni, hisz egyelore az az elterjedt.
Igen, olvastam a xz utility-krol, de nincs igazabol bizodalmam benne, tul uj. Majd 2 ev mulva.

En most full pacman alapu cuccot epitek, csak nem mindenben akarom a arch cuccait kovetni (rendes init script rendszer pl.)

Kulonben tenyleg cool a bsdtar. Most kiprobaltam iso fajlon, es egy cseppet leesett az allam :) Barcsak ismerne tobb cd formatumot is... abban remenykedem, hogy udf-et tud.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.