Systeembeheer

Masterbootrecord: R.I.P.

by jacco 26.01.2010

Harddiskfabrikanten zitten niet stil. De maximale grootte van een harddisk verdubbelt ongeveer elk jaar.
We leven nu in 2010 en als je naar de beter gesorteerde computerboer gaat kun je probleemloos een 2 terabyte (TB) grote harddisk kopen. Dat kost je…

Lees het volledige artikel →

Inotify

by miekg 04.12.2009

Tijdens onze werkzaamheden bij
Octrooicentrum Nederland kwam er
iemand met het volgende probleem. Er was die nacht een bestand
aangemaakt in een directory en hij wilde eigenlijk weten welk
proces daarvoor verantwoordelijk was. Een interessante vraag en
mijn eerste reactie was: “Kan niet”.

Totdat ik me ‘s avonds bedacht dat het misschien wel kon, met het
inotify framework in Linux. Met inotify kun je je filesysteem
in de gaten houden en een seintje krijgen als er iets wordt aangemaakt,
wordt gelezen, wordt beschreven, etc.

Met man 7 inotify
lees je hoe een en ander geimplementeerd is. De bijbehorende tools kunnen met:

# apt-get install inotify-tools

geinstalleerd worden. Daarmee krijg je toegang tot de inotify API. Er
worden een tweetal tools geinstalleerd inotifywatch en inotifywait.

Inotify kan standaard maar een beperkt aantal directories in de gaten
houden. Om dat op te rekken moeten we in /proc naar een bepaalde
file een nieuwe waarde schrijven:

% sudo -s
# echo $((2 ** 16)) > /proc/sys/fs/inotify/max_user_watches

64K watches zou genoeg moeten zijn voor deze tests. Het daadwerkelijk
starten van inotifywait gaat met:

% inotifywait -m  -r --format '%:e %f' ~
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.

Waar

  • -m: blijf wachten;
  • --format '%:e %f': iets andere output (zie de manual page)
  • -r: recursief, bekijk alles onder ~.

Nu maak ik in mijn home directory een file aan:

% touch bliep

En ik zie in mijn andere schermpje aardig wat output voorbij komen,
waaronder:

CREATE bliep
OPEN bliep
ATTRIB bliep
CLOSE_WRITE:CLOSE bliep

Oftewel, er is gedetecteerd dat de file ~/bliep is aangemaakt. Verder zie
je dat touch blijkbaar de file daarna opent, de meta data verandert
(ATTRIB regel) en de file daarna sluit.

Schrijvend process ontdekken

We creeeren hier een race conditie omdat er tijd zit
tussen het daadwerkelijk aanmaken van de file en het kijken met
lsof. Dus dit is niet bullet proof.

We starten onze inotifywait en wel zo dat we kijken of de file bliep
wordt gemaakt en zo ja, dan controleren we meteen of we het proces
kunnen achterhalen.

% inotifywait -m  -r --format '%:e %f' ~ | grep -q 'CREATE bliep' && \
lsof -t ~/bliep | xargs pstree -a -h

Met lsof kun je achterhalen welk proces de file ~/bliep geopend
heeft, lsof print (als die iets vindt) de PID op standard output.
Deze PID vangen we op met xargs en geven we door aan pstree die de
naam van het process achterhaalt.

Dus als ik nu intussen in een andere terminal:

% touch bliep && tail -f bliep

geef, dan zal de inotifywait pipeline hierboven als output geven:

tail -f bliep

Oftewel het tail commando is nu nog bezig met de file bliep. Het
touch commando krijgen we niet te pakken want die is al lang en breed
klaar.

Inotify is een krachtig framework waar je interessante toepassingen voor
kunt bedenken. En waarvan dit er een is. Een ander idee is bijvoorbeeld
een on-the-fly backup toepassing.

Andere tools die gebaseerd zijn op inotify zijn onder meer:

incron
cron-like daemon met support voor filesysteem events
inotail
tail vervanger die inotify gebruikt, waarschijnlijk sneller
dan de huidige GNU tail -f

Wat meer achtergrondinformatie is
hier te vinden, gemaakt door
de originele auteur van het framework Robert Love.

Lees het volledige artikel →

Inotify

by miekg 04.12.2009

Tijdens onze werkzaamheden bij
Octrooicentrum Nederland kwam er
iemand met het volgende probleem. Er was die nacht een bestand
aangemaakt in een directory en hij wilde eigenlijk weten welk
proces daarvoor verantwoordelijk was. Een interessante vraag en
mijn eerste reactie was: “Kan niet”.

Totdat ik me ‘s avonds bedacht dat het misschien wel kon, met het
inotify framework in Linux. Met inotify kun je je filesysteem
in de gaten houden en een seintje krijgen als er iets wordt aangemaakt,
wordt gelezen, wordt beschreven, etc.

Met man 7 inotify
lees je hoe een en ander geimplementeerd is. De bijbehorende tools kunnen met:

# apt-get install inotify-tools

geinstalleerd worden. Daarmee krijg je toegang tot de inotify API. Er
worden een tweetal tools geinstalleerd inotifywatch en inotifywait.

Inotify kan standaard maar een beperkt aantal directories in de gaten
houden. Om dat op te rekken moeten we in /proc naar een bepaalde
file een nieuwe waarde schrijven:

% sudo -s
# echo $((2 ** 16)) > /proc/sys/fs/inotify/max_user_watches

64K watches zou genoeg moeten zijn voor deze tests. Het daadwerkelijk
starten van inotifywait gaat met:

% inotifywait -m  -r --format '%:e %f' ~
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.

Waar

  • -m: blijf wachten;
  • --format '%:e %f': iets andere output (zie de manual page)
  • -r: recursief, bekijk alles onder ~.

Nu maak ik in mijn home directory een file aan:

% touch bliep

En ik zie in mijn andere schermpje aardig wat output voorbij komen,
waaronder:

CREATE bliep
OPEN bliep
ATTRIB bliep
CLOSE_WRITE:CLOSE bliep

Oftewel, er is gedetecteerd dat de file ~/bliep is aangemaakt. Verder zie
je dat touch blijkbaar de file daarna opent, de meta data verandert
(ATTRIB regel) en de file daarna sluit.

Schrijvend process ontdekken

We creeeren hier een race conditie omdat er tijd zit
tussen het daadwerkelijk aanmaken van de file en het kijken met
lsof. Dus dit is niet bullet proof.

We starten onze inotifywait en wel zo dat we kijken of de file bliep
wordt gemaakt en zo ja, dan controleren we meteen of we het proces
kunnen achterhalen.

% inotifywait -m  -r --format '%:e %f' ~ | grep -q 'CREATE bliep' && \
lsof -t ~/bliep | xargs pstree -a -h

Met lsof kun je achterhalen welk proces de file ~/bliep geopend
heeft, lsof print (als die iets vindt) de PID op standard output.
Deze PID vangen we op met xargs en geven we door aan pstree die de
naam van het process achterhaalt.

Dus als ik nu intussen in een andere terminal:

% touch bliep && tail -f bliep

geef, dan zal de inotifywait pipeline hierboven als output geven:

tail -f bliep

Oftewel het tail commando is nu nog bezig met de file bliep. Het
touch commando krijgen we niet te pakken want die is al lang en breed
klaar.

Inotify is een krachtig framework waar je interessante toepassingen voor
kunt bedenken. En waarvan dit er een is. Een ander idee is bijvoorbeeld
een on-the-fly backup toepassing.

Andere tools die gebaseerd zijn op inotify zijn onder meer:

incron
cron-like daemon met support voor filesysteem events
inotail
tail vervanger die inotify gebruikt, waarschijnlijk sneller
dan de huidige GNU tail -f

Wat meer achtergrondinformatie is
hier te vinden, gemaakt door
de originele auteur van het framework Robert Love.

Lees het volledige artikel →

Vol filesysteem en output redirection

by miekg 24.06.2009

Tijdens het geven van een cursus maakte een cursist mij attent op
het volgende:

$ ps -ef > /tmp/file

op een vol filesystem (/tmp is 100% gevuld) geeft dit geen foutmelding!

Dit artikel beschrijft de achter liggende oorzaken en geeft een
verklaring voor dit gedrag.

Eerst is het zaak om een filesystem (partitie) 100% gevuld te krijgen.
Het filesystem dat we gebruiken is gemount onder /media/disk.

$ cp /dev/zero /media/disk/GROTEFILE
cp: writing `/media/disk/GROTEFILE': No space left on device

Om te testen maken we een klein test programmaatje in C:

int
main(void)
{
    write(1, "hallo\n", 6);
}

Dit schrijft 6 bytes hallo\n naar standard output. Let op: we
vergeten hier opzettelijk om te controleren of het schrijven goed
is gegaan. De write() system call geeft namelijk terug hoeveel
karakters er daadwerkelijk zijn geschreven. Zie ook man 2 write.

Na een compilatie hebben we ons programmaatje schrijf:

$ gcc schrijf.c -o schrijf

Nu kunnen we de normale input/output redirection toepassen en die
doet wat je verwacht:

$ ./schrijf > tijdelijke_file
$ cat tijdelijke_file
hallo

Laten we dit nu eens uit proberen op het filesysteem dat 100% vol is.

$ ./schrijf > /media/disk/tijdelijke_file

Geen foutmeldingen – dus blijkbaar is het goed gegaan.

$ ls -l /media/disk/tijdelijke_file
-rw-rw-r-- 1 miekg miekg 0 Jun 10 12:47 /media/disk/tijdelijke_file

De file is blijkbaar wel aangemaakt, maar heeft als grootte 0 bytes. We
kunnen ons nu 2 vragen stellen. Waarom is de file eigenlijk aangemaakt,
het filesystem zat toch vol? En ten tweede, waarom zit er geen data in
de file aangezien er geen enkele foutmelding is?

Directories

Als je onder Linux (en ook Unix) een directory aanmaakt dan geeft ls
-ldh
weer dat deze (lege) directory 4.0K groot is:

$ mkdir /tmp/test
$ ls -ldh /tmp/test
drwxrwxr-x 2 miekg miekg 4.0K Jun 10 12:52 /tmp/test

De 4.0K wordt alvast gereserveerd, als ik nu files (en/of
subdirectories) aanmaak onder /tmp/test zal de grootte van
de directory /tmp/test pas vergroot worden als we over
de 4.0K heen gaan. Typisch wordt de grootte dan 12.0K (op Linux)

$ cd /tmp/test
$ for i in $(seq 0 260); do echo $i; touch file.$i; done
$ ls -ldh /tmp/test
drwxrwxr-x 2 miekg miekg 12K Jun 10 12:59 /tmp/test

Dus zolang je de reservering niet overschrijdt kun je (lege) files
blijven aanmaken.

Op een vol filesysteem is het meestal nog mogelijk om een file
aan te maken als je daarmee maar niet de reservering overschrijdt.

Schrijven van de data

Als we ./schrijf > media/disk/tijdelijke_file opschrijven, dan
weet schrijf niet dat hij naar disk schrijft. schrijf schrijft
naar zijn stdout. En in dit geval controleert schrijf ook niet of het
schrijven gelukt is. Als we deze controle wel inbouwen, dan is de
situatie geheel anders.

Eerst passen we de source aan:

int
main(void)
{
    if (write(1, "hallo\n", 6) != 6) {
        write(2, "schrijf: schrijf fout\n", 20);
        return 1;
    }
    return 0;
}

Opnieuw compileren

$ gcc schrijf.c -o schrijf

Opnieuw testen met:

$ ./schrijf > /media/disk/tijdelijke_file
schrijf: schrijf fout

Nu is in schrijf dit euvel verholpen, maar het zal je misschien
verbazen dat het ook in andere programma’s mis gaat.

De volgende commando’s gaan allemaal de mist in zonder een foutmelding
te geven:

$ ps -ef > /media/disk/tijdelijke_file  

$ free > /media/disk/tijdelijke_file     

$ grep 'as' testfile > /media/disk/tijdelijke_file           

$ perl -e 'print "hallo";' > /media/disk/tijdelijke_file

Gelukkig zijn er ook nog programma’s die het wel goed doen:

$ who > /media/disk/tijdelijke_file
who: write error: No space left on device

Ook veel (zo niet alle) programma’s gemaakt door
GNU vertonen het juiste gedrag.

Lees het volledige artikel →

Vol filesysteem en output redirection

by miekg 24.06.2009

Tijdens het geven van een cursus maakte een cursist mij attent op
het volgende:

$ ps -ef > /tmp/file

op een vol filesystem (/tmp is 100% gevuld) geeft dit geen foutmelding!

Dit artikel beschrijft de achter liggende oorzaken en geeft een
verklaring voor dit gedrag.

Eerst is het zaak om een filesystem (partitie) 100% gevuld te krijgen.
Het filesystem dat we gebruiken is gemount onder /media/disk.

$ cp /dev/zero /media/disk/GROTEFILE
cp: writing `/media/disk/GROTEFILE': No space left on device

Om te testen maken we een klein test programmaatje in C:

int
main(void)
{
    write(1, "hallo\n", 6);
}

Dit schrijft 6 bytes hallo\n naar standard output. Let op: we
vergeten hier opzettelijk om te controleren of het schrijven goed
is gegaan. De write() system call geeft namelijk terug hoeveel
karakters er daadwerkelijk zijn geschreven. Zie ook man 2 write.

Na een compilatie hebben we ons programmaatje schrijf:

$ gcc schrijf.c -o schrijf

Nu kunnen we de normale input/output redirection toepassen en die
doet wat je verwacht:

$ ./schrijf > tijdelijke_file
$ cat tijdelijke_file
hallo

Laten we dit nu eens uit proberen op het filesysteem dat 100% vol is.

$ ./schrijf > /media/disk/tijdelijke_file

Geen foutmeldingen – dus blijkbaar is het goed gegaan.

$ ls -l /media/disk/tijdelijke_file
-rw-rw-r-- 1 miekg miekg 0 Jun 10 12:47 /media/disk/tijdelijke_file

De file is blijkbaar wel aangemaakt, maar heeft als grootte 0 bytes. We
kunnen ons nu 2 vragen stellen. Waarom is de file eigenlijk aangemaakt,
het filesystem zat toch vol? En ten tweede, waarom zit er geen data in
de file aangezien er geen enkele foutmelding is?

Directories

Als je onder Linux (en ook Unix) een directory aanmaakt dan geeft ls
-ldh
weer dat deze (lege) directory 4.0K groot is:

$ mkdir /tmp/test
$ ls -ldh /tmp/test
drwxrwxr-x 2 miekg miekg 4.0K Jun 10 12:52 /tmp/test

De 4.0K wordt alvast gereserveerd, als ik nu files (en/of
subdirectories) aanmaak onder /tmp/test zal de grootte van
de directory /tmp/test pas vergroot worden als we over
de 4.0K heen gaan. Typisch wordt de grootte dan 12.0K (op Linux)

$ cd /tmp/test
$ for i in $(seq 0 260); do echo $i; touch file.$i; done
$ ls -ldh /tmp/test
drwxrwxr-x 2 miekg miekg 12K Jun 10 12:59 /tmp/test

Dus zolang je de reservering niet overschrijdt kun je (lege) files
blijven aanmaken.

Op een vol filesysteem is het meestal nog mogelijk om een file
aan te maken als je daarmee maar niet de reservering overschrijdt.

Schrijven van de data

Als we ./schrijf > media/disk/tijdelijke_file opschrijven, dan
weet schrijf niet dat hij naar disk schrijft. schrijf schrijft
naar zijn stdout. En in dit geval controleert schrijf ook niet of het
schrijven gelukt is. Als we deze controle wel inbouwen, dan is de
situatie geheel anders.

Eerst passen we de source aan:

int
main(void)
{
    if (write(1, "hallo\n", 6) != 6) {
        write(2, "schrijf: schrijf fout\n", 20);
        return 1;
    }
    return 0;
}

Opnieuw compileren

$ gcc schrijf.c -o schrijf

Opnieuw testen met:

$ ./schrijf > /media/disk/tijdelijke_file
schrijf: schrijf fout

Nu is in schrijf dit euvel verholpen, maar het zal je misschien
verbazen dat het ook in andere programma’s mis gaat.

De volgende commando’s gaan allemaal de mist in zonder een foutmelding
te geven:

$ ps -ef > /media/disk/tijdelijke_file  

$ free > /media/disk/tijdelijke_file     

$ grep 'as' testfile > /media/disk/tijdelijke_file           

$ perl -e 'print "hallo";' > /media/disk/tijdelijke_file

Gelukkig zijn er ook nog programma’s die het wel goed doen:

$ who > /media/disk/tijdelijke_file
who: write error: No space left on device

Ook veel (zo niet alle) programma’s gemaakt door
GNU vertonen het juiste gedrag.

Lees het volledige artikel →

GNU sed -i

by miekg 08.05.2009

In een vorig blog
hebben we uitgelegd hoe kleine scriptjes kunnen uitgroeien tot goed
gedocumenteerde (en ietwat grotere) scripts. Het voorbeeld script in dat
verhaal was een wrapper rond git, om versie beheer te
vergemakkelijken. Ook zit er een stukje code die het woord $Hash$
omzet in bijvoorbeeld

`$Hash: fstab f38c788 2009-04-10 19:58:50 +0200 miekg $

Zo kun je makkelijk zien dat een bestand onder versie beheer
zit en welke versie het heeft en door wie de laatste edit slag is
gepleegd.

De code die dat doet ziet er als volgt uit:

sed -i -e 's/\$[H]ash\$'/\$H''ash:\ $id\ \$/ "$base"

De “gekke” \$[H]ash\$ zorgt ervoor dat we dit script met
zichzelf kunnen bewerken. In andere woorden: deze reguliere
expressie matchet niet zich zelf.

Maar ik wil het hier over iets anders hebben, namelijk de -i vlag
van GNU sed. Met deze vlag kun je in-place editen. Vroeger
(of bij seds die geen -i vlag hebben) ging dat als volgt:

sed -e '<bewerking>' bestand > .bestand.tmp.$$
cp .bestand.tmp.$$ bestand
rm .bestand.tmp.$$

De cp + rmcombo is nodig om sym- en harde links intact te laten.
Gebruik je bijvoorbeeld mv dan gaan links wel stuk.

Een voorbeeld:

$ touch x
$ ln x y
$ ls -li x y
424 -rw-rw-r-- 2 miekg miekg 0 May  8 15:26 x
424 -rw-rw-r-- 2 miekg miekg 0 May  8 15:26 y

Oftewel, y is nu hard gelinkt aan x. Helemaal links staan de
inode nummers, die aangeven dat beide namen naar dezelfde inode
wijzen. Nu dan het kopiëren en daarna het terug zetten met mv

$ cp y .y.tmp.$$        # kopieer de file
$ mv .y.tmp.$$ y        # move het terug
$ ls -li x y
424 -rw-rw-r-- 1 miekg miekg 0 May  8 15:26 x
437 -rw-rw-r-- 1 miekg miekg 0 May  8 15:28 y

En zoals je ziet is de inode van y veranderd naar 437 en is
dus de hard link gebroken. Hadden we cp gebruikt

$ cp .y.tmp.$$ y        # kopieer het terug
$ ls -li x y
424 -rw-rw-r-- 1 miekg miekg 0 May  8 15:26 x
424 -rw-rw-r-- 1 miekg miekg 0 May  8 15:28 y

dan was er niks aan de hand geweest. Nu terug naar de -i vlag
van de GNU versie van sed.

Deze vlag lijkt handig omdat je dan niet zelf een tussen bestand (hier
.y.tmp.$$) hoeft aan te maken, maar deze klus door sed laat opknappen. Helaas
gebruikt sed intern een mv om de file terug te verplaatsen.

In de source van sed kun je dit nakijken. Bijvoorbeeld in sed versie
4.1.5 vind je in lib/utils.c op regel 324

int rd = rename (from, to);

De rename()-functie is system call die het mv commando ook gebruikt.
Oftewel: sed -i gebruikt intern dus een mv.

Dus als je GNU sed gebruikt om sym- en/of hard-links te bewerken dan
wordt de link verbroken! Dit hebben we natuurlijk ook
gemeld

aan de ontwikkelaars. De ontwikkelaars vonden dit geen bug, aangezien
dit gedrag gedocumenteerd is. Wij hebben vooralsnog deze documentatie
niet gevonden.

Het goede nieuws is dat in GNU sed versie 4.2 dit verbeterd zal
zijn. Het slechte nieuws is dat deze versie nog niet uit is. En er
zal dan waarschijnlijk nog wat tijd over heen gaat voordat deze versie
in alle Linux distributies zit.

Lees het volledige artikel →

GNU sed -i

by miekg 08.05.2009

In een vorig blog
hebben we uitgelegd hoe kleine scriptjes kunnen uitgroeien tot goed
gedocumenteerde (en ietwat grotere) scripts. Het voorbeeld script in dat
verhaal was een wrapper rond git, om versie beheer te
vergemakkelijken. Ook zit er een stukje code die het woord $Hash$
omzet in bijvoorbeeld

`$Hash: fstab f38c788 2009-04-10 19:58:50 +0200 miekg $

Zo kun je makkelijk zien dat een bestand onder versie beheer
zit en welke versie het heeft en door wie de laatste edit slag is
gepleegd.

De code die dat doet ziet er als volgt uit:

sed -i -e 's/\$[H]ash\$'/\$H''ash:\ $id\ \$/ "$base"

De “gekke” \$[H]ash\$ zorgt ervoor dat we dit script met
zichzelf kunnen bewerken. In andere woorden: deze reguliere
expressie matchet niet zich zelf.

Maar ik wil het hier over iets anders hebben, namelijk de -i vlag
van GNU sed. Met deze vlag kun je in-place editen. Vroeger
(of bij seds die geen -i vlag hebben) ging dat als volgt:

sed -e '<bewerking>' bestand > .bestand.tmp.$$
cp .bestand.tmp.$$ bestand
rm .bestand.tmp.$$

De cp + rmcombo is nodig om sym- en harde links intact te laten.
Gebruik je bijvoorbeeld mv dan gaan links wel stuk.

Een voorbeeld:

$ touch x
$ ln x y
$ ls -li x y
424 -rw-rw-r-- 2 miekg miekg 0 May  8 15:26 x
424 -rw-rw-r-- 2 miekg miekg 0 May  8 15:26 y

Oftewel, y is nu hard gelinkt aan x. Helemaal links staan de
inode nummers, die aangeven dat beide namen naar dezelfde inode
wijzen. Nu dan het kopiëren en daarna het terug zetten met mv

$ cp y .y.tmp.$$        # kopieer de file
$ mv .y.tmp.$$ y        # move het terug
$ ls -li x y
424 -rw-rw-r-- 1 miekg miekg 0 May  8 15:26 x
437 -rw-rw-r-- 1 miekg miekg 0 May  8 15:28 y

En zoals je ziet is de inode van y veranderd naar 437 en is
dus de hard link gebroken. Hadden we cp gebruikt

$ cp .y.tmp.$$ y        # kopieer het terug
$ ls -li x y
424 -rw-rw-r-- 1 miekg miekg 0 May  8 15:26 x
424 -rw-rw-r-- 1 miekg miekg 0 May  8 15:28 y

dan was er niks aan de hand geweest. Nu terug naar de -i vlag
van de GNU versie van sed.

Deze vlag lijkt handig omdat je dan niet zelf een tussen bestand (hier
.y.tmp.$$) hoeft aan te maken, maar deze klus door sed laat opknappen. Helaas
gebruikt sed intern een mv om de file terug te verplaatsen.

In de source van sed kun je dit nakijken. Bijvoorbeeld in sed versie
4.1.5 vind je in lib/utils.c op regel 324

int rd = rename (from, to);

De rename()-functie is system call die het mv commando ook gebruikt.
Oftewel: sed -i gebruikt intern dus een mv.

Dus als je GNU sed gebruikt om sym- en/of hard-links te bewerken dan
wordt de link verbroken! Dit hebben we natuurlijk ook
gemeld

aan de ontwikkelaars. De ontwikkelaars vonden dit geen bug, aangezien
dit gedrag gedocumenteerd is. Wij hebben vooralsnog deze documentatie
niet gevonden.

Het goede nieuws is dat in GNU sed versie 4.2 dit verbeterd zal
zijn. Het slechte nieuws is dat deze versie nog niet uit is. En er
zal dan waarschijnlijk nog wat tijd over heen gaat voordat deze versie
in alle Linux distributies zit.

Lees het volledige artikel →

Evolutie van scripts

by miekg 26.01.2009

Voor het beheren van UNIX machines worden er bij AT Computing regelmatig
scripts geschreven. Deze scripts beginnen altijd eenvoudig, maar dat
blijft meestal niet zo… Dit blog artikel laat zien hoe een simpel
script uitgebreid kan worden.

N.B. Dit is een manier om je script leesbaar te houden. Er zijn
andere manieren die ook door consultants van AT Computing gebruikt
worden.
Alvorens ik inhoudelijk op het script in ga, wil ik eerst een kleine
inleiding geven:

Het scriptje zorgt ervoor dat je een file met vi kunt editen en dat het
daarna in een git repository wordt gezet zodat er versie beheer is. Ook
wordt de speciale string $Hash$ geëxpandeerd a la de $Id$ string die
bekend is van subversion en CVS.

Het origineel

Het oer script is klein genoeg (net geen 50 regels) om hier in zijn volle
glorie te tonen, zoals je ziet is het ook nog in zsh geschreven
(aangezien dat mijn favoriete shell is).

#!/bin/zsh
# a wrapper around git and vi
# expands $Hash$ to $Hash: file short_hash date committer $

[[ ! -x =git ]] && exit 1

who=${SUDO_USER:-$LOGNAME}
full=$(getent passwd $who | awk -F: '{ gsub(/,*/, ""); print $5 }')
author="$full <$who@atoom.net>"

function search_git_dir {
gpath="$1"

[[ -d "$gpath/.git" ]] && echo "$gpath" && return
[[ -z "$gpath" ]] && echo "" && return

# strip that last path component and try again
search_git_dir "${gpath%/*}"
}

for file in "$@"; do
dir=$(dirname "$file")
cd "$dir"
base=$(basename "$file")

if [[ -z $(search_git_dir "$PWD") ]]; then
    # make a new one in $PWD
    git init || exit 1
else
    #echo FOUND ONE
fi
chmod +w "$base" 2> /dev/null

if ${EDITOR:-/usr/bin/vi} "$base"; then
    [[ ! -e $base ]] && exit 0
    git add $base
    # collapse $Hash: id $ line
    sed -i -e 's/\$Hash:.*\$/$Hash$/' "$base"
    git commit --author "$author" "$base"
fi

id=$(git-show -s --pretty=format:$base\ %h\ %ci\ $who%n -- "$base")
[[ -z $id ]] && exit 1

# re-add $Hash: sha1hash$ line
sed -i -e 's/\$Hash\$'/\$Hash:\ $id\ \$/ "$base"

chmod a-w $base 2> /dev/null
cd - >/dev/null
done

Zoals je ziet weinig commentaar, maar voor de gevorderde shell
programmeur nog redelijk te behappen.

Het script is nu af, maar binnen grotere organisaties is het vaak
gewenst om een script van meer commentaar te voorzien. Als er
veel scripts in inloop zijn kunnen deze het beste centraal beheert
worden. Ook een versie beheer systeem mag niet ontbreken, bijvoorbeeld
(hoe kan het ook anders) git.

Een file header toevoegen

Aangezien we op onze interne systemen meestal de bash shell gebruiken is het
script als eerste hiernaartoe omgezet. Ook is een programma header toegevoegd.
Het commentaar in dit script is in het Engels. Dit is geen bedrijfs standaard,
maar veel van onze klanten prefereren scripts in het Engels, vandaar.

Dit ziet er als volgt uit:

#!/bin/bash
#------------------------------------------------------------------------------#
# vi: set sw=4 ts=4 ai:                            ("set modeline" in ~/.exrc) #
#------------------------------------------------------------------------------#
# Program      : vigit                                                         #
#                                                                              #
# Author       : Ton Kersten                        Ton.Kersten@ATComputing.nl #
#                AT Computing                                 Toernooiveld 104 #
#                6525 EC  Nijmegen                             The Netherlands #
#                Fax: +31-24 3527292                       Tel: +31-24 3527282 #
#                                                                              #
# Date         : 23-01-2009                                       Time : 12:18 #
#                                                                              #
# Description  : Program to edit files and commit them to git                  #
#                                                                              #
# Parameters   : The files                                                     #
#                                                                              #
# Pre reqs     : Git should be installed                                       #
#                                                                              #
# Remarks      : Stolen from and based on an idea of Miek Gieben               #
#                                                                              #
# Exit codes   :    0 -> OK                                                    #
#                <> 0 -> !OK                                                   #
#                                                                              #
# Updates      : None (yet)                                                    #
#------------------------------------------------------------------------------#
#             (c) Copyright 2009 by AT Computing, The Netherlands              #
#------------------------------------------------------------------------------#

#------------------------------------------------------------------------------#
#                    V e r s i o n   i n f o r m a t i o n                     #
#------------------------------------------------------------------------------#
# $Id:: vigit 3 2009-01-26 09:36:01Z tonk                                   $: #
# $Revision:: 3                                                             $: #
# $Author:: Ton Kersten <Ton.Kersten@ATComputing.nl>                        $: #
# $Date:: 2009-01-26 10:36:13 +0100 (Mon, 26 Jan 2009)                      $: #
#------------------------------------------------------------------------------#
#             E n d   o f   v e r s i o n   i n f o r m a t i o n              #
#------------------------------------------------------------------------------#

#------------------------------------------------------------------------------#
# Determine the program name and the 'running directory'                       #
#------------------------------------------------------------------------------#
IAM="${0##*/}"
CRD="$( [[ "$(printf "${0}" | cut -c 1 )" = "." ]] &&
  {    printf "${PWD}/${0}"
  } || {
  printf "${0}"
  })"
CRD="${CRD%/*}"
CUR="${PWD}"

#------------------------------------------------------------------------------#
# Save the shell settings                                                      #
#------------------------------------------------------------------------------#
SETA=0; [[ ${-} = *a* ]] && SETA=1
SETE=0; [[ ${-} = *e* ]] && SETE=1
SETU=0; [[ ${-} = *u* ]] && SETU=1
SETX=0; [[ ${-} = *x* ]] && SETX=1

#------------------------------------------------------------------------------#
# Set and unset the needed shell settings                                      #
#------------------------------------------------------------------------------#
set +o noclobber            # Overwrite existing files, if needed              #
set -o nounset              # Don't allow uninitialized variables              #
set +o errexit              # No returncode checking                           #

Hierbij wordt een aantal zaken altijd op dezelfde manier opgezet, zoals bijvoorbeeld
het set -o nounset waardoor een script geen onbekende variabelen toestaat.

Deze complete header kan worden gegenereerd met een, speciaal voor dit doel geschreven,
header script.

Ook wordt het script voorzien van duidelijk commentaar, hier en daar doorspekt
met humor. Het commentaar dient ervoor te zorgen dat het script voor iedereen beter
leesbaar wordt.

Een paar voorbeelden uit de uiteindelijke code:

#------------------------------------------------------------------------------#
# No git, no glory                                                             #
#------------------------------------------------------------------------------#
[[ x"$(which git 2>/dev/null)" = x"" ]] &&
{   echo "No 'git' found. Please use plain vi(m)"
  exit 1
}

of

#----------------------------------------------------------------------#
# Check if the file is already in git (get the current hash)           #
#----------------------------------------------------------------------#
initial=0
hash=$(git-show -s --pretty=format:"${base} %h %ci ${who}" -- "${base}" 2>/dev/null)
[[ x"${hash}" = x"" ]] &&
{   git add "${base}"
  git commit --author "${author}" -m "Automatic initial checkin by '${IAM}'" "${base}"
  initial=1
}

of

#--------------------------------------------------------------------------#
# Re-add the $Hash$ line                                                   #
#--------------------------------------------------------------------------#
if [[ ${havehash} != 0 ]]
then
  if [[ ${longhash} != 0 ]]
  then
  id="${id}${spc}"
  sed -i.bck -e 's!\([[:space:]]*\$Hash::\).*\$:!\1 '"${id:0:66}"'\$:!' "${base}"
  else
  sed -i.bck "s/\\\$Hash\\\$/\$Hash: ${id} \$/" "${base}"
  fi
  rm -f "${base}.bck"
fi

Conclusie

Gedurende dit proces zijn ook talloze bugjes gefixt en is het hele
script geaudit. Het eind resultaat is dus dat we hier een “Enterprise
Ready” (TM) script hebben gekregen, terwijl het oer script ook heeft
geprofiteerd van dit herschrijf proces.

Hierbij is echter wel opvallend dat het oer script slechts uit 50 regels bestaat
en de uiteindelijke versie 254 regels groot is. Ook loopt het nu op
FreeBSD (en waarschijnlijk andere Unixen) in plaats van alleen op Linux.

Het uiteindelijke script is
hier te vinden.

Lees het volledige artikel →

iSCSI disk vergroten zonder te rebooten

by jacco 19.01.2009

Als je een iSCSI-device gebruikt voor opslag is de kans groot dat de ruimte op dat device op een gegeven moment niet meer toereikend is. Je wilt die dan graag vergroten en deze vergrote ruimte door de client laten gebruiken.

Nu is dat vergroten vanaf de machine die de iSCSI-target aanbiedt normal
gesproken niet zo’n probleem. OpenFiler biedt daarvoor mogelijkheden in zijn
webinterface.

Maar hoe maak je de iSCSI-client (de machine waarop het
stukje storage gebruikt wordt) duidelijk dat zijn device – dat hij ziet als
een normale SCSI-disk als /dev/sd[a-z] – groter is dan hij een paar
minuten geleden dacht?

Rebooten zou je zeggen. Maar dat is natuurlijk niet
de Linux-manier, een beetje Linux-server gaat alleen down wanneer de spanning
eraf moet …

Er bestaat een tooltje, genaamd /sbin/partprobe, dat tot doel heeft de
kernel kennis te laten nemen van een gewijzigde partitietabel.Maar dat
biedt geen soelaas, want de partitietabel is nog niet gewijzigd. Eerst moet je
de kernel duidelijk maken dat het device groter is geworden. Dat kan, en
gelukkig hoef je daarvoor niet te rebooten.

Het magische commando heet /sbin/blockdev. Als je dit tooltje aanroept met de parameters --rereadpt en de devicenaam (/dev/sd[a-z]), zal de kernel op de hoogte worden gesteld van de gewijzigde devicegrootte.

Daarna hoef je alleen nog de partitie te resizen (bijvoorbeeld met gparted) en het device opnieuw mounten -> highscore. Het resizen van het filesystem op de betreffende partitie gaat automatisch (met dank aan gparted).

Voorbeeld:

  • Oude situatie
# df -h /iscsi
Filesystem            Size  Used Avail Use% Mounted on
/dev/sdb1            1008M   18M  940M   2% /iscsi

# fdisk -l /dev/sdb
Disk /dev/sdb: 1073 MB, 1073741824 bytes
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1        1011     1048376+  83  Linux
  • iSCSI op (bijvoorbeeld) OpenFiler oprekken tot 3 GB.
  • Filesystem unmounten (anders vindt /sbin/blockdev dat het device busy is) en /sbin/blockdev het device opnieuw laten scannen.
# umount /iscsi
# /sbin/blockdev --rereadpt /dev/sdb
# fdisk -l /dev/sdb
Disk /dev/sdb: 3154 MB, 3154116608 bytes
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1        1011     1048376+  83  Linux
  • So far, so good. Nu de partitie oprekken. Start gparted en resize de partitie naar het maximum. Daarna kun je met fdisk opvragen of het gelukt is.
# fdisk -l /dev/sdb
Disk /dev/sdb: 3154 MB, 3154116608 bytes
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1        2970     3079859+  83  Linux
  • Opnieuw mounten en je kunt gebruik maken van de extra ruimte.
# mount /dev/sdb1 /iscsi
# df -h /iscsi
Filesystem            Size  Used Avail Use% Mounted on
/dev/sdb1             2.9G   18M  2.8G   1% /iscsi

Notabene:

  • Deze procedure zal ook prima werken met (fibre channel) SAN-devices. Het principe is gebaseerd op het gegeven dat de kernel een block-based SCSI-device heeft dat onder zijn handen van grootte verandert.
  • Het tooltje /sbin/blockdev zit in het pakket util-linux (zowel bij Ubuntu als RedHat).

Lees het volledige artikel →