No advertising, no support, no bug fixes, payment in advance.
— AT&T Unix Policy (1984)

Ports low as fuck

Wenn ich Urlaub habe spiele ich Dwarf Fortress, bastle an coffeestats.org und schaue mir Protokolle an, die kein Arsch mehr kennt.

Port 9: Discard Protocol

Erster Kandidat. Wow. Ein Protokoll das deine Message nimmt und wegwirft. Eingeführt wurde es aus Debugging Gründen. In den meisten Cases möchte ich wohl eher feststellen, dass das Netz funktioniert. Nicht umgekehrt. Vielleicht eine Art Null-Hypothese-Protokoll für den TCP/IP Stack.

Wie die meisten der nachfolgenden Protokolle ist Discard in (openbsd-)inetd enthalten und können durch einen einfachen Eintrag aktiviert und zum Spielen benutzt werden.

discard   stream  tcp     nowait  root    internal
discard   dgram   udp     wait    root    internal

Port 17: QOTD Protocol

Das Quote-of-the-Day Protokoll. Total gut.

$ nc ota.iambic.com  17 
Always listen to experts. They'll tell you what can't be done, and why. Then do it. | Lazarus Long

Es gibt anscheinend eine Hand voll Server die diesen Dienst noch fahren. Wer braucht da noch einen Zitate Kalender? :)

Port 19: Character Generator Protocol

Wie bei so vielen RFCs hängt auch hier Jon Postel mit drin. Das CharGen Protokoll gibt Zeichenketten zurück. Interessant ist aber das Verhalten bei TCP und UDP.

Bei TCP schickt einem der Dienst so lange Zeichen bis die Session beendet wird. Damals muss das ziemlich viel Sinn gemacht haben. Da die Zeichenketten eindeutig sind konnte man leicht geflippte Bits oder fehlende Pakete ausmachen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
telnet localhost 19
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi
#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk
%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno
)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopq
+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr
,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrs
-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrst
./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu

Bei UDP bekommt man eine zufällige Anzahl von Zeichen (zw. 0-512). Zumindest für DDOS Attacken soll es sich ja anbieten.

Konfiguration für inetd

chargen   stream  tcp     nowait  root    internal
chargen   dgram   udp     wait    root    internal

Port 13: Daytime Protocol

Nicht zu verwechseln mit dem Vorgänger von NTP (Time Protokoll) bietet das Daytime Protokoll eine für Debugging und Menschen lesbare Variante eines Timestamps.

$ nc localhost 13 
> Mon May  6 16:27:33 2013

Die entsprechende inetd Konfiguration

daytime   stream  tcp     nowait  root    internal
daytime   dgram   udp     wait    root    internal

Es ist 2140 und der Einzige der noch Bitcoins mined ist Good Guy Greg

Zuerstmal, Bitcoin: Toller denzentraler Shit weil alle Banken böse, harter Crypto-Porn. Versteh ich alles. Total gut. Nachfolgend, Dinge die ich an Bitcoin nicht verstehe.

Running out of Coins

11 Millionen Bitcoins von maximal 21 Milltionen Bitcoins sind bereits gemined. Mehr sind wohl nicht vorgesehen. Ich liess mich gerade per Twitter aufklären, dass garnicht mehr 50 BTC pro erfolgreichen Block ausgezahlt werden, sondern nur noch 25 BTC. Mit stetiger Verringerung der Mining Rewards sollen die 21M Bitcoins wohl bis ins Jahr 2140 ausreichen.

Die Frage ist, wer mined noch wenn es keine neuen Bitcoins gibt? Achja - Transaction Fees!

If a fee is required it will most commonly be 0.0005 BTC.

Total gut. Klingt nach ‘ner cleveren Lösung. Allerdings sind das beim aktuellen Bitcoin Preis schon fast 10 Cent. Wenn der Kurs weiterhin so steigt (was bei einer begrenzten Menge an Währung nicht unwahrscheinlich ist) werden die kleinstmöglichen Fees untragbar werden. Da Fees immer optional bleiben sollen, wird es wahrscheinlich spannend, wer dann noch mined. Warum sollte man auch?

Help me out, lost my wallet

Genau wie echtes Geld werden wohl auch Bitcoin Wallets verloren gehen. Völlig egal ob durch Festplatten Crashs oder sonst irgendwelchen Gründen. Da aber “einfach nachdrucken” nicht drin ist, sind diese einfacht hart für immer weg. Potentiell werden die Coins also auch noch weniger.

Green-IT

Strom/Kosten für die letzten 24 Stunden:

Electricity Consumption: 1,011.54 megawatt hours
Electricity Cost: $151,731.62

Zum Vergleich: 1 Dollar kostet in der Herstellung ganze 0.05 Dollar

Senseless-Computing

Ich persönlich finde es extrem Schade, dass man die Rechenpower die da eingespeist wird nicht mit sinnvollen Aufgaben betrauen kann. Ich mein jede PlayStation3/SetiatHome macht verteiltes rechnen, warum nicht Bitcoin? Stattdessen: Rätselraten auf Hashes. Total gut.

Über Aufklärung, wenn ich irgendwo falsch liege oder etwas falsch verstanden habe freue ich mich natürlich. Dann kaufen wir einfach alle Bitcoins und werden hart reich.

mutt und mairix

Ich war auf der Suche nach einem neuen Mailclient. Thunderbird wird nur noch mit Security Fixes versorgt. Und außerdem hängt er mir zum Hals raus. Bloated Shitfuck.

Probierte viel aus. KMail, Geary, ClawsMail. Wobei letzteres mir noch am Besten gefallen hat. Vollkommen überzeugt war ich aber nicht.

Ich musste mir erstmal klar werden was ich will. Nach etwas hin und her habe ich mich auf folgende Punkte festgelegt:

  • Minimalistischer Client
  • GPG/PGP-Fähigkeit
  • Schnelle und bedienbare Suche

Das dürfte eigentlich ja nicht so schwer sein. mutt bietet mir zwei dieser 3 Punkte. Die Suche funktioniert zwar über l auch ganz gut, aber nicht wirklich schnell und auch nicht in meinem ganzen IMAP Account.

Was her musste war ein Mailindexer. Textbasiert gibt es da verschiedene Lösungen. Unter anderem Sup, notmuch und mairix. Habe alle Tools angetestet und mich für mairix entschieden. Eingängige Syntax, leicht verständlich.

Einziges Problem: IMAP sprechen ist nicht drin. Local maildir’n’stuff. Deshalb habe ich mir dann die folgende Lösung überlegt.

mairix durchsucht also ausschliesslich das lokale Maildir, welches ich mit offlineimap täglich (reicht mir) synce, und gibt dir Ergebnisse in eine separate mbox Search.

1
$ sudo aptitude install mairix offlineimap

Meine mairix-Config:

$HOME/.mairixrc
1
2
3
4
5
6
7
8
9
10
11
12
# local Maildir 
base=/home/noqqe/Maildir/
database=/home/noqqe/Maildir/.mairixdb

# Set this to a list of maildir folders within 'base'.
maildir=INBOX
maildir=Archives*
[...]

# Drop search results here:
mfolder=Search
mformat=mbox

Die Konfiguration für offlineimap ist auch kein Kunststück, deshalb lass ich das hier weg. Mailindex durch mairix auf das bestehende Maildir initial aufbauen. Auch gleich ein schöner Überblick über das eigene Postfach.

1
2
3
4
5
6
7
8
9
10
11
$ mairix -v 
Wrote 126633 messages 
Wrote 0 mbox headers 
Wrote 0 bytes of mbox message checksums
To: Wrote 8458 tokens 
Cc: Wrote 4289 tokens 
From: Wrote 18506 tokens 
Subject: Wrote 36705 tokens 
Body: Wrote 620414 tokens 
Attachment Name: Wrote 3313 tokens 
(Threading): Wrote 133934 tokens

Angenommen man möchte alle Mails von Amazon finden, die im Subject “Linux” enthalten kann man

1
2
$ mairix f:Amazon s:Linux
Matched 3 messages

benutzen. Der springende Punkt ist allerdings: Wie kann ich das jetzt sinnvoll in meinen Client integrieren? Mein Mutt verbindet sich mit einem IMAP Account und die Suchergebnisse liegen irgendwo lokal auf der Platte.

Dafür habe ich einen Macro in meiner .muttrc definiert.

1
2
macro index,pager L "<change-folder-readonly>/home/noqqe/Maildir/Search<enter><shell-escape>mairix " "search via mairix"
unset wait_key # do not require additional enter for shell commands

Noch 2 Cronjobs für regelmäßiges mairix und offlineimap sync und alles ist entspannt. Für Verbesserungsvorschläge und Inspirationen aus euren Mail-Setups bin ich wie immer gerne zu haben! :)

BerkeleyDB Cache Size in OpenLDAP

Ehrlich gesagt fühlt man sich als wäre es 1990, wenn man anfängt sich zu Performance Tuning für OpenLDAP schlauzulesen.

Das Default Backend BDB kann eigentlich Out-of-the-Box benutzt werden. Sobald ein paar Objects im DIT sind begegnet einem aber schnell die Cache Size.

1
2
3
$ slapindex
51252ecd bdb_db_open: warning - no DB_CONFIG file found in directory /usr/local/var/openldap-data: (2).
Expect poor performance for suffix "dc=noqqe,dc=de".

Welche Files sind interessant?

Um die Files der Berkely DB zu finden und auswerten zu können werden extra Tools benötigt.

1
2
$ aptitude install db-util
$ db_stat -h /usr/local/var/openldap-data/ -m

So werden erstmal alle statistischen Daten zu den einzelnen Datenbankfiles ausgegeben. Zwei dieser Files kommen bei OpenLDAP eine besondere Aufgabe zu. id2entry und dn2id

dn2id.bdb

Zuerst wird man etwas erschlagen von Werten. Wenn man weiss nach was man suchen muss, ist aber nur noch wenig Aufwand nötig um die richtigen Werte rauzusuchen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ db_stat -h /usr/local/var/openldap-data/ -d dn2id.bdb  

Fri Mar  8 09:11:16 2013  Local time
53162 Btree magic number
9 Btree version number
Little-endian Byte order
duplicates, sorted duplicates Flags
2 Minimum keys per-page
4096  Underlying database page size
1007  Overflow key/data size
2 Number of levels in the tree
1149  Number of unique keys in the tree
2281  Number of data items in the tree
3 Number of tree internal pages
11444 Number of bytes free in tree internal pages (6% ff)
29  Number of tree leaf pages
48174 Number of bytes free in tree leaf pages (59% ff)
4 Number of tree duplicate pages
642 Number of bytes free in tree duplicate pages (96% ff)
0 Number of tree overflow pages
0 Number of bytes free in tree overflow pages (0% ff)
0 Number of empty pages
0 Number of pages on the free list

Das einzig wichtige dabei ist folgendes Extrakt:

1
2
3
4096  Underlying database page size
3 Number of tree internal pages
29  Number of tree leaf pages

Die Page Size bei dn2id.bdb ist abhängig vom darunter liegenden Filesystem. Also ein Stück weit generisch. Anders bei id2entry.

id2entry.bdb

Dort ist die Page Size nämlich konstant 16kb. Egal welches Filesystem

1
2
3
4
$ db_stat -h /usr/local/var/openldap-data/ -d id2entry.bdb 
16384 Underlying database page size
1 Number of tree internal pages
29  Number of tree leaf pages

Berechnung der Cache Size

dn2id: ( 1 root Page + 3 internal + 29 leaf ) * 4KB = 132 KB

id2entry: ( 1 root Page + 1 internal + 29 leaf ) * 4KB = 496 KB

Insgesamt 628 KB was 643072 Bytes entspricht. Da so ein DIT aber ständig wächst möchte man vielleicht noch zwischen 20 und 50% Puffer einbauen. Ist aber jedem selbst überlassen.

Setzen der Cache Size

Variante 1:

Direkt in der von BDB dafür vorgesehenen Datei DB_CONFIG

1
2
3
vim /usr/local/var/openldap-data/DB_CONFIG
# Cache      GB  Bytes   Anzahl 
set_cachesize 0  643072  1

Variante 2:

oder in der /usr/local/etc/openldap/slapd.conf - Sektion Backend/Database

1
dbconfig set_cachesize 0 643072 1

Noch einen kurzen slapindex und der Cache ist aktiv.

Orks und Zwerge im OpenLDAP

Ich setzte mich gerade wegen LPIC 301 etwas mit OpenLDAP auseinander. Um Dinge zu testen wie Replikation über syncrepl, Index Aufbau oder Accesslists für Gruppenadmins brauche ich Datensätze.

Um diese Daten nicht alle von Hand schreiben zu müssen, hatte ich mir überlegt eine Art Automatismus zu basteln, mit der ich LDIF Files erzeugen kann. Vor kurzem habe ich (als ich auf der Suche nach einem Hostnamen war) mal larisweb.de gefunden. Dort gibt es tolle Generatoren für Fantasy Namen die man mit ein bisschen curl schoen maschinell auslesen kann.

1
2
3
4
for x in {1..5} ; do 
  curl --silent -d "neuerversuch=Start&menge_eingabe=30" http://www.larisweb.de/tools/namen_gen_zwerg.php \
  | grep "^<tr>" | sed -e 's#<tr><td>##g' -e 's#</td></tr>##g' -e 's#</td><td>#\n#'
done | sed -e 's#&szlig;#ss#g' -e 's#&uuml;#ue#g' -e 's#&auml;#ae#g' -e 's#&ouml;#oe#g' > /tmp/XPEFZKL.txt

Leider sind auf der Site keinerlei Informationen zur Lizenz oder ähnlichem angegeben. .oO(Auch wenn ich somit keinerlei Recht hätte die Namen für irgendwas zu benutzen sollte es für private Zwecke wohl ok sein, für was gäbs die Seite sonst?).

Als erstes brauche ich aber eine Gruppe ou=zwerge,dc=noqqe,dc=de für die neuen User in meinem Verzeichnis.

1
2
3
4
5
6
./ldapmodify -a -xWD "cn=admin,dc=noqqe,dc=de" << EOF
dn: ou=zwerge,dc=noqqe,dc=de
objectClass: top
objectClass: organizationalUnit
ou: zwerge
EOF

Der Einfachheit halber habe ich mich bei den erzeugten Usern für die STRUCTURAL Objektklasse inetOrgPerson gepaart mit der AUXILIARY Klasse posixAccount entschieden.

fantasy-ldif-generator.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# GID & UID
LDIFUID=10000
LDIFGID=10000
NAMEFILE=/tmp/XPEFZKL.txt

function ldif () {

    set -- $*

    UNIQ="${1:0:1}$2"
    cat << EOF
dn: uid=$UNIQ,ou=zwerge,dc=noqqe,dc=de
objectClass: top
objectClass: inetOrgPerson
objectClass: posixAccount
uid: $2
uidNumber: $LDIFUID
gidNumber: $LDIFGID
homeDirectory: /home/$UNIQ
loginShell: /bin/bash
cn: $1
sn: $2

EOF

((LDIFUID++))
}

while read name ; do
   ldif "$name"
done < $NAMEFILE

Den Output des Skripts am Besten in ein File umleiten und dann mit ldapmodify zum Directory hinzufügen.

1
./ldapmodify -ac -xWD "cn=admin,dc=noqqe,dc=de" -f zwerge.ldif

Wichtig ist dabei das -c da ich innerhalb des Scripts keine Prüfung auf duplicates durchführe. Im Continuous Operation Mode macht OpenLDAP bei Fehlern einfach weiter mit dem LDIF File.

Mirroring gitolite

gitolite hat einen eingebauten Mechanismus sich selbst zu replizieren. Finde aber unschön dass ich dafür einen zweiten gitolite betreiben und neue Repos auf beiden Systemen anlegen muss.

Wie also das Backup realisieren. rsync für git ist in an so vielen Stellen eine Verschwendung von Ressourcen, Zeit und Funktionalität. Macht einfach keinen Sinn.

Wer sich schonmal mit

ssh git@git.example.com 

auf seinen Server verbunden hat weiss aber, dass bei vorhandenem Public Key alle für diesen User verfügbaren Repositories ausgespuckt werden. Mit diesem Feature kann man sich den Slave einfach selber bauen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash 

MASTER="git@git.example.com"
BASEDIR="/data/gitslave/"

for repo in $(ssh $MASTER | grep '^ R' | awk '{print $3}') ; do
  if [ -d $BASEDIR/$repo ]; then
    cd $BASEDIR/$repo
    for rbranch in $(git branch -a | grep -v '^*\ master\|HEAD\ -' | awk -F/ '{print $3}'); do  
      git checkout $rbranch
      git pull origin $rbranch
    done
    cd -
  else
    git clone $MASTER:$repo $BASEDIR/$repo
  fi
done  &>/dev/null

Das Skript clont eigenständig alle Remote Branches und neue Repositories. Relativ pragmatischer Ansatz. Der Übersichtlichkeit halber, habe ich die logger Commands jetzt weggelassen.

Wems gefällt darfs behalten.

Wenn etwas groß wird und du merkst du hast gefailed.

Zugegeben, ein Projekt über das ich im Blog noch nie gesprochen habe ist coffeestats.org. Holger wollte sich mit PHP beschäftigen, ich mit Google Charts. Wir taten uns zusammen. Das ist nun über ein Jahr her. Es geht grundsätzlich nur darum den eigenen Koffeein-Konsum zu tracken und statistisch auszuwerten. Auf der Site haben im Grunde nur Holger, ein paar Kollegen und ich rumgelurked.

Vor kurzem empfand Jan es für sinnvoll einen kleinen Blogpost über das Projekt zu schreiben. In planet.debian.org. Klar die Hits würden ein bisschen hochgehen. Aber so wirklich an Registrierungen hab ich nicht geglaubt.

Tatsächlich haben sich aber wirklich Menschen angemeldet. 40 Stück in am ersten Tag. Von London, Montreal, Zürich, Schweden über Griechenland zu Madrid. Alles dabei. Fand ich witzig. Bis ich mir die “Overall Statistics” ansah. Wer zur Hölle säuft um 3 Uhr nachts Kaffee… und bucht das auch noch auf coffeestats.org? Erstmal etwas rumdebuggt.

1
INSERT INTO cs_coffees VALUES ('', '$userid' , NOW() );

Tjo. Problem gefunden. Wann immer jemand (egal wo) auf “Ich hab gerade ne leckere Tasse Kaffee vor mir stehen” drückte, hab ich diese unter der aktuell vorherrschenden ServerTime abgespeichert. Für Holger und mich hätte das ja super funktioniert. Wir hätten damals nicht gedacht das sich dort überhaupt jemand anmeldet. Schon garnicht jemand der nicht in unserer Zeitzone wohnt (und kein Bot ist).

Time zone settings are normally not the first thing you think of when working on a web project with PHP

Was tun? Auch wenn ich mir sicher bin dass die registrierten User da nicht auf Dauer aktiv sein werden, hatt mich das trotzdem genervt. Was ist wenn ich mal in den Urlaub fahre und Kaffee trinke … mit einem falschen Timestamp? Schrecklich!

Nach etwas herumgoogeln stellte sich heraus, dass man die Client Zeit wohl mit JavaScript feststellen möchte. Und das Date Formatting mit JS unglaublich hässlich ist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function coffeetime(d){
  function pad(n){return n<10 ? '0'+n : n}
  return d.getFullYear()+'-'
    + pad(d.getMonth()+1)+'-'
    + pad(d.getDate())+' '
    + pad(d.getHours())+':'
    + pad(d.getMinutes())+':'
    + pad(d.getSeconds())}

var d = new Date();

function AddPostData() {
  document.getElementById('coffeetime').value = coffeetime(d);
  document.getElementById('form').submit();
}

Resultiert in dem tollen Stamp 2013-02-15 12:47:41 den ich an die gewünschte HTML Form bastle. Mit viel Handliebe Nullen padden müssen, 6 Funktionen callen und getMonth Index Workaround. Pfui.

Dann noch durch den XSS und SQL Injection Validator schicken und fertig. Jetzt sind die User im Zweifel selbst für falsche Timestamps verantwortlich und ich kann beruhigt $irgendwohin in Urlaub fahren.

Sollte ich nochmal vor so eine Aufgabe gestellt werden würde ich die Timestamps aber in Unix-Epoch in UTC Zeitzone abspeichern und dann nur noch die Zeitzonen zum herumrechnen benutzen. Die Variante gefällt mir (gestern von Mullet erklärt bekommen) aber der Zug is jetzt definitiv schon abgefahren. Ums mit seinen Worten zu sagen “Fehler, die man nur einmal macht.” :)

This graph is bad and you should feel bad!

Vor kurzer Zeit ging ja dieser Tweet im Netz herum. Es ist nicht so das ich mit der Message des Graphs nicht einverstanden wäre :P oder den Spass nicht verstanden hätte, aber da ich mir zur Zeit ein paar Bücher über Statistik reinziehe bin ich was Graphen angeht etwas aufmerksam.

Warum also die Zoidberg’sche Headline für diesen Post?

Der Nachbau und die Lesbarkeit

Zuerstmal hab ich den Graphen mit Google Charts einfach nachgebaut um zu sehen, ob ich die Werte abbilden kann. Scheint so. Aber beim Herauslesen der Zahlen fängts schon an. Durch die krummen Gridlines (800 Morde/15% Marktanteil) der Achsen sind die Werte relativ bescheiden herauszulesen. Trotzdem:

Dramatische Darstellung mit Hilfe der Achsen

An den minimalen und maximalen Werten der beiden Achsen kann man schnell erkennen was das Ziel war. Größtmögliche Übereinstimmung zwischen den Variablen schaffen.

Eigentlich ist alles ganz einfach und üblich. Der durschnittliche Vertriebler möchte seine Umsätze natürlich nur realtiv zueinander darstellen, da Unterschiede so wesentlich größer aussehen (dramatisiert sind). A la “Umsatzsteigerung over 9000, bin ich geil oder was?” Gleiches ist mit den Morden hier passiert.

Zum MarketShare des IE brauch ich wohl nicht viel sagen. Welcher normale Mensch stellt prozentuale Values von 10%-90% dar?

Wenn ich beide Achsen von 0 bis $max gehen lasse, sieht die Geschichte gleich ganz anders aus.

Äpfel und Birnen vergleichen

Prozentuale Werte gegen absolute Zahlen vergleichen. Unschön. Wenn ich schon zwei Ereignisse korreliert haben möchte, dann wenigstens in der gleichen Maßeinheit. Da ich jetzt nicht sagen kann wie viele Browser 46% entsprechen würde ich wohl eher die Anzahl der Morde wählen.

Bei Morden ist es auch nochmal schwierig die richtige Population zu wählen. Also die Grundgesamtheit wie das in deutsch so schön heisst. Am nähesten dran käme die Anzahl der Einwohner in den USA. Wie viele US-Amerikaner wurden 2010 eigentlich nicht ermordet? Seltsame Null-Hypothese.

1
2
3
4
5
6
7
for x in 17500 17250 16900 16400 15550 15250 ; do echo "$x/30000000" | bc -l ; done
.00058333333333333333
.00057500000000000000
.00056333333333333333
.00054666666666666666
.00051833333333333333
.00050833333333333333

Es macht auch nicht so umbedingt Sinn hier jetzt Promille Werte zu vercharten. Gute Graphen machen bleibt eben schwierig.

Abgesehen davon weichen die Werte von denen hier sowieso total ab. Was solls. Für Graphen bin ich immer zu haben. Gelacht hab ich jedenfalls :)

Wie der Zufall so will.

Ab und zu stößt man auf etwas und fragt sich “Wie kommt das eigentlich zu stande?”. So ging es mir die Woche mit $RANDOM. Die globale Variable der bash liefert einen Wert zufälligen Wert zwischen 0 und 32767 zurück. Dieser lustige kleine Integer erzeugende Kamerad ist mir jetzt nicht umbedingt neu. 2009 schrieb ich bei Codecocktail mal einen Blogpost darüber. Anders als damals interessierte mich aber was hinter der Value steht und wie sie zustande kommt.

Randomness in Bash

Ich lud mir den Source der bash herunter und laß erstmal etwas. Sollte man eigentlich öfters mal tun, weil man auch über andere unbekannte Dinge wie z.B. $LINENO stößt. Außerdem interessante Comments:

/* From “Random number generators: good ones are hard to find”, Park and Miller, Communications of the ACM, vol. 31, no. 10,
October 1988, p. 1195. filtered through FreeBSD */

Das Paper (das nebenbei gesagt älter ist als ich) lässt schon bisschen erahnen wohin die Reise geht. Außerdem erfährt man, dass der Random Number Generator von Turbo Pascal wohl nicht so der Wahnsinn ist. Schade eigentlich.

Creative Commons Attribution-NonCommercial 2.5 License http://xkcd.com/221/

Im Source etwas umgeschaut findet man aber schnell das was man sucht. Mit etwas Bitwise XOR Spass einen Seed auf Zeit- und PID-Basis erstellt, durch den minimalen Standard aus dem Paper gejagt und schon wars das eigentlich.

1
sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());

Taugt das was?

Ich wollts dann schon noch etwas genauer wissen. Mit einem Einzeiler

1
S=100000000 ; time while [ $S -gt 0 ]; do echo $RANDOM >> random.txt ; ((S--)) ; done

habe ich dann (innerhalb 90 Minuten) 100 Millionen Zahlen generiert. Mein Thinkpad war von dem ganzen Gerechne (immerhin 18500 Values pro Sekunde) allerdings nicht so begeistert:

1
2
[45106.836033] intel ips: MCP limit exceeded: Avg temp 9513, limit 9000
[45106.836036] intel ips: MCP limit exceeded: Avg power 41641, limit 35000

Wahrscheinlich habe ich es etwas übertrieben. Was solls. Auf die Sample Size kommts schliesslich an!

Die 540MB große Datei hab ich dann etwas sortiert und in einen Graphen gestopft. Die Null-Hypothese dabei: Einer der Werte ist statistisch signifikant höher als alle anderen. Weil für Google Charts 32k Values wohl etwas zu viel ist, ein Graph mit gnuplot

Den vielen Values gedankt sei auch die Unübersichtlichkeit des Graphen. Deshalb noch ein kleinwenig mehr Statistikpr0n. Im Durchschnitt wird jede der Zahlen bei 100 Mio. Durchgängen ca. 3050 mal genannt. Wesentlich interessanter dabei ist aber die Abweichung vom Durchschnitt (siehe auch Standard Deviation). Bei der Bestimmung derer hilft eine kleine awk Zeile.

1
awk '{sum+=$1; sumsq+=$1*$1} END {print sqrt(sumsq/NR - (sum/NR)**2)}' sorted.txt

Bei meinen File wich also jeder der Werte durchschnittlich 53.6643 von der Durchschnittshäufigkeit ab. Wenn man das weiß, hat man schonmal ein ziemlich gutes Gefühl für das Auftreten der Integers.

Unterm Strich empfinde ich $RANDOM als eine mehr als zufriedenstellende Variante eines Random Number Generators. Vielleicht war aber auch einfach nur ein guter Tag (wörtlich!) für das zufällige generieren von Zahlen.

Redesign des Blogs

Eine Sache die schon ewig in meiner Taskwarrior rumeiert, ist mal was am Layout des Blogs zu ändern. Das default Theme von Octopress hat mir zwar gefallen, war aber imho sehr überladen und außerdem sieht ja irgendwie jeder Octopress Blog gleich aus. Als dann noch dieser Guide kam war ich wieder motiviert an meinem Branch weiter zu basteln :)

Folgende Dinge haben sich geändert:

  • Navigationsleiste ist gestorben
  • Die insgesamte Breite des Blogs ist aus Gründen der Lesbarkeit reduziert worden
  • Der neue grün-weiße Anstrich
  • Disqus Kommentare Zusammenfassung entfernt
  • Menüführung ist jetzt in der Sidebar

Insgesamt wirkt der Blog auf mich jetzt ruhiger und der Fokus auf den Content ist größer als vorher. Allgemein wars aber recht stressfrei. Bisschen mit SASS herumgebaut und an den Page Views rumeditiert. Voila.

Meinungen höre ich wie immer gerne. Ich wäre auch bei zu vielen “IHGITT!” Kommentaren gewillt es wieder zurück zu rollen ;)