05.02.2014

Notfall-Update für Adobe Flash player - mal wieder

Und schon wieder ein außerplanmäßiges Update des Flash-Players für Windows, Mac OS und Linux. heise empfiehlt aufgrund der Dringlichkeit die sofortige manuelle Installation. Das Problem wird schon von Bösewichten ausgenutzt. Man sollte sich nicht auf das automatischen Update-Verfahren verlassen, das in den Flash-Player eingebaut ist.

Wie üblich in ihrem freundlichen Service-Blog die passende Automation zum Herunterladen und Installieren.
Falls ein Proxy verwendet wird, das "rem" bzw. "#" entfernen.

Das Tool wget wird bei Windows noch benötigt wie hier beschrieben. Bei Linux sollte es schon vorhanden sein, da es von vielen anderen Programmen intern verwendet wird.

Für Windows wie üblich beide Varianten, ActiveX und Netscape Plugin.
@echo off

rem set http_proxy=http://192.168.100.100:3128/
set VNP=12.0.0.70
set VAX=12.0.0.70

set H=fpdownload.macromedia.com
set P=/get/flashplayer/current/licensing/win
set AX=install_flash_player_12_active_x.exe
set NP=install_flash_player_12_plugin.exe

wget http://%H%%P%/%AX% -O flash-%VAX%_ax.exe
.\flash-%VAX%_ax -install
wget http://%H%%P%/%NP% -O flash-%VNP%_np.exe
.\flash-%VNP%_np -install

Für Linux 64 bit rpm (als root ausführen oder "sudo rpm" schreiben):
#!/bin/sh

# http_proxy=http://192.168.100.100:3128/

VL=11.2.202.341

H=fpdownload.macromedia.com
PL=/get/flashplayer/current/licensing/linux

DL() { wget -N "$1/$2"; mv "$2" "$3"; }

echo Linux 64 bit rpm ...
DL http://${H}${PL} \
   flash-plugin-${VL}-release.x86_64.rpm \
   flash-${VL}.x86_64.rpm
rpm -F --force
flash-${VL}.x86_64.rpm
[Update 20140205: Tippfehler in der Windows-Versionsnummer, Hinweis auf wget für Windows]
[Update 20140221: Neue Flashversionsnummern gemäß 2. heise-Meldung]

22.01.2014

"Im Internet steht aber ..." - Teil 2 - etwas versöhnlicher, aber nicht viel

Die Geschichte mit dem verkauften Handy hat noch eine Fortsetzung gefunden.

Offensichtlich bin ich zu gutmütig. Aus meinem Angebot, das Handy einzurichten, ist eine fast einstündige Telefonsitzung geworden, weil die Käuferin des Handys die Passwörter nicht aufschreiben konnte.

Aber von Anfang an ... (ich mach jetzt latürnich nicht nochmal den Scherz mit Asterix und dem "Lorbeerkranz des Cäsar").


Ich verabredete mich nochmals mit dem Bruder, um das Handy vor Ort so weit wie möglich einzurichten. Ich erklärte ihr am Telefon genau, was mitzubringen und zu notieren war.

Obwohl nicht nachgefragt, war auch das alte, kaputte Handy mit im Beutel dabei. Der Bildschirm war so richtig schön kaputt und zersplittert. Aus dem Handy konnte ich dann SIM und SD-Karte aus- und ins neue(re) Handy einbauen.

Was dann natürlich nicht - zumindest nicht leicht erkennbar - auf dem Notizzettel stand, war das Passwort zum Google-Konto. Nach einigem Hin und her mit dem Telefon konnte ich immerhin klären, was Name und Passwort des heimischen WLANs sein sollte, und die PIN der SIM-Karte hat auf Anhieb funktioniert.

Die WLAN-Zugangsdaten habe ich dann quasi blind eingegeben und gehofft, dass keine Fehler beim Aufschreiben oder Abtippen passiert sind. Immerhin hatte das Handy dann mit der SIM-Karte Internetzugang, so dass ich guter Dinge war, den Rest der Einrichtung schnell abzuschließen.

Zuerst wollten wir uns deshalb bei einem erneuten Telefonat darauf einigen, dass ich ein neues Google-Konto einrichte, damit wenigstens ihr geliebtes Whatsapp wieder funktioniert. Ohne Google-Konto kein Play Store, und ohne Play Store kein Whatsapp. Soweit, so klar.

Als ich dann aber fragte, ob im alten Google-Konto wichtige Daten gespeichert seien, kam der Dame langsam, aber gewaltig die Erkenntnis, dass ohne das alte Konto auch keine Kontaktdaten auf das neue Handy synchronisiert werden können. Also: es muss unbedingt wieder ein Zugang zum alten Google-Konto beschafft werden. Es sollten wohl auch soviele Kontakte sein, dass das Wiederbeschaffen und Wiedereingeben richtig schmerzhaft sei.

Google ist eine extrem mißtrauische Firma, wenn man auf "Passwort vergessen" klickt. Eine Wiederherstellungs-Email kann nur geschickt werden, wenn man sich vier Tage lang nicht angemeldet hatte. Ansonsten muss man den Termin der Erstellung des Google-Kontos genau wissen, den Tag der letzten Benutzung, und eine Sicherheitsfrage beantworten. Da ich selbst aber auch eher paranoid bin und mir die Vorstellung schrecklich vorkommt, dass jemand Fremdes mein Emailkonto kapert, finde ich diese Vorsichtsmaßnahmen prinzipiell gut ;)

Zwischendurch musste ich lang und breit das Mißverständnis bekämpfen, dass nach der Einrichtung eines neuen Kontos die Daten des alten Kontos wieder zur Verfügung stünden. Weia!

Wir sind dann so verblieben, dass ich eine kurze Anleitung mitgebe, wie auf dem Handy nachträglich ein Google-Konto eingerichtet wird, wenn sie mit Hilfe ihres anderen Emailkontos ein neues Google-Passwort bekommen hat, und danach Whatsapp einrichtet.

Zwischendurch musste ich mir noch mehrfach anhören, dass ja angeblich "nur die SIM-Karte einlegen, PIN eingeben, und alles ist wie auf dem alten Handy" sowas von gelogen sei. Als ob ich Schuld daran war, dass sie das Google-Passwort nicht zur Hand hat!

Sowas ärgert mich eigentlich am meisten. Vielleicht ist es ja gar nicht an mich gerichtet, aber trotzdem nehme ich solche Vorwürfe persönlich. Natürlich, so einfach ist es wirklich: man bekommt ein neues Handy, SIM rein, einschalten, den Fragen auf dem Bildschirm folgen, PIN, Google-Email und -Passwort eingeben, und danach müsste tatsächlich das Handy mehr oder weniger wieder so aussehen wie das alte, zumindest, was Google-Kontakte, und eigentlich sogar Whatsapp angeht - diese Daten werden nämlich auf dem Server gespeichert und bei Anmeldung wieder zurück auf's Handy gespielt.

Wenn ich geahnt hätte, wie gnadenlos manche Leute Hilfsbereitschaft ausnutzen, hätte ich der Käuferin lieber den billigen Triumph gönnen sollen, dass sie das Handy zurückgeben konnte, auch wenn sie darauf keinen rechtlichen Anspruch hat. Diese ganze Geschichte hat mich soviel Zeit und Nerven gekostet, dass es mich selbst jetzt, ein paar Tage später, immer noch ärgert, dass ich mich darauf eingelassen habe,

Und das alles nur, weil es mir leidtut, wenn gute Technik sinnlos im Regal liegt, statt benutzt zu werden.

20.01.2014

"Im Internet steht aber ..." - beliebte Behauptungen

Letztens - ist jetzt schon ein paar Wochen her - hab ich eines meiner Android-Testgeräte verkauft. Leider verlief die Geschichte sehr unschön, und ich bin nach wie vor ziemlich neben der Spur.

Das Blatt mit der Beschreibung des Telefons (als Spässeken sogar mit qr-code mit Link zur (damaligen) Handygalerie der c't, die jetzt zu techstage.de umgezogen wurde) hing eine ganze Zeitlang irgendwo an einem Schwarzen Brett (so richtig echt aus Holz, kein Forum im Internet ...).

Ich hatte den Aushang schon fast vergessen - erstaunlicherweise hing er ziemlich lang dort - rief dann jemand die Telefonnummer auf dem Inserat an; er wollte dieses Gerät für seine Schwester kaufen, weil ihr Telefon kaputt war und sie dringend und schnell ein neues Gerät brauche. Allerdings kenne er sich selbst mit Handys nicht besonders gut aus.

Nun denn, aufgeladen, zur Kontrolle nochmal eingeschaltet, alle Daten gelöscht, nochmal Reset auf Werkszustand, am nächsten Tag bei einem Treffen das Gerät mit Ladekabel übergeben.

Eine Woche später ein erneuter überraschender Anruf des Im-Auftrag-Käufers - er bittet mich im Namen seiner Schwester, das Gerät zurückzunehmen, weil es ihr zu kompliziert sei. Die Sache ist ihm sichtlich peinlich, weil er jetzt natürlich zwischen den Stühlen steht. Ich biete ihm an, direkt mit der Schwester zu sprechen, damit er aus der Geschichte 'raus ist, und schon eine Stunde später bimmelt mein Handy. Ich kann aber erst abends von zuhause aus anrufen, weil ich ein paar dringende Arbeiten hatte.

Natürlich will ich das Gerät nicht zurücknehmen, ich bin im Gegenteil froh, dass es nicht unbenutzt bei mir zuhause herumliegt. Bei der Übergabe hatte ich auch deutlich gesagt, dass es keinerlei Garantie oder Gewährleistung geben kann, weil ich es auch schon gebraucht gekauft hatte.

Das Gespräch ist dann verständlicherweise ziemlich unerfreulich für beide Seiten gelaufen: die Dame erklärt mir, dass ihre Tochter ihr ebenfalls als Überraschung ein Handy gekauft hat, angeblich dasselbe Modell, das sie vorher hatte (Sony Xperia), und dass sie mit dem LG gar nicht zurecht käme. Es habe ihre Kontakte nicht und auch nicht ihr Whatsapp. Angeblich funktioniert es auch nicht, wenn man die SIM-Karte einlegt.

Nach mehreren Versuchen behauptet sie dann plötzlich, sie habe "im Internet" gefunden, dass ich das Gerät auf jeden Fall zurücknehmen muss, weil sie ein 14-tägiges Rückgaberecht habe.

Au Weia! Ob die aktuelle PIAAC-Studie doch recht hat, was die Lesekompetenz angeht? Beim Verkauf von privat gibt es die Möglichkeit, Gewährleistung vollständig auszuschließen, und das habe ich auch deutlich getan. Ich habe ihr erklärt, dass ich dem Bruder bei der Übergabe diese Tatsache mitgeteilt habe, aber weil ihr Bruder ihr das nicht weitergegeben habe, gelte es für sie nicht, und sie habe Anspruch auf Rückgabe. Dieser merkwürdigen Rechtsauffassung habe ich natürlich vehement widersprochen.

Wir sind dann schlussendlich so verblieben, dass ich ihr erklärt habe, wie sie mit dem Gerät die Grundeinstellung durchführt, also WLAN einrichten, Google-Konto einrichten, und danach aus dem Play Store Whatsapp installieren.

Beim vorherigen Handy habe das angeblich ihre Tochter für sie eingerichtet. Frag ich mich an dieser Stelle im Gespräch doch: warum macht die Tochter das dieses Mal nicht auch? War die Tochter beleidigt, dass die Mutter sich selbst um ein Ersatzgerät gekümmert hat?

Ein äußerst unerfreulicher Verlauf. Als Technik-Freak war ich froh, dass ein Gerät mit einem fast aktuellen Android 4.2.2 und brauchbaren technischen Daten (Dualcore, 800x480, 8 GB, 8 MP, SD) wieder "normal" benutzt wird, statt bei mir zu vergammeln und alle paar Wochen nur mal kurz zum Experimentieren eingeschaltet zu werden.

Noch dazu ein Anruf bei der Käuferin auf dem Handy auf meine Kosten, der über 20 Minuten gedauert hat. Da vergeht mir die Lust, nochmal Gebrauchtteile zu verkaufen, wenn dann hinterher so ein Gemecker kommt.

Letztes Jahr zum Geburtstag hab ich mir ein gebrauchtes Galaxy Nexus gegönnt und dafür das Galaxy S verkauft. Die Käuferin war (nach ein bißchen Gefeilsche um den Preis und einen wönzigen Nachlass von mir) überglücklich mit dem Gerät und ist über's ganze Gesicht strahlend aus dem Haus gegangen. Das hat mich richtig gefreut, dass mein altes Galaxy S sozusagen in "gute Hände" verkauft wurde.

Wenn man sich ein Smartphone kauft, erwarte ich auch den Willen, sich soweit einzuarbeiten, oder es meinetwegen von einem Bekannten einrichten zu lassen. Aber doch nicht beim ersten Anzeichen von Schwierigkeiten gleich alles fallen zu lassen und dem Verkäufer mit hanebüchenen juristischen Behauptungen "aus dem Internet" zu drohen ...

18.01.2014

Erbsen zählen - Perl und CGI - Dritter Teil

Im dritten Teil beschreibe ich nun das Interessanteste: die Auswertung der Daten in Text- und Grafikform, deren Prinzip ich im ersten Teil und Sammlung im zweiten Teil beschrieben habe.

Genau wie schon früher beschrieben, verwende ich das Grafikmodul GD für Perl. Dort kann man ein paar Daten hineinstecken und dann mit einem Aufruf unterschiedliche Schaubilder erzeugen lassen, als "line"-Graph, "bar"-Graph und viele andere. Der Output ist dann HTTP-konform eine Grafikdatei im gewünschten Format, z.B. PNG oder GIF, zusammen mit einer Headerzeile und dem passenden MIME-Typ.

Beim Design gibt es zwei grundsätzliche Überlegungen:
  1. Die Logdateien sammeln Daten pro überwachtem Hostname, ich muss also noch eine Summe bilden, um den Verbrauch pro Host im Verhältnis zum Rest beurteilen zu können.
  2. Ich würde gern sowohl eine Tabelle mit den aktuellen Daten sehen als auch einen Graphen, aus dem man Trends ablesen kann. Ich brauche also im CGI-Skript eine Fallunterscheidung - Text oder Grafik.
Hier kommt das komplette CGI-Skript. An ein paar Stellen füge ich Kommentare in rot oder blau ein, wenn es etwas zu erklären gibt.

Eine grundsätzliche Bemerkung vorneweg: die Datenübergabe erfolgt immer in Form von Referenzen (in C wären das Zeiger auf Datenstrukturen), deshalb verwende ich im Skript i.a. auch gleich Variablen, Arrays und Hashes, die Referenzen enthalten, damit die Übergabe an GD nicht noch unnötig Datenformate umwandeln muss.

Um das CGI-Skript bequem testen zu können, habe ich einen "Testmodus" eingebaut. Man kann generell Skripte, die das CGI-Modul verwenden, auch auf der Kommandozeile aufrufen. Die Argumente, die normalerweise in der Request-URL nach dem "?" folgen, schreibt man einfach als Pärchen mit "name=wert" hinter den Skriptnamen in die Kommandozeile, wie man hier am Beispiel sieht.
# ./accounting.pl scale=1000 testmode=99 xmax=400 ymax=300
Bei diesem Testmodus ist noch zu bedenken, dass natürlich die Umgebungsvariablen des Webservers nicht gesetzt  sind (QUERY_STRING, PATH_INFO, die SSL_*-Variablen, wenn das Skript mit https aufgerufen wurde, usw.). Ggfs. müsste man diese Variablen manuell mit passenden gefälschten Inhalten setzen, damit das Skript an den entsprechenden Stellen sinnvolle Werte bekommt.

Dem Skript kann man einen Parameter "filter" übergeben, um nur einen ganz bestimmten Hostnamen auszufiltern. Deshalb gibt es eine etwas unübersichtliche Fallunterscheidung, ob ein Filter gesetzt ist oder nicht.

Mit "mode=1" wird eine Grafik in voller Größe mit mehreren Graphen und der Gesamtsumme angezeigt; "mode=16" erzeugt eine Tabelle in Textform mit einer Zeile pro Hostname in der Logdatei und hinter jedem Hostnamen in einer Extraspalte eine wönzig kleine Grafik mit der Gesamtsumme und dem Verbrauch dieses Hosts.

#!/usr/bin/perl -w

use strict 'vars';
use strict 'refs';

use GD::Graph;
use GD::Graph::lines;
use GD::Graph::bars;
use GD::Graph::hbars;

# Die GD-Module zeichnen die verschiedenen Typen von Graphen
use CGI qw(:standard);

#use lib "/usr/local/bin";

my @data;

# Array mit Beschriftung, x-Skala und ein oder mehreren y-Datenpunkten
my %hosts;

# Array zum Speichern der Daten pro Hostname
my $graph;
my $format;
my ($xmax,$ymax);

# Größe des auszugebenden Bilds
my $query;
my $range;
my $filter;

# Regexfilter für Hostnamen
# mode=0 full graph mode all hosts
# mode=9 sum only graph
# mode=16 text mode
# mode=99 debug text mode
my $mode;
my @x;
my @y1;

# y1=summierte Daten IN
my @y2;

# y2=summierte Daten OUT
my $scale;
#my $offset;
my $title="LTE volume statistics";

# X-Beschriftung
# werte1
# werte2
# ...
@data=(
);

%hosts=(
# "dummy.moeller-seeling.local" => { "IN " => [ 0 ], "OUT" => [ 0 ] }
);

my $max=0;
my $lines=0;

# logfile inbound, outbound
my ($login,$logout);


$query=new CGI();
$mode=$query->param( 'mode' ) || 0;
$range=$query->param( 'range' ) || "60";
#$offset=$query->param( 'offset' ) || "0";
$scale=$query->param( 'scale' ) || "2500";
$filter=$query->param( 'filter' ) || "";
$xmax=$query->param( 'xmax' ) || "800";
$ymax=$query->param( 'ymax' ) || "600";
$login=$query->param( 'infile' ) || "/var/log/lte-acct.in";
$logout=$query->param( 'outfile' ) || "/var/log/lte-acct.out";
$graph = GD::Graph::lines->new($xmax, $ymax);
$format = $graph->export_format;

# 20140108-110202 IN  i9100.moeller-seeling.local              91M
# 20140108-110202 IN  lifetab.moeller-seeling.local            42M
# 20140108-110202 IN  nexus.moeller-seeling.local              8748K
# 20140108-110202 IN  vettie68.moeller-seeling.local           143M

#my $d;

sub readfile {
  my ($file,$tag,$y)=@_;

  if (open(F,"<",$file)) {
    my ($dt,$old)=("","");

      while (<F>) {
      my ($name,$num,$unit,$hm,$m);

      chomp;
# 20140107-184506 IN  lifetab.moeller-seeling.local            0
# dieser Regex zerlegt eine Zeile, $tag ist IN oder OUT      if ($filter) {
        ($name,$num,$unit,$dt,$hm,$m)=($filter,$7,$8,"$1$2$3-$4$5$6","$4$5",$5)
          if (/^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2}) $tag\s+$filter.*?\s*(\d+)([KMG]?$)/);
      }
      else {
        ($name,$num,$unit,$dt,$hm,$m)=($7,$8,$9,"$1$2$3-$4$5$6","$4$5",$5)
          if (/^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2}) $tag\s+([a-z.-][0-9a-z.-]+)\s*(\d+)([KMG]?)/);
      }
      if ($name && defined($num) && defined($unit)) {

        ++$lines;
        if ($unit eq "")  { $num>>=10; }
#       if ($unit eq "K") { $num<<=10; }
        if ($unit eq "M") { $num<<=10; }
        if ($unit eq "G") { $num<<=20; }
# alle Zahlenwerte sind in KB        if ($num>1000) {
          if ($ymax<100) { push(@x,$m); }
          else { push(@x,$hm); }
          $num>>=10;
          push(@{$hosts{$name}->{$tag}},$num);
if ($mode==99) { print STDERR "# <$_>\n# $hm $tag $filter $num $unit | $max\n"; }

# in den Logfiles stehen kumulierte Daten pro Host
# alle Werte vom selben Timestamp addieren für Gesamtsumme
          if ($dt eq $old) { $num+=pop(@{$y}); }
#         else { $num+=$offset; }
          push(@{$y},$num);
          $old=$dt;

# Timestamp aufheben für Vergleich mit nächster Zeile
          if ($max<$num) { $max=$num; }

# Maximum merken für y-Skalierung
if ($mode==99) { print STDERR "# @{$y}\n"; }
        }
      }

    }
    close(F);
  }
}

readfile($login, "IN ",\@y1);
readfile($logout,"OUT",\@y2);

if ($mode==99) {
  local($,=", ");
  foreach my $h (keys(%hosts)) {
    print STDERR "# h $h\n";
    print STDERR "# i @{$hosts{$h}->{'IN '}}\n";
    print STDERR "# o @{$hosts{$h}->{'OUT'}}\n";
  }
  print STDERR "# Sum\n";
  print STDERR "# i @y1\n";
  print STDERR "# o @y2\n";
}


# maximal die letzten $range werte aus der Datei darstellen


if ($range>0) {
  splice(@x,0,-$range);
  splice(@y1,0,-$range);
  splice(@y2,0,-$range);
  $lines=$range;
}
$data[0]=\@x;

# mode=9 nur die Summe
# mode !=9 alle Hostnamen auch einzeln zeigen
if ($mode!=9) {
  foreach my $h (keys(%hosts)) {

# inbound ist interessanter
# man könnte aber in und out anzeigen
    push(@data,$hosts{$h}->{'IN '});
#   push(@data,$hosts{$h}->{'OUT'});
  }
}
# sum of all hosts IN
push(@data,\@y1);
# sum of all hosts OUT
#push(@data,\@y2);

$max=$scale*int($max/$scale+1);

# sinnvolle obere y-Grenze ausrechnen

if ($mode==99) { print STDERR "# max=$max\n"; }

# Daten übergeben$graph->set(
  x_label       => 'Volume Date/Time, ' . $lines . " samples",
  y_label       => 'max ' . $max . ' GB',
  title         => 'LTE volume (GB)',
  y_max_value   => $max,
  y_tick_number => 20,
  y_label_skip  => ($ymax<200) ? 5: 2,
  x_labels_vertical => 1,
  x_label_skip  => $lines / 12,
) or die $graph->error;

# optional die Farben selbst festlegen
#$graph->set( dclrs => [ qw(green lred blue cyan) ] );

# gibt es überhaupt daten?

if ($lines>0) {
  if ($mode<16) {

# bilddatei erzeugen
# binmode ist nur mit windows-webserver wichtig
    print header("image/$format");
    binmode STDOUT;
    print $graph->plot(\@data)->$format();
  }

# textmodus output als tabelle
  elsif ($mode==16) {
    print CGI::header();

    print <<__HEADER__;
<HTML>
<HEAD>
<TITLE>$title</TITLE>
<META http-equiv="refresh" content="300" />
<style type="text/css">
th {
  padding : 3px;
  text-align : left;
}
td {
  padding : 3px;
  text-align : right;
}
</style>
</HEAD>
__HEADER__

print "<!-- ";
print join( " -->\n<!-- ",
           map( "$_ => $ENV{$_}", sort(keys(%ENV)))
          );
print "-->\n";

    print <<__BODY__;
<BODY>
<H1>$title</H1>
<table border="1">
<tr>
<th> Hostname </th>
<th> Download </th>
<th> Upload </th>
</tr>
__BODY__

    foreach my $h (keys(%hosts)) {
      print "<tr>\n";
      print "<th> $h</th>\n";
      print "<td>",pop(@{$hosts{$h}->{'IN '}})," MB </td>\n";
      print "<td>",pop(@{$hosts{$h}->{'OUT'}})," MB </td>\n";

# hinter jeden hostnamen einen wönzigen Detailgraphen anzeigen
if (defined($ENV{'HTTP_HOST'})) {
      print "<td><IMG SRC=http://",$ENV{'HTTP_HOST'},$ENV{'SCRIPT_NAME'},"?filter=$h&xmax=300&ymax=100&scale=",int($max/10),"</IMG></td>\n";
}
      print "</tr>\n";
    }
    print "<tr>\n";
    print "<th style='text-align:right'> &Sigma; </th>\n";
    print "<td>",pop(@y1)," MB </td>\n";
    print "<td>",pop(@y2)," MB </td>\n";
if (defined($ENV{'HTTP_HOST'})) {
      print "<td><IMG SRC=http://",$ENV{'HTTP_HOST'},$ENV{'SCRIPT_NAME'},"?mode=9&xmax=300&ymax=100&scale=",int($max/10),"</IMG></td>\n";
}
    print "</tr>\n";

    print <<__BODY__;
</table>
<DIV>
<HR/>
<ADDRESS>
ths, last changed 09.01.2014
</ADDRESS>
</DIV>
</BODY>
</HTML>
__BODY__

  }
}


[Update 20140122: Links zum 1. und 2. Teil eingefügt]

Leserbrief zu TTIP, dem Transatlantischen Abkommen zur Privatisierung der Staatssouveränität

In der WZ war neulich eine kurze Meldung, dass eine Papierfabrik vor dem Bundesverfassungsgericht klagt. Hier bei Spiegel Online wird etwas genauer erklärt, warum die Firma Klage eingereicht hat.

Diese kurze Meldung war für mich der Anlass, etwas allgemeiner über das TTIP-Abkommen zu schreiben und das als Leserbrief wegzuschicken.

Leserbrief zur Meldung "Firma PKV zieht vor das Bundesverfassungsgericht" (07.01.2014)

Die WZ berichtet, dass eine Papierfabrik vor das Bundesverfassungsgericht zieht, weil das firmeneigene Kraftwerk gesetzlich in die allgemeine Energieversorgung einbezogen wird, d.h. dass das firmeneigene Kraftwerk bei Überproduktion von außen vom Energieversorger Tennet gedrosselt wird, um weniger Strom einzuspeisen und das öffentliche Netz stabil zu halten. Die Firma betreibt dieses Kraftwerk, um Wärme für die Papierproduktion zu erzeugen, der Strom ist "nur" ein Abfallprodukt. Die Firma klagt vor dem höchsten deutschen Gericht gegen Regelungen im Erneuerbare-Energien-Gesetz wegen Eingriff in ihr Eigentum.

Diese kleine, an sich nicht besonders spannende Meldung (Firma klagt gegen Staat) ist ein guter Anlass, über das "Transatlantische Freihandels- und Partnerschaftsabkommen" TTIP nachzudenken. Dieses Abkommen soll weltweit Standards vereinheitlichen und damit den Handel vereinfachen. In dieser Schlichtheit hört sich das richtig gut an. Allerdings steckt wie üblich der Teufel im Detail: eine Vereinheitlichung bedeutet im Allgemeinen, dass immer der niedrigste Standard aus allen Partnerländern angenommen wird. Dieses Abkommen wird weltweit zwischen USA, EU, Kanada, Mexiko, Schweiz und vielen anderen Ländern abgeschlossen. Leider stammen die Entwürfe nicht von den Parlamenten, sondern von Lobbyvertretern der Industrie unter Ausschluss der Öffentlichkeit, ohne Beteiligung der nationalen Parlamente oder des EU-Parlaments, und damit faktisch ohne demokratische Kontrolle. Es steht zu befürchten, dass durch das Abkommen Umwelt- und Gesundheitsstandards untergraben und Arbeitnehmerrechte aufgeweicht werden. Die Vertragstexte dürfen nicht einmal von den Parlamentariern oder anderen Organisationen eingesehen, geschweige denn von ihnen Änderungen vorgeschlagen werden! Auffällig ist z.B. auch, dass Gewerkschaften, die ja die Arbeitnehmer repräsentieren, zur Arbeitsgruppe für Arbeitsplatz und Wachstum keinen Zugang haben.

In Deutschland formiert sich Widerstand gegen dieses Abkommen, u.a. sprechen sich Gewerkschaften, NGOs und Umweltschutzorganisationen wie ver.di, ATTAC, BUND, DNR uvm. dagegen aus. ver.di bezeichnet TTIP als "Angriff auf Löhne, Soziales und Umwelt". Die wirtschaftsfreundliche Bertelsmann-Stiftung, die übrigens an diesem Abkommen mitschreiben darf, schreckt nicht vor methodisch unsauberen Studien zurück, die schon widerlegt wurden (http://goo.gl/zSfU1R).

Nach meiner Meinung schafft dieses Abkommen nationale Regierungen und demokratisch verabschiedete Gesetze so gut wie ab. Wenn einem internationalen Konzern ein Gesetz nicht passt, wird dagegen geklagt. Würden Staaten also gegen die TTIP-Vertragsregelungen verstoßen, könnten hohe Entschädigungen an Unternehmen fällig werden. Darüber würden sogenannte Schiedsgerichte entscheiden, die keiner nationalen Gesetzgebung und Kontrolle unterworfen wären. Diese Entscheidungen von Schiedsgerichten könnten dann nicht mehr gerichtlich angegriffen werden.

Unternehmen könnten so etwa das staatliche Verbot bzw. die Kennzeichnungspflicht gentechnisch veränderter Lebensmittel oder der Gasförderung mittels Fracking verhindern, oder Entschädigungszahlungen für den Ausstieg aus der Kernenergie erzwingen. Ein weiterer Verhandlungsgegenstand von TTIP ist z.B. die Rücknahme von Kontrollen und Regeln für den Finanzsektor. In den USA sind 90% aller Lebensmittel gentechnisch verändert und müssen trotzdem nicht gekennzeichnet werden. Wollen wir die Konzerne weiter entfesseln und unsere Souveränität privatisieren?

Zwei reale Beispiele, was mit diesen Regelungen auch auf uns zukommen könnte:

Die kanadische Provinz Québec hat ein Moratorium für das Fracking von Schiefergas und Öl erlassen. Deshalb klagt das US-Unternehmen Lone Pine, welches zuvor eine Probebohrungslizenz erworben hatte, vor einem internationalen Schiedsgericht gegen den Staat Kanada und fordert 250 Millionen Dollar für den zu erwartenden Gewinnausfall. Wohlgemerkt, es geht nicht um die Rückzahlung von Gebühren oder Entschädigung für schon getätigte Ausgaben, sondern um den entgangenen Gewinn!

Der Tabakkonzern Phillip Morris klagt auf Entschädigung in Milliardenhöhe gegen den Staat Australien aufgrund entgangener Gewinne durch strengere Gesetze zum Tabakkonsum.

10.01.2014

Erbsen zählen - zweiter Teil

Nachdem ich also im 1. Teil das Grundprinzip des Erbsenzählens beschrieben habe, kommt hier ein bißchen mehr Skripting zum Einsatz, damit sich der Tag "Automation" auch lohnt ;)

Das Datensammeln an sich ist eine triviale Sache. Man braucht ein kleines Skript, das mit Hilfe von cron regelmäßig ausgeführt wird.

Ich habe einfach mal willkürlich festgelegt, dass es einmal stündlich laufen soll:

2 * * * * /usr/local/sbin/ip-accounting.sh -I /var/log/lte-acct.in -O /var/log/lte-acct.out >/dev/null 2>&1

Der Inhalt des Skripts sieht ein bißchen aufgebläht aus, weil ein paar Kommandozeilenargumente ausgewertet werden wollen. Die wichtigste Zeile im Skript habe ich nochmal extra rot markiert, das ist die eigentliche Datensammlung.
#!/bin/sh

PATH=/opt/sbin:/opt/bin:/usr/bin:/usr/sbin${PATH:+:$PATH}

bytesin=0
bytesout=0
while getopts "iI:oO:" opt
do
  case "$opt" in
  i) bytesin=1;;
  I) filein="$OPTARG";;
  o) bytesout=1;;
  O) fileout="$OPTARG";;
  esac
done
shift $(expr $OPTIND - 1)
if [ "$bytesin$bytesout$filein$fileout" = "00" ]
then
  bytesin=1
  bytesout=1
fi

dt=$(date +%Y%m%d-%H%M%S)

out=$(iptables -L INET_OUT -v | awk -v D="$dt" '{if(NR>2){printf"%s OUT %-40s %s\n",D,$7,$2}}'|sort)
if [ "$bytesout" -eq 1 ]
then
  echo "$out"
fi
if [ -n "$fileout" ]
then
  echo "$out" >> "$fileout"
fi

out=$(iptables -L INET_IN  -v | awk -v D="$dt" '{if(NR>2){printf"%s IN  %-40s %s\n",D,$8,$2}}'|sort)
if [ "$bytesin" -eq 1 ]
then
  echo "$out"
fi
if [ -n "$filein" ]
then
  echo "$out" >> "$filein"
fi
Und was genau macht diese rot markierte Zeile? Schauen wir mal genauer hin. Es sind mehrere Befehle in einer Zeile, verbunden mit dem Weiterleitungszeichen | (pipe). Diese Pipe verbindet den Output des Programms vorne mit dem Input des Programms dahinter.
iptables -L INET_IN  -v |

Der erste Befehl zeigt die bis dahin aufgelaufene Datenmenge für alle überwachten Systeme an. Wichtig ist der Switch -v, sonst bekomme ich die Datenmenge nicht zu sehen.
awk -v D="$dt" '{if(NR>2){printf"%s IN  %-40s %s\n",D,$8,$2}}' |

Die Ausgabe enthält allerdings ziemlich viel Kram, der mich gar nicht interessiert, deswegen verwende ich awk, um mir gezielt nur das zu extrahieren, was ich für meine Neugier brauche. Natürlich könnte ich normalerweise auch grep verwenden, um etwas zu suchen, aber weil ich gleichzeitig ein wenig programmieren will und einzelne Felder aus jeder Zeile des Outputs ausschneiden will, verwende ich ganz gern awk, statt grep mit cut o.ä. zu kombinieren. Spart ein wenig CPU ;)

Bei diesem Befehl benutze ich zwei eingebaute Fähigkeiten von awk: es zählt automatisch die Zeilennummern in der Variablen NR (number of records) mit, was ich verwende, um die ersten zwei Zeilen mit der Überschrift des iptables-Outputs wegzuwerfen, und es zerlegt automatisch die Zeile in "Felder" gemäß einem vorgegebenen Trennzeichen (üblicherweise Whitespace). awk arbeitet also den printf-Befehl nur ab der dritten Zeile des Inputs ab und zeigt davon nur das achte und das zweite Feld an (Hostname und Datenmenge)

Bei der Chain INET_OUT ist  übrigens  der Hostname im siebten Feld enthalten, und bei der Chain INET_IN im achten Feld, weil ich ja einmal nach "source" und einmal nach "destination" suche.

Randbemerkung: damit awk einen Timestamp mit ausgeben kann, gebe ich dem Aufruf von awk einen solchen vorgefertigten Timestamp auf der Kommandozeile mit dem Switch -v NAME=WERT mit. Das können nicht alle awk unter allen Unix-Varianten, m.W. wurde das mit dem "new awk" (nawk) unter SunOS eingeführt, GNU awk unter Linux kann es auf jeden Fall.
sort
Der letzte Befehl sortiert das ganze schlicht und einfach alphabetisch. Ich könnte auch iptables mit dem Switch -n verwenden, um die IP-Adressen zu sehen anstatt der Hostnamen. Dann wäre es angebracht, bei sort ebenfalls -n zu verwenden, um numerische Sortierung zu erzwingen.

Dieses Skript sammelt also stündlich Daten pro überwachtem Hostname. Die gesammelten Daten sehen dann so aus:
server:/share/www/cgi-bin # tail -n2 /var/log/lte-acct.in /var/log/lte-acct.out
==> /var/log/lte-acct.in <==
20140109-150202 IN  nexus.moeller-seeling.local              23M
20140109-150202 IN  vettie68.moeller-seeling.local           286M


==> /var/log/lte-acct.out <==
20140109-150202 OUT nexus.moeller-seeling.local              6752K
20140109-150202 OUT vettie68.moeller-seeling.local           13M

Wie man sieht, ist der Output menschenfreundlich und es wird immer eine halbwegs lesbare Größenordnung bei der Datenmenge angezeigt: KB, MB oder GB. Ohne Einheit sind "Byte" gemeint. Diese Kleinigkeit ist beim Programmieren einer kleiner Stolperstein gewesen ;)

Das Auswerte-Skript habe ich als CGI-Skript in Perl gelöst. Es kann sowohl eine Tabelle als auch eine Grafik erzeugen (mit dem Perl-Modul GD). Das folgt dann im nächsten Teil.

Am Ersten des Monats sollte man übrigens die Zähler wieder auf Null setzen:
iptables -L INET_IN -Z
iptables -L INET_OUT -Z
Ich habe schon ein Skript, das mir meinen Verbrauchszähler auf Null setzt, dort werde ich das noch mit einbauen.

09.01.2014

Erbsen zählen mit Linux - IP Traffic accounting

Manche unter uns wurden geplagt von langsamem DSL.

Ein paar von denen sind mutig auf LTE umgestiegen, das die Telekom und andere Anbieter in unterversorgten Gebieten anbieten müssen (zwar nur im 800 MHz-Band, aber immerhin).

Und ein paar von denen sind neugierig, welcher Mitbewohner denn nun den Traffic verursacht und schon am 20. die Quote für den ganzen Monat aufgebraucht hat ;)

Das alles und noch viel mehr kann man mit einem Linux-Server machen, wenn man seine PCs und Smartphones im Haushalt nicht direkt ins Internet lässt, sondern den Linux-Rechner als Gateway dazwischenklemmt.

Dieser Ansatz hat nebenher noch den Vorteil, dass auf dem Linux-Server ein Caching Proxy laufen kann und damit potentiell ein bißchen vom Traffic nur einmal über die externe Internetverbindung abgerufen werden muss.

Hauptsächlich ist aber das interessanteste, dass nun wirklich jede Internetverbindung am Linux sichtbar ist.

Mit dem Modul "iptables", das bei jeder Linux-Distribution dabei sein sollte, kann man eine Statistik führen, welcher Rechner oder welches Smartphone wieviel Traffic verursacht hat.

Normalerweise wird "iptables" dazu verwendet, mit Regeln festzulegen, was erlaubt ist und was nicht. Nebenbei wird aber auch mitgezählt, wieviele Bytes durch die Regeln geflossen sind. Man kann diesen Mechanismus also für den Nebeneffekt des Zählens verwenden, wenn man schlicht "keine Regel" festlegt.

Nebenbei könnte man aber natürlich auch am Proxy datenintensive Sites wie spotify usw. sperren, die besonders viel vom Datenvolumen verbrauchen.

Grundsätzlich muss man dazu einen Linux-Server mit zwei Ethernet-Schnittstellen aufbauen. Manche Mainboards haben schon zwei davon; falls nicht, kann man für sehr wenig Geld eine zweite Karte in den PC einbauen. Es bietet sich an, den Linux-Server dann auch gleich zum NAS auszubauen mit einer zweiten Festplatte (für ein gespiegeltes RAID1) oder mit mehreren (wenn man ein RAID5 haben will).

Das wichtigste kommt nun: der Router (DSL oder LTE) kommt an die zweite Schnittstelle (oBdA ab jetzt eth1 genannt), und niemand sonst darf direkt mit dem Router verbunden sein. Es ist also sinnvoll, das WLAN am Router abzuschalten oder das Passwort so zu ändern, dass sich niemand mehr anmelden kann. Außerdem muss am Linux-Server das IP-Forwarding eingeschaltet sein, damit die anderen Systeme im Haushalt die Chance haben, trotzdem ins Internet zu kommen. Wenn man das WLAN am Router abschaltet, handelt man sich noch ein zu lösendes Problem ein: man benötigt einen separaten WLAN-Access Point für das interne Netz, der für Smartphones die Basisstation wird.

Alle anderen PCs und der Linux-Server mit seiner ersten Schnittstelle (eth0) müssen an einen "internen" Switch angeschlossen werden. Das kann der interne, neue WLAN-Access Point sein, oder eine eigene kleine Kiste, ganz nach Geschmack und örtlichen Gegebenheiten.

Am Linux muss nun prinzipiell das IP-Forwarding eingeschaltet werden. Dies bedeutet, dass aller Traffic, der an eth0 ankommt und nicht für eine andere interne Station gedacht ist, automatisch an eth1 weitergeleitet wird (und von dort natürlich weiter ins Internet).

Das deute ich hier nur an, für Details mal ein bißchen im Internet nach "linux ip forwarding" suchen.
    iptables -A FORWARD -j ACCEPT -m state --state ESTABLISHED
    echo 1 > /proc/sys/net/ipv4/ip_forward
    net=192.168.1.0/24
    iptables        -A FORWARD     -j ACCEPT     -i eth0 -s "$net"
    iptables -t nat -A POSTROUTING -j MASQUERADE -o eth1 -s "$net"

    iptables -A FORWARD -j REJECT
Statt des gesamten Netzes (in der Variablen $net) kann man auch gezielt einzelne Hostnamen oder IPs angeben, aber das Setup des IP-Forwardings soll hier nicht das Thema sein.


Stattdessen will ich ein bißchen genauer auf das eigentliche Thema eingehen: das Zählen des Traffics.

Dazu richtet man mit iptables zwei zusätzliche "Chains" ein, und klinkt beide in die immer vorhandene FORWARD-Chain mit ein. Das bedeutet so etwas wie ein Verdoppeln der Daten, wobei die Duplikate in den neuen Chains (eine für Input, eine für Output) nur für das Zählen verwendet und dann weggeworfen werden.
    iptables -N INET_OUT
    iptables -N INET_IN 
    iptables -I FORWARD -j INET_IN
    iptables -I FORWARD -j INET_OUT
Diese beiden neuen Chains stattet man als nächstes mit der Information aus, für welche IP-Adresse überhaupt gezählt werden soll. Dabei muss zum Zählen pro IP-Adresse natürlich dieselbige angegeben werden als "Selektor". Einmal muss der Selektor als "source" (ausgehender Traffic) und einmal als "destination" (eingehender Traffic) verwendet werden (die Variable $ip geeignet setzen).
    iptables -I INET_OUT -s "$ip"
    iptables -I INET_IN  -d "$ip"
Danach kann man schon mal ein bißchen mit dem Gerät spielen (am Smartphone den App Store/Play Store aufrufen oder eine andere App mit Internetabrufen) und beobachten, ob sich bei den Zählern etwas bewegt.
iptables -L INET_IN -v
Um das regelmäßig anzuschauen, kann man den Befehl "watch" verwenden, der ein anderes Kommando regelmäßig aufruft. Meinem Smartphone habe ich im DNS den Namen "nexus" gegeben, und nach diesem Namen suche ich nun:

# watch -n 60 "iptables -L INET_IN -v|grep nexus"

Every 60.0s: iptables -L INET_IN -v|grep nexus
1567 1198K all -- any any anywhere nexus.moeller-seeling.local
Das Interessanteste dürfte der "inbound" sein, der hereinkommt. Typischerweise ist das Downloadvolumen eher groß, und der "outbound" besteht hauptsächlich darin, die Anfragen zu verschicken. Außer natürlich, man lädt gerade große Bilder zu Flickr oder Videos zu Youtube hoch ;)

Als nächstes richtet man dann einen cron-job ein, der diese Daten in regelmäßigen Abständen protokolliert und daraus lustige Reports erstellt. Davon erzähle ich im nächsten Beitrag, wenn ich mit Programmieren fertig bin ;)


[Update: Idee von Robert Dahlem von der LUG Frankfurt, außerdem diese FAQ]

17.12.2013

Raising Steam - Terry Pratchett - Buchbesprechung

Die Neugierde hat über die Sammelleidenschaft gesiegt (mal wieder), deshalb habe ich mir das neueste Buch von Sir Terry Pratchett als ebook  besorgt und gestern beendet. Zur Veröffentlichung hat das gebundene Buch bei amazon 19,99 € gekostet und das ebook 15,99, aber ein paar Tage später gab es schon ein Sonderangebot für die fast runde Summe von 6,87 €, und da konnte ich dann wirklich nicht widerstehen ;).  Bei englischen Büchern greift die Buchpreisbindung nicht, die Preise schwanken deshalb sehr. Heute z.B. waren die Preise 16,99 und 13,99. Es lohnt sich offensichtlich, ein paar Tage auf ein Sonderangebot zu warten ;)

"Raising Steam" ist das 40. Buch der Scheibenwelt (derzeit nur auf Englisch erhältlich), und ich habe irgendwie das Gefühl, dass in den letzten Büchern die moderne Rundwelt (also wir ...) und die Industrialisierung mit Macht Einzug halten sollen.

Im Wesentlichen handelt das Buch von der Erfindung der dampfgetriebenen Lokomotive und dem Ausbau des Schienennetzes. Nebenbei taucht als  Bösewicht Ardent nochmals auf, der schon in "Thud!" für Unruhe gesorgt hat, bis es dort am Schluss zum Waffenstillstand von Koom Valley gekommen war. Er versucht erneut, zusammmen mit den konservativen Grags den Fortschritt aufzuhalten und die Zwerge vom Rest der Welt zu isolieren. Dabei versuchen die Zwerge zunächst, die Clacks-Türme zu sabotieren; das hat allerdings nicht viel Erfolg.

Um das Fazit gleich vorneweg zu nehmen: die Kunst von Sir Terry liegt im genauen Beobachten von Menschen und ihren Beziehungen, und im feinsinnigen Kommentieren seiner Erkenntnisse. In diesem Buch hat er sich zuviel Inhalt vorgenommen, und die feinen Zwischentöne bleiben leider auf der Strecke. Es ist eine recht straff erzählte Geschichte; es gibt zwar einige witzige Fußnoten und kleine Einschübe, aber alles, was passiert, muss dazu beitragen, die Geschichte voranzubringen. Das ist sehr ungewohnt und eigentlich schade für einen Erzähler wie Sir Terry.

Der Zeitraum der Geschichte ist mit mehreren Jahren viel zu groß: von der Entwicklung der Dampfmaschine (mit Hilfe von "logger-rhythms", "quader-ratics", "slide rule", "sine" und seinem Cousin "cosine" und nicht zu vergessen der "tangent") über die Finanzierung über den Ausbau des Schienennetzes bis hin zum Putsch im Zwergenkönigreich will Sir Terry alles in einem Buch abdecken. Das mathematische Wortspiel mit "sine", "cosine" und "tangent" taucht leider im Buch allzu häufig auf, und nach dem zweiten oder dritten Mal hat es sich verbraucht. Dasselbe gilt für die Charakterisierung der Lokomotive als Maschine, die alle vier Elemente (Erde (i.S.v. Erzen für den Stahl), Wasser, Feuer, Luft) in sich vereint. Auch das wird mehrfach wiederholt und stört dann. Anfänglich konnte ich den Erfinder Simnel noch als Person erkennen, zum Ende hin wird seine Beschreibung immer oberflächlicher, genau wie seine Beziehung zu Harrys Tochter Emily.

Allein die Herstellung von genügend Stahl, um die Bahnlinien nach Sto Lat, Quirm usw. zu bauen, muss Jahre dauern, und dann wird im Eiltempo der Brücken- und Tunnelbau erfunden, um die Bahnlinie nach Uberwald fertigzustellen.

Mich beschleicht das Gefühl, dass Sir Terry zwar noch unglaublich viele Ideen für die Scheibenwelt hat, aber er Angst hat, diese Geschichten nicht mehr erzählen zu können, weil ihn seine Krankheit einholt und ihn seiner geistigen Fähigkeiten beraubt. Deswegen packt er soviel Handlung in eine einzige Geschichte.

Im Vergleich dazu ist das vorherige Buch "Snuff" eine recht kurze, knackige Geschichte mit einer einzigen stringenten Handlung, und darum herum befindet sich eben genau das, was Sir Terry gut kann: beschreiben, was die Menschen in der Geschichte ausmacht, und warum sie so sind und das tun, was sie tun. "Snuff" ist für mich neben "Nightwatch" eines der besten Bücher mit Vimes, und im Vergleich dazu ist "Raising Steam" eher schwach.

Der erste große Teil des Buches wird verwendet, um die Erfindung des Dampfantriebs zu schildern und die Nutzung des Zuges als eine Art Jahrmarktsattraktion vor den Toren von Ankh-Morpork. Danach nimmt die Geschichte Fahrt auf, und als der "Low King" einen längeren politisch bedingten Aufenthalt im Tausende Meilen von seinem Königreich entfernten Quirm verbringt, reißt Ardent die Macht im Schmaltzberg in Uberwald an sich und will sich zum neuen "Low King" krönen lassen.

Damit wird auch klar, warum Vetinari so vehement darauf bestanden hat, die Bahnlinie von Ankh-Morpork bis Uberwald mit höchster Priorität fertigzustellen: damit Rhys Rhysson, der König, so schnell wie möglich von Quirm zurück nach Schmaltzberg reisen kann. Warum hier allerdings nicht Kutschen mit Golem-Pferden eingesetzt wurden oder z.B. andere Hilfsmittel, ist wohl allein der Dramaturgie geschuldet. Der Low King wird mehrfach beschrieben beim "Pläne machen" für die Zeit nach der Ankunft. Welche Pläne das konkret sind, wird bis zum Ende nicht klar. Die Reise muss mehrere Wochen gedauert haben. Im Buch wird als Entfernung zwischen Ankh-Morpork und Uberwald 1225 Meilen angegeben, und die Lokomotive ist 30 Meilen pro Stunde schnell.

Die Reise des Zugs mitsamt der gesamten Prominenz der Nachtwache nimmt einen weiteren großen Teil des Buchs ein, es passieren Anschläge auf die Schienen, auf den Zug, und auf Versorgungsstationen für Kohle und Wasser, die allerdings alle glimpflich verlaufen.

Das Buch las sich für mich wie eine Liste von Personen, die irgendwie mitspielen. Keiner von ihnen hatte eine wesentliche Rolle, außer zu einer bestimmten Zeit am richtigen Ort zu sein und dort das Richtige zu tun: Vimes konnte die Namen der Mitverschwörer herausfinden, weil er den Zwergen mit dem Abzeichen der "Summoning Darkness" auf seinem Arm Angst machen konnte. Als die Zwerge bei dem Angriff auf die Bahn die Kupplung auftrennen konnten, hat ein Troll mit einer Art Draisinenantrieb die Waggons angetrieben und die Lokomotive wieder eingeholt. Cheery Littlebottom, Colon, Nobbs, Angua, Detritus und Sally tauchen auch "mal kurz" auf und werden erwähnt, aber keiner hat eine tragenden Rolle. Immerhin wird Carrot nicht auch auf diese Weise verheizt. Auch die Magier der Unsichtbaren Universität haben eine kurze Sprechrolle: sie dürfen feststellen, dass die Dampfmaschine keinerlei Magie enthält.

Sir Harry King, der "King of the golden river", der bislang sein Geld im untergründigen Geschäft mit der Entsorgung von Exkrementen gemacht hat, wird hier zum Eisenbahntycoon mit einem riesigen Herz, der z.B. einer Mutter eines verunglückten Möchtegern-Ingenieurs eine großzügige Pension zukommen lässt. Vom knallharten Geschäftsmann aus den vorherigen Büchern ist kaum noch etwas zu erkennen.

Schön fand ich die Begeisterung von Vetinaris Assistenten Drumknott, der dem Eisenbahnfieber verfällt und als Zweitberuf zum Lokführer wird.

Die Goblins, die zuerst in "Snuff" eine größere Rolle bis hin zur Gleichberechtigung bekamen, erhalten hier sogar noch mehr Raum für Entwicklung. Auch werden einige kulturelle Besonderheiten erwähnt, wie z.B., dass sie großen Wert darauf legen, mit ihrem vollständigen Namen angeredet zu werden. Außerdem sind sie sehr schlagfertig und können interessante Tränke aus Pflanzen und Schnecken brauen, wie Lipwig mehrfach feststellt. Bei einem Besuch in Quirm spricht "Of the Twilight the Darkness" mit dem Marquis eines kleinen Weinguts: "Nice place you got here. Don't worry about smell. I'll get used to it." - "Mon Dieu" - "Not a god, Mister Mar-kee, only goblin. And not only that, Mister Mar-kee. I'm real. If you cut me, do I not bleed? And if you do, I bleeding well cuts you too, no offence meant.". Außerdem sind die Goblins fasziniert von Technik und stürzen sich mit Begeisterung in den Betrieb der Clacks-Türme und der Eisenbahn.

Die Rückeroberung des Königsreichs ist hingegen am Ende eher unauffällig und unblutig. Dabei gibt es noch einige Überraschungen sowohl beim Low King als auch zu Vetinari. Diese Pointen will ich hier aber nicht vorwegnehmen ;)