=head1 GOOFED - Generic Object Oriented Flexible Extensible Daemon =head2 Abstract Perl ist eine tolle Sprache. Leider hat sie auch viele Abhängigkeiten. Besonders bei Embedding muss man im Allgemeinen eine ganze Perl-Installation "mitschleppen". Bei einem bestimmten Projekt musste ich ohne externe Dateien oder andere Abhängigkeiten auskommen. Um trotzdem Perl einsetzen zu können, musste alles in das Binary wandern. Die Probleme und Lösungen, die ich fand, sind im Folgenden beschrieben. =head1 Wozu, weshalb und warum? Das Projekt, für das Goofed entstand, bestand darin, den Radius-Server eines großen Internet-Providers in einigen Funktionen zu ersetzen. Da die Anforderungen durch viele Jahre hinweg sehr speziell wurden (ein typisches "I setzen den Standard"-Phänomen) und die alte Codebasis neuen Anforderungen nicht mehr gut gewachsen war, musste ein neuer Daemon her. Recht früh kam die Idee, im Kern für die komplizierten Regeln eine High-Level-Skriptsprache zu verwenden und nur für das eigentliche Radius-Paket-Management C zu verwenden. Perl schien zuerst an vielen Gründen zu scheitern: kompliziertes Build-System, viele Abhängigkeiten und Dateien, keine gute Kontrolle über den Speicherbedarf (Leaks wären tödlich für einen Daemon, der meherre Monate durchhalten muss) und Last-not-Least stand zu befürchten, daß die Server-Abteilung die Lösung sofort ablehnen würde, sobald bekannt würde, daß sie Perl benutzt (typisches "Real-World - macht keinen Sinn"-Phänomen). =head2 Eckpunkte der Implementierung Trotz dieser Gegenanzeigen versprach Perl auch wichtige Vorteile, der wichtigste war das Vorhandensein einer grossen Codebasis und viele Module. Das und Perl selbst führen meist sehr schnell zu verwertbaren Ergebnissen. Um die Anforderungen zu Erfüllen, habe ich folgende Entscheidungen gefällt: =over 4 =item einzelnes Binary Bis auf die Konfigurationsdateien sollte alles - Perl-Interpreter, Module, Bibliotheksdateien - in einer einzigen Datei sein und keine externen Dateien benötigen oder anlegen. Dies schließt leider das (ansonsten hervorragende) PAR-Modul aus, da dieses alle Bibliotheksdateien zwar in eine Datei packt, zur Laufzeit diese aber lediglich entpackt und entsprechend viele Dateien schreibt. =item festgelegte Perl-Version Die Perl-Version sollte hartverdrahtet werden. Der Grund liegt in vielen, kleinen Änderungen auch innerhalb stabiler Release-Zyklen, die zu Build-Problemen oder Fehler zur Laufzeit führen können. I war hier ausschlaggebend. Ausserdem kann man, sobald man die Kontrolle über dei Perl-Quellen hat, bedenkenlos Anpassungen vornehmen (ich nenne das die "Lizenz zum Rumsauen"-Regel). Es sollte möglich sein, den Perl-Kern upzugraden, wobei aber klar sein sollte, das dies Veränderungen am Quellcode nach sich zieht. =item festgelegte Module und festgelegte Versionen Ähnliches gilt für Module: es sollte möglich sein, fast beliebige Module einzubauen, aber auch diese sollten in einer festen Version kommen. Perl-Module auf CPAN haben - seien wir ehrlich - fast immer kleine und große Bugs. Meistens kann man mit ihnen leben oder Workarounds finden - häufig muss man dazu auf Interna zugreifen, die sich von Version zu Version ändern. =item "normales" Build-System, kein Configure Configure nichtinteraktiv auszuführen ist kompliziert, und sehr viel kann schiefgehen. Ein einfaches Makefile fühlt sich wesentlich stabiler und einfacher wartbar an. =back Da der Daemon sehr viele Anfragen erhält und in anderer Form weiterleitet, wäre es schön, wenn man die Bearbeitung der Anfragen verzahnen könnte. Dazu braucht man ein vernünftiges Event-System (der Name sagts schon - das Event-Modul) und etwas, mit dem man effizient und vor allem einfach Pseudoparallelität erreichen kann - Coroutinen, implementiert mit dem Coro-Modul. Coro greift auf viele Perl-Interna zu und ist eigentlich recht experimentell, ich hatte aber Erfahrungen mit langlaufenden Servern und Coro und wußte daher, das es sehr stabil läuft, sobald es läuft. Daß das Umfeld stabil ist, gab den Ausschlag. =head1 Implementierung Nachdem ein paar grundlegende Gedanken gedacht waren, konnte ich an die Implementierung gehen. Flexibilität ist alles, wenn etwas schiefgeht. =head2 Perl? .... Microperl! Wenig bekannt ist, daß es auch eine Microperl-"Distribution" gibt, die ein sehr kleines und portables Perl verspricht - ohne Module, ohne Systemabhängigkeiten und ohne interaktives Configure. Die "Distribution" ist sehr klein: sie besteht nur aus den drei Dateien C, C und C. Daher ist bei Perl gleich mit dabei. Ich habe das Makefile und die nötigen Perl-Sourcen in ein Unterverzeichnis kopiert und dann die Konfiguration, die standardmäßig nur ISO-C89 + einige wenige Erweiterungen wie C benötigt, solange getuned, bis ich Zugriff auf die meisten POSIX-Funktionen hatte. Dazu habe ich die C von: -DPERL_CORE -DPERL_MICRO -DSTANDARD_C -DPERL_USE_SAFE_PUTENV auf -DPERL_CORE=1 -DPERL_MICRO=1 geändert und die Konfiguration in C getuned, compiliert und solange iteriert, bis ich zum Link-Stadium kam. Danach hatte ich folgende Dateien in meinem C-Verzeichnis: embed.h mg.h perlio.c proto.h thrdvar.h EXTERN.h embedvar.h miniperlmain.c perlio.h reentr.c thread.h INTERN.h ext.libs nostdio.h perliol.h reentr.h toke.c Makefile form.h numeric.c perlsdio.h reentr.inc uconfig.sh XSUB.h globals.c op.c perlvars.h regcomp.c universal.c av.c gv.c op.h perly.c regcomp.h unixish.h av.h gv.h opcode.h perly.h regexec.c utf8.c config_h.SH handy.h opnames.h pp.c regexp.h utf8.h configpm hv.c pad.c pp.h regnodes.h util.c cop.h hv.h pad.h pp_ctl.c run.c util.h cv.h intrpvar.h patchlevel.h pp_hot.c scope.c warnings.h deb.c iperlsys.h perl.c pp_pack.c scope.h xsutils.c doio.c keywords.h perl.h pp_proto.h sv.c doop.c locale.c perlapi.c pp_sort.c sv.h dump.c mg.c perlapi.h pp_sys.c taint.c Beim Bauen bekommt man die C, das Microperl-Äquivalent zur C und C, das zum Bauen benötigt wird und auch beim weiteren Build-Prozess hilfreich sein kann. Mit C und den Header-Dateien hat man alles, was man zum Embedden benötigt. Dieser Schritt war relativ leicht. Er wäre noch leichter gewesen, wenn ich auf die meisten esoterischen Funktionen wie C, C oder Signal-Handling verzichtet hätte, da ich fast alles über die C- und C-Module erreichen kann, und notfalls auch auf C zurückgreifen könnte. Im absoluten Notfall. =head2 Module Embedden =head2 Bibliothek =head2 Bootstrapping