| 1 |
root |
1.1 |
=head1 GOOFED - Generic Object Oriented Flexible Extensible Daemon |
| 2 |
|
|
|
| 3 |
|
|
=head2 Abstract |
| 4 |
|
|
|
| 5 |
root |
1.2 |
Perl ist eine tolle Sprache. Leider hat sie auch viele |
| 6 |
|
|
Abhängigkeiten. Besonders bei Embedding muss man im Allgemeinen eine |
| 7 |
|
|
ganze Perl-Installation "mitschleppen". Bei einem bestimmten Projekt |
| 8 |
|
|
musste ich ohne externe Dateien oder andere Abhängigkeiten auskommen. Um |
| 9 |
|
|
trotzdem Perl einsetzen zu können, musste alles in das Binary |
| 10 |
|
|
wandern. Die Probleme und Lösungen, die ich fand, sind im Folgenden |
| 11 |
|
|
beschrieben. |
| 12 |
root |
1.1 |
|
| 13 |
root |
1.2 |
=head1 Wozu, weshalb und warum? |
| 14 |
|
|
|
| 15 |
|
|
Das Projekt, für das Goofed entstand, bestand darin, den Radius-Server |
| 16 |
|
|
eines großen Internet-Providers in einigen Funktionen zu ersetzen. Da die |
| 17 |
|
|
Anforderungen durch viele Jahre hinweg sehr speziell wurden (ein typisches |
| 18 |
|
|
"I<Wir> setzen den Standard"-Phänomen) und die alte Codebasis neuen |
| 19 |
|
|
Anforderungen nicht mehr gut gewachsen war, musste ein neuer Daemon her. |
| 20 |
|
|
|
| 21 |
|
|
Recht früh kam die Idee, im Kern für die komplizierten Regeln eine |
| 22 |
|
|
High-Level-Skriptsprache zu verwenden und nur für das eigentliche |
| 23 |
|
|
Radius-Paket-Management C zu verwenden. |
| 24 |
|
|
|
| 25 |
|
|
Perl schien zuerst an vielen Gründen zu scheitern: kompliziertes |
| 26 |
|
|
Build-System, viele Abhängigkeiten und Dateien, keine gute Kontrolle |
| 27 |
|
|
über den Speicherbedarf (Leaks wären tödlich für einen Daemon, der |
| 28 |
|
|
meherre Monate durchhalten muss) und Last-not-Least stand zu befürchten, |
| 29 |
|
|
daß die Server-Abteilung die Lösung sofort ablehnen würde, sobald |
| 30 |
root |
1.3 |
bekannt würde, daß sie Perl benutzt (typisches "Real-World - macht |
| 31 |
|
|
keinen Sinn"-Phänomen). |
| 32 |
root |
1.2 |
|
| 33 |
root |
1.3 |
=head2 Eckpunkte der Implementierung |
| 34 |
root |
1.1 |
|
| 35 |
root |
1.3 |
Trotz dieser Gegenanzeigen versprach Perl auch wichtige Vorteile, der |
| 36 |
|
|
wichtigste war das Vorhandensein einer grossen Codebasis und viele |
| 37 |
|
|
Module. Das und Perl selbst führen meist sehr schnell zu verwertbaren |
| 38 |
|
|
Ergebnissen. |
| 39 |
|
|
|
| 40 |
|
|
Um die Anforderungen zu Erfüllen, habe ich folgende Entscheidungen |
| 41 |
|
|
gefällt: |
| 42 |
|
|
|
| 43 |
|
|
=over 4 |
| 44 |
|
|
|
| 45 |
|
|
=item einzelnes Binary |
| 46 |
|
|
|
| 47 |
|
|
Bis auf die Konfigurationsdateien sollte alles - Perl-Interpreter, Module, |
| 48 |
|
|
Bibliotheksdateien - in einer einzigen Datei sein und keine externen |
| 49 |
|
|
Dateien benötigen oder anlegen. |
| 50 |
|
|
|
| 51 |
|
|
Dies schließt leider das (ansonsten hervorragende) PAR-Modul aus, da |
| 52 |
|
|
dieses alle Bibliotheksdateien zwar in eine Datei packt, zur Laufzeit |
| 53 |
|
|
diese aber lediglich entpackt und entsprechend viele Dateien schreibt. |
| 54 |
|
|
|
| 55 |
|
|
=item festgelegte Perl-Version |
| 56 |
|
|
|
| 57 |
|
|
Die Perl-Version sollte hartverdrahtet werden. Der Grund liegt in vielen, |
| 58 |
|
|
kleinen Änderungen auch innerhalb stabiler Release-Zyklen, die zu |
| 59 |
|
|
Build-Problemen oder Fehler zur Laufzeit führen können. I<Never change a |
| 60 |
|
|
running system> war hier ausschlaggebend. |
| 61 |
|
|
|
| 62 |
|
|
Ausserdem kann man, sobald man die Kontrolle über dei Perl-Quellen |
| 63 |
|
|
hat, bedenkenlos Anpassungen vornehmen (ich nenne das die "Lizenz zum |
| 64 |
|
|
Rumsauen"-Regel). |
| 65 |
|
|
|
| 66 |
|
|
Es sollte möglich sein, den Perl-Kern upzugraden, wobei aber klar sein |
| 67 |
|
|
sollte, das dies Veränderungen am Quellcode nach sich zieht. |
| 68 |
|
|
|
| 69 |
|
|
=item festgelegte Module und festgelegte Versionen |
| 70 |
|
|
|
| 71 |
|
|
Ähnliches gilt für Module: es sollte möglich sein, fast beliebige |
| 72 |
|
|
Module einzubauen, aber auch diese sollten in einer festen Version kommen. |
| 73 |
|
|
|
| 74 |
|
|
Perl-Module auf CPAN haben - seien wir ehrlich - fast immer kleine und |
| 75 |
|
|
große Bugs. Meistens kann man mit ihnen leben oder Workarounds finden |
| 76 |
|
|
- häufig muss man dazu auf Interna zugreifen, die sich von Version zu |
| 77 |
|
|
Version ändern. |
| 78 |
|
|
|
| 79 |
|
|
=item "normales" Build-System, kein Configure |
| 80 |
|
|
|
| 81 |
|
|
Configure nichtinteraktiv auszuführen ist kompliziert, und sehr viel kann |
| 82 |
|
|
schiefgehen. Ein einfaches Makefile fühlt sich wesentlich stabiler und |
| 83 |
|
|
einfacher wartbar an. |
| 84 |
|
|
|
| 85 |
|
|
=back |
| 86 |
|
|
|
| 87 |
|
|
Da der Daemon sehr viele Anfragen erhält und in anderer Form |
| 88 |
|
|
weiterleitet, wäre es schön, wenn man die Bearbeitung der Anfragen |
| 89 |
|
|
verzahnen könnte. Dazu braucht man ein vernünftiges Event-System (der |
| 90 |
|
|
Name sagts schon - das Event-Modul) und etwas, mit dem man effizient |
| 91 |
|
|
und vor allem einfach Pseudoparallelität erreichen kann - Coroutinen, |
| 92 |
|
|
implementiert mit dem Coro-Modul. |
| 93 |
|
|
|
| 94 |
|
|
Coro greift auf viele Perl-Interna zu und ist eigentlich recht |
| 95 |
|
|
experimentell, ich hatte aber Erfahrungen mit langlaufenden Servern und |
| 96 |
|
|
Coro und wußte daher, das es sehr stabil läuft, sobald es läuft. |
| 97 |
|
|
|
| 98 |
|
|
Daß das Umfeld stabil ist, gab den Ausschlag. |
| 99 |
|
|
|
| 100 |
|
|
=head1 Implementierung |
| 101 |
|
|
|
| 102 |
|
|
Nachdem ein paar grundlegende Gedanken gedacht waren, konnte ich an die |
| 103 |
|
|
Implementierung gehen. Flexibilität ist alles, wenn etwas schiefgeht. |
| 104 |
root |
1.1 |
|
| 105 |
|
|
=head2 Perl? .... Microperl! |
| 106 |
root |
1.3 |
|
| 107 |
|
|
Wenig bekannt ist, daß es auch eine Microperl-"Distribution" gibt, |
| 108 |
|
|
die ein sehr kleines und portables Perl verspricht - ohne Module, ohne |
| 109 |
|
|
Systemabhängigkeiten und ohne interaktives Configure. |
| 110 |
|
|
|
| 111 |
|
|
Die "Distribution" ist sehr klein: sie besteht nur aus den drei Dateien |
| 112 |
|
|
C<README.micro>, C<Makefile.micro> und C<uconfig.sh>. Daher ist bei Perl |
| 113 |
|
|
gleich mit dabei. |
| 114 |
|
|
|
| 115 |
|
|
Ich habe das Makefile und die nötigen Perl-Sourcen in ein |
| 116 |
|
|
Unterverzeichnis kopiert und dann die Konfiguration, die standardmäßig |
| 117 |
|
|
nur ISO-C89 + einige wenige Erweiterungen wie C<rename()> benötigt, |
| 118 |
|
|
solange getuned, bis ich Zugriff auf die meisten POSIX-Funktionen hatte. |
| 119 |
|
|
|
| 120 |
|
|
Dazu habe ich die C<CFLAGS> von: |
| 121 |
|
|
|
| 122 |
|
|
-DPERL_CORE -DPERL_MICRO -DSTANDARD_C -DPERL_USE_SAFE_PUTENV |
| 123 |
|
|
|
| 124 |
|
|
auf |
| 125 |
|
|
|
| 126 |
|
|
-DPERL_CORE=1 -DPERL_MICRO=1 |
| 127 |
|
|
|
| 128 |
|
|
geändert und die Konfiguration in C<uconfig.sh> getuned, compiliert und |
| 129 |
|
|
solange iteriert, bis ich zum Link-Stadium kam. |
| 130 |
|
|
|
| 131 |
|
|
Danach hatte ich folgende Dateien in meinem C<uperl>-Verzeichnis: |
| 132 |
|
|
|
| 133 |
|
|
embed.h mg.h perlio.c proto.h thrdvar.h |
| 134 |
|
|
EXTERN.h embedvar.h miniperlmain.c perlio.h reentr.c thread.h |
| 135 |
|
|
INTERN.h ext.libs nostdio.h perliol.h reentr.h toke.c |
| 136 |
|
|
Makefile form.h numeric.c perlsdio.h reentr.inc uconfig.sh |
| 137 |
|
|
XSUB.h globals.c op.c perlvars.h regcomp.c universal.c |
| 138 |
|
|
av.c gv.c op.h perly.c regcomp.h unixish.h |
| 139 |
|
|
av.h gv.h opcode.h perly.h regexec.c utf8.c |
| 140 |
|
|
config_h.SH handy.h opnames.h pp.c regexp.h utf8.h |
| 141 |
|
|
configpm hv.c pad.c pp.h regnodes.h util.c |
| 142 |
|
|
cop.h hv.h pad.h pp_ctl.c run.c util.h |
| 143 |
|
|
cv.h intrpvar.h patchlevel.h pp_hot.c scope.c warnings.h |
| 144 |
|
|
deb.c iperlsys.h perl.c pp_pack.c scope.h xsutils.c |
| 145 |
|
|
doio.c keywords.h perl.h pp_proto.h sv.c |
| 146 |
|
|
doop.c locale.c perlapi.c pp_sort.c sv.h |
| 147 |
|
|
dump.c mg.c perlapi.h pp_sys.c taint.c |
| 148 |
|
|
|
| 149 |
|
|
Beim Bauen bekommt man die C<libuperl.a>, das Microperl-Äquivalent zur |
| 150 |
|
|
C<libperl> und C<microperl>, das zum Bauen benötigt wird und auch beim |
| 151 |
|
|
weiteren Build-Prozess hilfreich sein kann. |
| 152 |
|
|
|
| 153 |
root |
1.4 |
Mit C<micrperl>, C<libuperl.a> und den Header-Dateien hat man alles, was |
| 154 |
|
|
man zum Embedden benötigt. |
| 155 |
root |
1.3 |
|
| 156 |
|
|
Dieser Schritt war relativ leicht. Er wäre noch leichter gewesen, wenn |
| 157 |
|
|
ich auf die meisten esoterischen Funktionen wie C<getpwnam>, C<mkdir> oder |
| 158 |
|
|
Signal-Handling verzichtet hätte, da ich fast alles über die C<POSIX>- |
| 159 |
|
|
und C<Event>-Module erreichen kann, und notfalls auch auf C zurückgreifen |
| 160 |
|
|
könnte. Im absoluten Notfall. |
| 161 |
root |
1.1 |
|
| 162 |
root |
1.4 |
=head2 Bibliothek |
| 163 |
|
|
|
| 164 |
|
|
Im nächsten Schritt |
| 165 |
|
|
|
| 166 |
root |
1.1 |
=head2 Module Embedden |
| 167 |
|
|
|
| 168 |
root |
1.4 |
Header und Library sind zwar genug, aber Perl-Module wollen ein |
| 169 |
|
|
lauffähiges Perl und Zugriff auf Kleinkram wie C<ExtUtils::MakeMaker>, |
| 170 |
|
|
damit man sie übersetzen kann. |
| 171 |
root |
1.1 |
|
| 172 |
|
|
=head2 Bootstrapping |
| 173 |
|
|
|