IOException.de

Icon

Ausgewählter Nerdkram von Informatikstudenten der Uni Ulm

Veranstaltungen: Streiflicht + OpenCityCamp 2012

Zwei aktuelle Uni-verwandte Veranstaltungen, bei denen auch IOException.de Autoren kräftig beteiltigt sind:

Streiflicht 2012

Zum einen findet am 25. April der Streiflicht 2012 statt. Jedes Jahr präsentieren dort Studenten der Medieninformatik-Anwendungsfächer die Highlights ihrer umgesetzten Projekte. Die Präsentationen sind immer unterhaltsam aufbereitet, so dass auch Nicht-Informatiker etwas davon haben :-).

Zu der Veranstaltung sind neben Studenten, Professoren und Mitarbeitern natürlich auch alle anderen Interessierten herzlich eingeladen. Die Fachschaft stellt dieses Jahr wieder die Getränkeversorgung sicher, so dass es sicher ein interessanter, unterhaltsamer Abend werden wird.

Am 25. April um 18 Uhr im Hörsaal O-27 H22. Alle weiteren Infos zu den Projekten oder etwa eine Wegbeschreibung findet sich auf der Streiflicht-Seite des Institus für Medieninformatik.

Es gibt außerdem ein passendes Facebook Event zu der Veranstaltung.


OpenCityCamp

Der zweite Veranstaltungshinweis betrifft das OpenCityCamp 2012, das am 12. und 13. Mai stattfindet. Organisiert von der datalove Hochschulgruppe wird es dort an zwei Tagen Vorträge, Sessions und Workshops rund um das Thema “Open Data” geben.

Die Veranstaltung im Barcamp-Stil ist für jeden offen! Jeder ist herzlich eingeladen sich einzubringen — seien es eigene Sessions, Workshops oder Projekte, die ihr schon lange verwirklichen wollt: Hier findet sich die geeignet Plattform!

Die (kostenfreie) Anmeldung ist über occ-ulm.mixxt.de möglich.

Dort ist es auch möglich Sessions, Sessionvorschläge und Hackathonthemen einzutragen.

Benachrichtigungen für die Piratenpads skripten

Die Piratenpads sind eine bequeme, kostenfreie Möglichkeit um kollaborativ an Texten zu arbeiten oder etwa Ideen zu sammeln. In letzter Zeit nutze ich den Service mit einigen anderen Leuten sehr exzessiv. Über die verschiedenen Pads in verschiedenen Teams habe ich aber inzwischen einfach den Überblick verloren. Um herauszufinden, ob es irgendwo Neuerungen gibt muss ich alles manuell abklappern und mich bei jedem einzelnen Team einloggen. Ich bin der Ansicht, dass man genau solche Aufgaben nach Möglichkeit immer automatisieren sollte.

Deswegen habe ich einen Notificationservice für die Piratenpads geschrieben. Das Skript loggt sich ein, fragt die aktuelle Version des Dokuments ab und speichert diese lokal zwischen. Beim nächsten Überprüfen (idealerweise als Cronjob) wird die lokale Kopie mit dem online-Original verglichen. Gibt es eine Differenz wird eine E-Mailbenachrichtigung mit dem diff versandt.

Um das Ganze zu Skripten habe ich node.js mit der Bibliothek request verwendet:

Request is designed to be the simplest way possible to make HTTP calls. It support HTTPS and follows redirects by default.

Tolle Sache! Ohne HTTPS-Support kommen wir eh nicht weit, bei einigen Pads ist der Zugriff auf HTTPS eingeschränkt. Außerdem muss man sich dank der Bibliothek nicht um das mühsame Parsen von “Set-Cookie” Feldern kümmern. request übernimmt die Cookies standardmäßig einfach global für zukünftige Requests.

Um die Session zu initialisieren, also sich vor dem Login einen Cookie zu holen, sieht der Code etwa so aus:

var request = require('request'); 
var base = 'https://foo.piratenpad.de';

(function initiateSession() {
	request.get(base, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			login();
		}
	});
})();

Die Loginfunktion habe ich zusammengebaut, nachdem ich den kompletten Skriptablauf im Firefox durchgespielt habe und alle Requests mittels des (unheimlich praktischen) Live HTTP Headers Add-ons aufgezeichnet habe.

function login() {
	var options = {
			url: base + '/ep/account/sign-in', 
			form: {'email': 'john@doe.de', 'password' : 'mypw'}
	};
	
	request.post(options, function (err, res, body) {	
			request.get(base + '/' + acc.padId, function (err, resp, body) {
				// get the latest version of the pad document
				var linkToLatestVersion = body.match(/[\w\d\/\-\.]*(latest')/gi);
				linkToLatestVersion = linkToLatestVersion[0].replace("'", '');

				getLatestDocument(linkToLatestVersion);
			});			
		}
	);	
}

Die aktuelle Version des Dokuments lässt sich dann mit einem einfachen GET-Request abfragen:

function getLatestDocument(linkToLatestVersion) {
	request.get(base + linkToLatestVersion, function (err, resp, body) {
		var start = body.search('id="padcontent">') + 'id="padcontent">'.length;
		var end = body.search("<!-- /padeditor -->");
		var padContent = body.substring(start, end);
		
		// strip all tags and entities
		padContent = padContent.replace(/(<[^>]+>)|(&[#\w]+;)/gi, '');
		
		console.log(padContent.trim());
	});
}

Das Ganze zusammengefasst als ordentliches, sauber konfigurierbares, Projekt gibt es hier auf GitHub. Das Skript kann sehr einfach für ähnliche Aufgaben wiederverwendet werden. Als Anregung: Beispielsweise wäre es möglich das Uni Hochschulportal anzuzapfen um E-Mailbenachrichtigungen zu versenden, wenn neue Prüfungsergebnisse eingetragen sind.

Update: Ich habe noch die Möglichkeit hinzugefügt, Benachrichtigungen für Änderungen an dem Inhalt hinter einer URL zu konfigurieren (im Ordner simple-webpage/). Ich benutze das als simple Lösung für Seiten, die keinen RSS-Feed bereitstellen.

Globale Verteilung der WikiLeaks Mirror visualisieren

Am 2. Dezember 2010 haben verschiedene große DNS Anbieter, nach dem Release der US-Botschaftsdepeschen, aufgehört die wikileaks Domains aufzulösen. Daraufhin wurde der Inhalt der WikiLeaks-Website auf knapp 2.200 Servern gespiegelt. Die Server wurden von Freiwilligen auf der ganzen Welt zur Verfügung gestellt.

Dieser Vorfall zeigt eindrucksvoll, wie die dezentralen Strukturen des Internets genutzt werden können um Zensur zu umgehen und Informationsunterdrückung zu verhindern.

Nachdem ich im vorigen Beitrag die Ausgabe des traceroute Utilities visualisiert habe wollte ich hier noch etwas weiter machen. Dieses Mal war mein Ziel die Domains der verschiedenen Mirrors herauszubekommen, diese erst zu einer IP, dann zu WGS84 aufzulösen. Die WGS84 Koordinaten kann ich dann auf einer Erdkugel abbilden.

Die Domains herauszubekommen war einfach, es gibt verschiedene Seiten die die Adressen der Spiegelserver auf einer HTML-Seite auflisten. Diese Liste lässt sich bequem parsen. Das Auflösen zu einer WGS84-Koordinate habe ich wieder über eine freie GeoIP-Datenbank vorgenommen. Von da an konnte ich große Teile des traceroute Projekts wiederverwenden um die Domains auf eine Weltkugel zu mappen.

Ganz interessant ist, dass die Server tatsächlich auf der ganzen Welt verteilt sind. Die meisten Server stehen — jetzt nicht besonders überraschend — in Mitteleuropra. Es sind wohl aber auch einige wenige in China. Natürlich geben die Ergebnisse der GeoIP Datenbank keine sicher bestimmten Standorte wieder, aber ich finde der Trend ist doch schön erkennbar.

Ich habe ein kleines Video zu der globalen Verteilung zusammengestellt:

Der Direktlink: vimeo.

Ein graphisches Frontend für traceroute

Um mich näher mit Processing und OpenGL auseinanderzusetzen habe ich ein Frontend für das Unix Programm traceroute geschrieben. Die Ausgabe von traceroute ist eine Liste mit Stationen die ein Paket auf seinem Weg durch das Netzwerk passiert. Dies kann etwa für das Debuggen einer Netzwerkverbindung genutzt werden.

Technisch wird dies über das “Time-To-Live”-Feld im Header von IP-Paketen realisiert. Der TTL-Eintrag gibt an, nach wie vielen Stationen ein Paket verworfen werden soll. Jeder Router, den das Paket passiert, dekrementiert dieses Feld. Ist die TTL bei 0 angelangt wird das Paket verworfen und der Absender mittels einer ICMP-Nachricht TIME_EXCEEDED benachrichtigt.

traceroute macht sich diesen Umstand zunutze indem immer wieder Pakete an den Zielhost gesendet werden. Die TTL wird dabei schrittweise erhöht bis das Ziel erreicht ist. Die Hosts auf dem Weg werden sich nach und nach mit ICMP-Nachrichten melden, so dass wir dann Informationen über den Absender gewinnen und somit (hoffentlich) die einzelnen Stationen auf unserer Route identifizieren können. Diese Route muss hierbei nicht zwangsläufig korrekt sein. Es können sich aus verschiedenen Gründen Abweichungen ergeben, etwa durch Firewalls die aus Sicherheitsgründen ICMP gleich komplett deaktivieren.

Für die Visualisierung habe ich traceroute, wie im letzten Beitrag beschrieben, als externes Programm an Processing angebunden. Das Frontend liest dabei die Ausgabe des Kommandoaufrufs traceroute domain.org bis EOF. Jede Zeile wird geparsed, dabei werden die einzelnen Hosts der Route nach IP-Adressen aufgelöst und anschließend die Koordinate bestimmt. Die Koordinaten können anschließend mit etwas sin/cos Grübeln auf einer Erdkugel abgebildet werden. Das Auflösen nach Geolocations habe ich mittels einer GeoIP-Datenbank realisiert. GeoIP-Datenbanken stellen eine Zuordnung IP zu Koordinate dar. Natürlich nur mit einer bestimmten Wahrscheinlichkeit und auch nicht völlig exakt, aber für unsere Zwecke reicht das. Es gibt hier einige freie und natürlich jede Menge kommerzielle Anbieter. Ich habe mich dabei für die freie GeoLite City von Maxmind entschieden. Im Endeffekt löse ich so jetzt eine IP-Adresse zu einer WGS84-Koordinate auf.

Für das Frontend habe ich in Java mithilfe der Processing API eine Visualisierung geschrieben. Die Textur der Weltkugel wird mittels eines in GLSL geschriebenen Shaders weiter verändert. Bibliotheken, die ich verwendet habe: GLGraphics (OpenGL Rendering Engine für Processing), controlP5 (Button, Slider, Textfield) und toxiclibs (Interpolation & andere numerische Methoden).

Den Sourcecode habe ich unter MIT auf GitHub veröffentlicht: visual-traceroute.

Ein bisschen Eye Candy gibts in dem Video unter diesem Beitrag bzw. hier als Direktlink auf Vimeo.

Processing als Frontend: Anbindung an andere Programmiersprachen

Processing ist eine Programmiersprache, die entwickelt wurde um es möglichst einfach zu machen Visualisierungen zu erstellen und grafische Arbeiten am Computer erstellen zu können. Der Sprachumfang (bzw. die API) ist sehr übersichtlich und ist hier auf einer Seite zusammengefasst.

Für Processing existieren heute enorm viele Bibliotheken und die Sprache wurde u.a. nach Javascript portiert bzw. wird aktuell nach Android portiert. Somit wird Processing 2.0 auch das Ausführen im Browser und auf einem Android-Gerät unterstützen (bzw. trunk unterstützt es schon :) ).

Toll ist, dass sich die Processing-Bestandteile als externe Java Bibliotheken referenzieren lassen. Wir können also in unserem Java-Projekt sehr bequem die Processing-Möglichkeiten ausschöpfen. Wir können außerdem jede Menge Processing-Bibliotheken referenzieren, etwa für Physik, Typographie, etc..

Dieser Beitrag soll zeigen wie einfach es ist in Java mit den Processing Bibliotheken ein reines Frontend zu schreiben. Bei vielen Aufgaben bietet es sich an eine Sprache zu verwenden, die sich speziell für diese Aufgabe eignet. In manchen Fällen wollen wir also das Frontend in Processing schreiben, das Backend aber in einer anderen Sprache.

Wir haben also zwei verschiedene Programme. Wie verbinden wir die Beiden jetzt?

Unser Processing-Programm startet einen Thread der wiederum das externe Programm startet und konstant die Ausgabe des externen Programmes liest (bis EOL). Diese Ausgabe wird konstant geparset und in einem Objekt, beispielsweise einer Liste, abgelegt.

Das Hauptprogramm hat die Aufgabe unsere “Zeichenfläche” darzustellen. Processing sieht dazu eine Methode draw() vor, die entsprechend der Framerate oft aufgerufen wird. In der draw() Methode lesen wir das Objekt des Threads ein und berücksichtigen dies für unsere Darstellung.

In Processing sieht das Ganze dann so aus:

/*
 * Aufgabe des Threads:
 * Die Ausgabe unseres "Backend"-Programmes zu lesen und zu parsen.
 */
public class Stream extends Thread { 
  /* Für jede Zeile der Ausgabe erstellen wir einen Eintrag in der Liste */
  Vector <StreamEntry > entries = new Vector <StreamEntry >();

  public void run() { 
    String line; 

    try {
      Process p = Runtime.getRuntime().exec("ping uni-ulm.de");
      BufferedReader input = new BufferedReader(
         new InputStreamReader(p.getInputStream()));

      while ((line = input.readLine()) != null) 
        entries.add(new StreamEntry(line));

      input.close(); 
    } catch (Exception err) {
      err.printStackTrace();
    }
}

public class StreamEntry {
  public int time;

  public StreamEntry(String line) {
    /* Parsing action ... */
    this.time = ...;
  }
}

Unser Hauptprogramm zur Darstellung:

public class Main extends PApplet {
  /* Unser Thread, der die Ausgabe des Backends liest */
  private Stream stream;

  public void setup() {
    size(1024, 768);

    stream = new Stream();
    stream.start();
  }

  /* draw() wird entsprechend der Bildwiederholrate oft aufgerufen */
  public void draw() {
    background(BLACK);

    /* Wir geben alle Einträge der Ausgabe aus */
    for (StreamEntry s : stream.entries)
      text(s.time, 10, y+=15);
  }
}

Der zweite Thread verarbeitet also kontinuierlich die Ausgabe des Backend. Unser Hauptprogramm aktualisiert bei jedem Aufruf von draw() die Zeichenfläche und berücksichtigt hierbei die Ausgabe des Backends.

Sopra-Projekt: Routenplaner für das Gelände der Uni Ulm

An der Uni Ulm muss jeder Bachelorstudent in einem Informatik / Informatik verwandten Studiengang das Sopra absolvieren. Das ist ein umfangreiches Softwareprojekt, das sich über zwei Semester erstreckt. Im ersten Semester steht dabei die Erfassung von Anforderungen, die Planung und das Pflichtenheft im Vordergrund. Das passiert in einem Team aus drei Studenten. Im zweiten Semester werden zwei Teams zusammengelegt, so dass fortan sechs Leute an der Implementierung arbeiten.

Alle Teams, die am Sopra teilnehmen, müssen dasselbe Projekt implementieren, wobei die Projekte jedes Jahr wechseln. Dieses Jahr war die Aufgabe von Grund auf einen Routenplaner für die Universität zur erstellen. Die Ausgangsbasis waren die Baupläne der Uni, die vom Bauamt zur Verfügung gestellt wurden.

Einige Problemstellungen des Projekts waren eine besondere Herausforderungen. Beispielsweise müssen handelsübliche Straßen-Routenplaner nicht mit fünf Stockwerken die übereinander liegen zurecht kommen.

Zum Routing: Algorithmen für Routingprobleme (Dijkstra, Bellman-Ford, etc.) verwenden üblicherweise einen gewichteten Graphen. Unser Team hat sich dafür entschieden eine graphenbasierte NoSQL-Datenbank zu verwenden: Neo4J. Da wir vorhatten einen Routeplaner zu entwickeln bot es sich einfach an eine Datenbank zu verwenden die inhärent schon auf einer Graphenstruktur aufbaut und somit viele Probleme — wie das Routing zwischen Knoten — schon im Vornherein löst.

Routingalgorithmen auf relationalen Datenbanken können recht hässlich werden. Für einige andere Teams war das eines der Kernprobleme.

Man sollte Technologien nie deswegen auswählen, weil es eben die einzige Technologie ist die man kennt.
Stattdessen sollte eine Technologie, ein Werkzeug, gewählt werden, weil es sich am Besten für die Aufgabe eignet.

Der Routenplaner sollte webbasiert realisiert werden. Wir haben uns für Vaadin als Web-Framework entschieden. Vaadin setzt auf GWT auf und ermöglicht es Web-Applikationen in Java zu schreiben, als würde man eine Swing-Applikation entwickeln. Java Code wird durch einen Cross-Compiler in JavaScript, HTML & CSS übersetzt. Die Arbeit mit dem Framework hat ziemlich gut funktioniert, es lassen sich sehr schnell vorzeigbare Ergebnisse erstellen.

Wir haben außerdem eine Desktop-Applikation für das Bearbeiten und Hochladen von Kartenmaterial geschrieben. Da wir die Aufgaben im Vornherein aufgeteilt hatten war ich hier aber nur beteiligt als es um die Synchronisation mit der Webapp ging. Dafür haben wir Git verwendet (siehe auch den Blogbeitrag “Git als Update-Mechanismus“).

Weitere erwähnenswerte Technologien: Für den PDF-Druck haben wir LaTeX verwendet. Aus einer generierten *.tex Datei wird eine PDF erstellt und an den Browser zurückgegeben. Wir haben mittels node.js das Uni-Adressbuch gescraped um so vernünftig einen vollständigen Datensatz in die Datenbank zu bekommen.

Die erstellen Projekte werden nicht produktiv eingesetzt werden, es wird aber an einer Musterimplementierung gearbeitet. Diese wird später eventuell auch auf den Terminals, die auf dem Unigelände verteilt sind, laufen.
Allgemein war es ziemlich interessant mal in einem größeren Team an einem umfrangreicheren Projekt zu arbeiten. Man lernt einige Dinge über die Arbeit im Team und verschiedene Technologien. Insbesondere lernet man aber wo die eigenen Stärken liegen.

 

Die UNIX-Philosophie

Das UNIX-Betriebssystem wurde in den sechziger Jahren von Bell Laboratories entwickelt. Ein Großteil der heute populären Betriebssysteme wie GNU/Linux oder Mac OS X verwenden grundlegende Konzepte, die aus dem ursprünglichen UNIX-Betriebssystem stammen. Die BSD Familie oder Solaris sind direkte UNIX-Nachfolger. Man bezeichnet diese Systeme daher als unixoide Betriebssysteme. Es ist kein Zufall, dass die effizientesten, sichersten und fortschrittlichsten Betriebssysteme UNIX-Derivate sind. Unix-Systeme sind ideal für die Zukunft gerüstet, da sie die Grundannahme haben, dass sich alles ständig ändert. Der Aufbau ist deshalb äußerst flexibel gehalten.

Mit der Unix-Philosophie bezeichnet man die Konzepte, die beim Erstellen des ursprünglichen Unix-Systems von den Entwicklern angewandt wurden. UNIX wurde als System für Experten entworfen. Für Leute, die wissen was sie tun und was sie wollen. Auf Einsteigerfreundlichkeit wurde keinen Wert gelegt. Die Benutzerschnittstelle zur Interaktion mit dem Betriebssystem ist die Shell.

Es gibt einige Konzepte, die allgemein unter die Unix-Philosophie fallen. Ich möchte in diesem Beitrag einige der wichtigsten herausgreifen um einen Einblick zu geben.

UNIX-Typografie

Make each program do one thing well.
Anstatt ein großes monolithisches Programm zu entwickeln sollte ein Programm genau eine Sache tun — und das richtig! Ein solches Programm hat viele Vorteile:

Programme die genau eine Sache tun können speziell für diese Aufgabe konzipiert werden.

Viele einzelne Programme, die genau eine Sache machen, können sehr einfach kombiniert werden! Aus der Kombination von vielen einzelnen Dingen ergibt sich leicht etwas neues, viel größeres. Wie in einem Werkzeugkasten ist es sehr einfach sich die richtigen Programme für eine Aufgabe zu suchen und diese passend zu kombinieren. All das ermöglicht einen hohen Grad an Abstraktion, der es ermöglicht sehr schnell und effizient ein Ziel zu erreichen.

Um etwa herauszufinden, wie viele verschiedene Dateien in einem Verzeichnis liegen und welchen Typ diese haben können folgende Kommandos in der Shell kombiniert werden: file -b * | sort | uniq -c.
Pipes | sind ein effizientes Konzept um die Programmausgabe direkt als Eingabe an ein anderes Programm weiterzuleiten. Das Programm, an das weitergeleitet wird, beginnt mit der Verarbeitung sobald eine Eingabe vorliegt.
Summiert man die Quelltexte der einzelnen Programme auf kommt man auf mehrere tausend Zeilen Quelltext, die in dieser einen Zeile verwendet werden. Jedes dieser Programme wurde für genau eine Aufgabe entworfen, über Jahre konstant verbessert, ausgebessert und angepasst.

Bei unixoiden Systemen zieht sich dieser Aufbau durch, so sind viele Benutzeroberflächen oft einfach nur grafische Frontends für Kommandozeilentools (diskutil/Festplattendienstprogramm unter Mac OS X oder tcpdump/Wireshark etwa).

Small programs are beautiful.
Programme sollten klein sein. Das macht es einfach sie zu entwickeln, zu pflegen und zu verstehen. Für neue Entwickler ist es leichter sich in das Programm einzuarbeiten, die Funktionalität des Quelltextes ist überschaubar. Die Komplexität eines Programms wird so klein gehalten. Es fällt leichter Fehler zu finden und zu beheben.

Ein weiterer Vorteil ist, dass kleine Programme ressourcenschonend sind: Sie können vom Betriebssystem schnell geladen werden, swapping und paging sind seltener nötig. Im Endeffekt ergibt sich so ein schnelleres Gesamtsystem.

Wie aber legt man fest was ein “kleines” Programm ist? Mike Gancarz gibt in seinem Buch “The UNIX Philosophy” einige Anhaltspunkte: Funktionsdeklarationen, die aufgrund von vielen Parametern umgebrochen werden müssen oder auftretende Probleme, die nicht mehr augenblicklich einem Kontext zugeordnet werden können sind Warnsignale. Strikt der Unix-Philosophie folgend sollte ls keinen Flag zur Formatierung oder Sortierung der Ausgabe haben — hierfür gibt es andere Programme (column, sort, …).

Dieser Aufbau von vielen verschiedene kleinen Programmen die zusammenarbeiten, lässt sich dem Konzept “Divide & Conquer” zuordnen: Dadurch, dass große, komplexe Probleme in einfachere, kleine zerlegt werden wird es einfach diese einzeln zu lösen.

Avoid captive user interfaces.
Programme sollten so geschrieben sein, dass sie problemlos in einem Skript verwendet werden können. Die Schnittstelle muss für Maschinen entworfen sein — nicht für Menschen. Das bedeutet beispielsweise, dass Programme nach dem Aufruf nicht noch auf Benutzereingaben angewiesen sein sollten (“Sind Sie sicher, dass Sie …? y/n”), sondern nur über die Kanäle STDIN, STDOUT & STDERR kommunizieren sollten.
Das macht Automatisierung einfacher, es ist sehr einfach Programme miteinander zu verknüpfen, die eben nicht irgendwann auf eine Benutzereingabe warten und deswegen blocken. Das UNIX-Konzept macht es sehr einfach, Programme zu kombinieren.

Zum Vergleich: Befehle wie dir erzeugen unter Windows-Systemen auf ein leeres Verzeichnis angewandt eine Ausgabe mit mehreren Zeilen — ohne relevanten Informationen. Diese Ausgabe weiterzuverarbeiten oder zu parsen ist nicht ohne weiteres machbar. Programme, die strikt der Unix-Philosophie folgen verhalten sich dagegen nach dem Grundsatz “Silence is golden”.

Build a prototype as soon as possible.
In der Unix-Philosophie nehmen Prototypen eine zentrale Rolle ein. Die Sichtweise ist hier, dass viele traditionelle Softwareentwicklungsprozesse durch Anforderungsanalyse, Spezifikationen, etc. darauf abzielen von Anfang an das ideale System zu den Anforderungen zu bauen. Oder anders ausgedrückt: Beim ersten Mal alles richtig zu machen.

Die Sichtweise der Unix-Philosophie ist hier, dass sich ein solches System nie in einem Zyklus bauen lässt. Systeme müssen verschiedene Phasen durchlaufen und konstant, iterativ verbessert werden um Anforderungen ideal zu erfüllen. In jeder Phase muss dazu gelernt werden. Die Alternative ist einen Prototypen zu bauen und diesen iterativ auszubauen. Da Programme klein und überschaubar sein sollen fällt der Prototypenbau leichter.

Automate everything
Aufgaben, die heute von Hand ausgeführt werden, obwohl sie auch das System übernehmen könnte, sind nüchtern betrachtet Zeitsverschwendung. Da unter unixoiden Systemen Programme darauf ausgelegt sind mit anderen Programmen zu interagieren — und nicht mit Menschen, können Abläufe beispielsweise mittels Shellskripten einfach automatisiert werden. In Shellskripten können Programme und Kommandos wie Funktionen verwendet werden. Die Ausgabe kann direkt in den Programmfluss integriert weren.

Fazit
Die wichtigsten Konzepte zusammengefasst: Dinge einfach halten, Komplexität vermeiden. Eine Sache richtig machen. Programme miteinander kombinieren.

Das war jetzt lediglich ein kleiner Einblick in die Unix-Philosophie. Es gibt noch deutlich mehr Konzepte und man kann die Ideen auch nicht nur auf Betriebssysteme anwenden. Für weitere Informationen kann ich folgende Quellen sehr empfehlen:

Natürlich gibt es auch Probleme eines solchen Systemaufbaus. Diese habe ich hier absichtlich nicht thematisiert. Wer sich hierfür interessiert sollte sich etwa zu Plan 9 oder Kernelproblemen informieren.

Git als Update-Mechanismus

In diesem Beitrag möchte ich einen einfachen Mechanismus beschreiben, um in einer Client-Server Infrastruktur Updates an die Clients auszuliefern. Ich hatte bei einem Projekt einige Clients in ubiquitären Umgebungen und war dafür auf der Suche nach solch einem System. Die Clients sind einfache Kleincomputer mit einer schlanken Linux-Distribution und müssen ohne technische Wartung auskommen.

Anforderungen an den Update-Mechanismus sind:

  • Authentifizierung
    Die Verbindung zum Update-Server muss authentifiziert sein.
    Das war in den großen Betriebssystemen früher ein großes Problem und ist in vielen Programmen noch immer ein Schwachpunkt. Der Instant Messenger-Exploit Anfang dieses Jahr war etwa ein Beispiel, in dem der Update-Server nicht authentifiziert wurde.
  • Fallbacks
    Falls ein Update schiefgeht sollte es sehr einfach möglich sein den alten Stand wiederherzustellen. Es ist nicht akzeptabel, dass Daten verloren gehen. Alles muss abgespeichert werden.
  • Scripting
    Ich will an jeden Zeitpunkt des Updateprozesses einen Hook setzen können (vor dem Update, danach, etc.). Dies kann später benutzt werden um beispielsweise das Gerät nach dem Einspielen von Neuerungen zu rebooten, um zu überprüfen ob das Update erfolgreich verlaufen ist oder um etwa Datenbankänderungen durchzuführen.
  • Autorisierung
    Der Updateserver darf nicht öffentlich verfügbar sein — Clients müssen sich ihm gegenüber autorisieren um Zugriff auf Updates zu bekommen. Die Option später eine gruppenbasierte License-Policy für verschiedene Softwareversionen nachrüsten zu können soll offengehalten werden.

Ich habe mich letztlich dazu entschieden hierfür git zu verwenden. Die Versionsverwaltung deckt bereits im Vornherein einen großen Teil der Anforderungen ab. Git bot sich an, da ich auch sonst sehr gerne mit dem Tool arbeite und damit also schon umgehen konnte. Der Aufbau meines Systems sieht inzwischen so aus:

 

System-Aufbau

 

Auf der Serverseite

  • Webserver, Zugriff nur über HTTPS möglich, selbstsigniertes X.509 Zertifikat.
  • Ein “bare” git repository, geschützt über HTTP-Authentifizierung: https://updates.foo.net/bar.git.
    Neue Updates werden zu diesem Repository gepusht.
  • hooks/post-update: Wird ausgeführt nachdem neue Updates gepusht wurden.

    # wird von git für HTTP repositories benötigt
    git update-server-info  
    
    # korrekte Zugriffsrechte
    find ~/bar.git -exec chmod o+rx '{}' \;  
    

 

Auf der Client-Seite

  • Cronjob. Überprüft regelmäßig ob neue Updates vorliegen. check.sh enthält:
    #!/bin/sh
    
    # unser Update-Repository
    cd "~/bar.git"
    
    # neue Updates fetchen, git legt diese dann in FETCH_HEAD ab
    git fetch
    
    # liegen im origin-Repository neue Änderungen vor?
    newUpdatesAvailable=`git diff HEAD FETCH_HEAD`
    
    if [ "$newUpdatesAvailable" != "" ]
    then
            # fallback erstellen
            git branch fallbacks
            git checkout fallbacks
    
            git add .
            git add -u
            git commit -m `date "+%Y-%m-%d"`
            echo "fallback created"
    
            git checkout master
            git merge FETCH_HEAD
            echo "merged updates"
    else
            echo "no updates available"
    fi
    
  • Unter hooks/post-merge können Aktionen definiert werden, die nach dem Einspielen der Änderungen ausgeführt werden (Reboot, Validierung des Updateverlaufs, etc.).

Der Client hat eine Kopie des Server-Zertifikats und benutzt dieses um die Verbindung zu authentifizieren. So können die Geräte sicher sein, dass sie zum richtigen Server verbunden sind. Die passende ~/bar.git/.gitconfig sieht in etwa so aus:

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        fetch = +refs/heads/*:refs/remotes/origin/*
        url = https://user@updates.foo.net/bar.git
[http]
        sslVerify = true
        sslCAInfo = ~/server.crt
[core]
        askpass = ~/echo_your_https_authorization_pw.sh

 
Was sind Nachteile dieses Aufbaus?
Ich habe mich bewusst für ein Versionierungssystem entschieden, da meine wichtigste Anforderung ist, dass die Geräte auf keinen Fall Daten verlieren. Dies ist gleichzeitig auch ein Nachteil des Systems, da letztlich alles archiviert wird kann dies bei Geräten mit geringem Speicher kritisch sein. Ein ähnliches Problem tritt auf, falls häufig ein großer Teil des Systems geändert werden muss. In diesem Fall würde eine Lösung mittels rsync oder ähnlichen Tools eventuell mehr Sinn machen. rdiff-backup führt etwa inkrementelle Backups durch und bietet dazu noch die Möglichkeit die Anzahl der Versionen die behalten werden zu beschränken.

Do not use SSH.
Ein einfacherer Weg solch ein System aufzubauen wären SSH-Zugänge mit eingeschränkten Dateirechten für die Clients. Das Problem hierbei ist, dass Geräte Zugriff auf den Server bekommen. Ein Angreifer könnte sich mit dem private key des Clients zum Server verbinden. Es ist nicht aussergewöhnlich, dass in Betriebssystem-Kernen oder in System-Bibliotheken Sicherheitslücken entdeckt werden. Im schlimmsten Fall könnte ein Angreifer so die Dateien innerhalb des Repositories manipulieren. Die Clients würden automatischen den manipulierten Inhalten fetchen. Das manipulierter Inhalt ausgeliefert wird, ist auch in meinem Setup nicht ausgeschlossen, dazu muss der Angreifer aber über einen Zugang zu dem Server verfügen.

ioexception.de

Benjamin Erb [] studiert seit 2006 Medieninformatik und interessiert sich insbesondere für Java, Web-Technologien, Ubiquitous Computing, Cloud Computing, verteilte Systeme und Informationsdesign.


Raimar Wagner studiert seit 2005 Informatik mit Anwendungsfach Medizin und interessiert sich für C++ stl, boost & Qt Programmierung, Scientific Visualization, Computer Vision und parallele Rechenkonzepte.


David Langer studiert seit 2006 Medieninformatik und interessiert sich für Web-Entwicklung, jQuery, Business Process Management und Java.


Sebastian Schimmel studiert seit 2006 Informatik mit Anwendungsfach Medizin und interessiert sich für hardwarenahe Aspekte, Robotik, webOs, C/C++ und UNIX/Linux.


Timo Müller studiert seit 2006 Medieninformatik. Er interessiert sich allen voran für Mobile and Ubiquitous Computing, systemnahe Enwticklung und verteilte Systeme, sowie Computer Vision.


Achim Strauß studiert seit 2006 Medieninformatik. Seine Interessen liegen in Themen der Mensch-Computer Interaktion sowie Webentwicklung und UNIX/Linux.


Tobias Schlecht studiert seit 2006 Medieninformatik und interessiert sich vor allem für Software Engineering, Model Driven Architecture, Requirements Engineering, Usability Engineering, Web-Technologien, UML2 und Java.


Fabian Groh studiert seit 2006 Medieninformatik. Seine Interessengebiete sind Computer Graphics, Computer Vision, Computational Photography sowie Ubiquitos Computing.


Matthias Matousek studiert seit 2007 Medieninformatik und interessiert sich besonders für Skriptsprachen, Echtzeitsysteme und Kommunikation.


Michael Müller [] studiert seit 2009 Medieninformatik. Er interessiert sich vor allem für Web-Technologien, Ubiquitous Computing, User-Interfaces, UNIX und Creative Coding.


Falco Nogatz [] studiert seit 2010 Informatik mit Anwendungsfach Mathematik. Er interessiert sich für Web-Technologien, Programmierparadigmen und theoretische Grundlagen.

Archiv

Februar 2015
M D M D F S S
« Mrz    
 1
2345678
9101112131415
16171819202122
232425262728