Unter “Single-Serving Sites” versteht man gemeinhin Websites wie IsItChristmas.com, WhatIsMyIP.org und purple.com, die als Gegenentwurf zu informationsüberladenen Webportalen meist genau eine einzige Frage beantworten. Es gibt in der Regel keine Unterseiten, keine echte Interaktivität und auch sonst sind solche Websites sehr angenehm anzusehen.
Meist ist so eine Website schlicht in statischem HTML geschrieben worden, denn serverseitige Scripts sind ebenso überflüssig wie jQuery oder andere Perversionen des Modernen. Was aber, wenn man dem Besucher etwa abhängig von seiner Browsersprache oder per Zufallsalgorithmus eine andere Ansicht präsentieren will? Natürlich könnte man auf Javascript zurückgreifen; wer das aber nicht möchte, zum Beispiel, weil er sich nicht darauf verlassen möchte, dass der Besucher Javascript aktiviert hat und einen mit dem geschriebenen Code kompatiblen Browser benutzt, dem bleibt letztlich nur die Nutzung serverseitigen Codes, etwa Perl oder PHP, übrig.
Nun gilt für Server mehr noch als für einen Laptop oder PC, dass jede zusätzlich installierte Software wegen möglicher noch unentdeckter oder unkorrigierter Fehler ein potenzielles Sicherheitsrisiko birgt, wozu auch Skriptsprachen, Webserversoftware und sogar Texteditoren zählen. Dazu kommt der Ressourcenhunger, den mancher stack mit sich bringt: Benutzt man etwa die beliebte Kombination aus dem Webserver nginx und der Sprache PHP im FastCGI-Modus (php-fcgi), ist es keine Seltenheit, dass auch im Leerlauf diverse Prozesse an den Systemressourcen knabbern.
Lässt sich eine solche Webanwendung auch programmieren, ohne den Server unnötiger Last und Gefahren auszusetzen? Nun, je nach Betriebssystem stellt das tatsächlich kein Problem dar, denn zum Beispiel OpenBSD verfügt von Haus aus über einen brauchbaren Texteditor (vi), einen FastCGI unterstützenden Webserver (httpd mit slowcgi) sowie einen C‑Compiler (cc). C mag nicht gerade eine besonders einsteigerfreundliche Sprache sein, aber für ein bisschen Textausgabe ist nicht viel Code nötig. Ich mach’ mal ein Beispiel.
Wir gehen im Folgenden davon aus, dass wir eine bestehende Website namens “Sind-wir-schon-verloren.net” haben, die bisher aus einem unter OpenBSD in einem nginx-Server laufenden PHP-Script besteht, das, wenn ein englischsprachiger Besucher es aufruft, “Yes”, sonst “Ja” ausgibt.
<!doctype html> <html> <head> <title>Sind wir schon verloren?</title> </head> <body> <h1><?php /* Browsersprache ermitteln: */ if (substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2) == "en") { /* Aha, ein englischsprachiger Nutzer! */ echo "Yes"; } else { /* Aha, kein englischsprachiger Nutzer! */ echo "Ja"; } ?></h1> </body> </html>
So weit, so verständlich. PHP hat den Vorteil, dass man es einfach in bestehende HTML-Seiten integrieren kann und der Webserver erledigt den Rest. Aber wir wollen ja Ressourcen sparen, also programmieren wir das ganze Ding mal neu.
Wenn ihr Webserver wie nginx oder lighttpd einsetzt, läuft PHP bei euch vermutlich bereits im FastCGI-Modus. FastCGI bedeutet im Wesentlichen, dass der Webserver laufend einige Kopien von PHP im Speicher hält, die bei einer Anfrage schnell das gewünschte Ergebnis berechnen, hier also aus der Liste der Aufrufvariablen die übermittelte Liste der unterstützten Sprachen heraussuchen und die ersten zwei Zeichen dieser Liste, zum Beispiel “de” für Deutsch oder “en” für Englisch als Hauptsprache, mit der Zeichenkette “en” vergleichen. Das klingt nicht nur wie der wohlbekannte Schuss aus Kanonen auf Spatzen, es hat auch ähnliche Konsequenzen. Dass PHP selbst im Leerlauf gelegentlich CPU-Zeit für sich beansprucht, ist nur eine davon.
Warum also bei jedem Aufruf die ganze HTML-Seite neu kompilieren? Viel effizienter ist es doch, ein bereits vorkompiliertes Programm hinter dieses FastCGI zu hängen. Damit diese wiederum zusätzliche Software keinen Unsinn anstellt, können wir sie mit dem OpenBSD-Befehl pledge() unter Kontrolle bekommen. Los geht’s!
Die C‑Version unserer PHP-Seite sieht so aus:
/* Erst mal ein paar Funktionen nachladen: */ #include <stdlib.h> /* Standardbibliothek */ #include <stdio.h> /* puts() */ #include <string.h> /* strncmp() */ #include <stdbool.h> /* bool */ #ifdef __OpenBSD__ /* Unter OpenBSD können wir mit pledge() die Sicherheit erhöhen: */ #include <unistd.h> /* pledge() */ #include <err.h> /* err() */ #endif int main(void) { #ifdef __OpenBSD__ if (-1 == pledge("stdio", NULL)) { /* Wir wollen nicht zu viele Befugnisse! */ err(EXIT_FAILURE, "pledge"); } #endif /* In der Umgebungsvariablen HTTP_ACCEPT_LANGUAGE * stehen die Browsersprachen des Besuchers (weiter- * geleitet per FastCGI): */ const char* curr_language = getenv("HTTP_ACCEPT_LANGUAGE"); bool is_en = false; if (curr_language != NULL) { /* is_en auf "true" setzen, falls die ersten zwei * Zeichen "en" sind und die Sprache nicht NULL * (also der Browser des Besuchers nicht schweigsam) * ist: */ is_en = strncmp(curr_language, "en", 2) == 0; } /* HTTP-Statuscodes ausgeben: */ puts("Status: 200 OK\r"); puts("Content-Type: text/html\r"); puts("\r"); /* Es folgt die eigentliche Website. */ puts("< !doctype html>"); puts("<html>"); puts(" <head>"); puts(" <title>Sind wir schon verloren?</title>"); puts(" </head>"); puts(" <body>"); puts(" <h1>"); /* Falls is_en "true" ist, gib "Yes" aus, sonst "Ja": */ if (is_en) { puts("Yes"); } else { puts("Ja"); } /* Der Rest der Seite: */ puts(" </h1>"); puts(" </body>"); puts("</html>"); return(EXIT_SUCCESS); }
Dieses Script kann jetzt zum Beispiel als website.c gespeichert und anschließend mit OpenBSDs C‑Compiler zu einem Programm gemacht werden; ich empfehle die Aktivierung von Warnungen und Optimierungen sowie statischer Kompilierung, damit keine externen Abhängigkeiten bei plötzlichem Ausfall die Funktionsfähigkeit einschränken können:
cc -static -Wall -O3 -o website website.c
Ein einfacher Aufruf des Programms website gibt jetzt, wenn alles geklappt hat, den Statusheader sowie den deutschsprachigen (da HTTP_ACCEPT_LANGUAGE lokal natürlich nicht existiert) HTML-Code aus. Als nächstes müssen wir nur noch den Webserver konfigurieren. Falls ihr bisher nginx benutzt habt, ist es sinnvoll, ihn zu deaktivieren, bevor der httpd gestartet wird. Außerdem sollte slowcgi, die auf das Wesentliche reduzierte FastCGI-Implementierung von OpenBSD, aktiviert werden:
# rcctl stop nginx # rcctl disable nginx # rcctl enable slowcgi # rcctl start slowcgi
Jetzt folgt der letzte Schritt: Beim Aufruf von Sind-wir-schon-verloren.net soll der httpd die Ausgabe des oben geschriebenen FastCGI-Programms zurückgeben. Es ist nicht damit getan, den Server auf das Verzeichnis zeigen zu lassen, denn mit Binärdaten kann er hier nichts anfangen. Ein Eintrag in eurer /etc/httpd.conf schafft Abhilfe:
ext_addr="egress"
server "sind-wir-schon-verloren.net" {
# Der Server soll sich mit und ohne "www" gleich verhalten
alias "www.sind-wir-schon-verloren.net"
listen on $ext_addr port 80
# Der Aufruf "zeigt" direkt auf das FastCGI-Script:
root "/pfad/zu/eurem/programm"
# FastCGI aktivieren:
fastcgi socket "/run/slowcgi.sock"
}
Ein beherztes — falls noch nicht geschehen — Aktivieren von httpd und einen Neustart (rcctl restart httpd) des Daemons später ist eure Webanwendung wieder von außen erreichbar, als wäre sie nie weg gewesen — sie benötigt nur keine zusätzlichen Pakete mehr. Da nun alles wie gewünscht funktioniert, können wir schließlich unseren jetzt nur noch unnötig Platz verschwendenden nginx-Server und die PHP-Installation entfernen. Dabei sollte darauf geachtet werden, dass keine alten PHP-Module übrig bleiben, weshalb eine vorherige Auflistung der installierten PHP-Teile ratsam ist:
# pkg_info | grep php # pkg_delete php-fpm nginx
Möglicherweise bittet pkg_delete euch nun darum, übrig gebliebene Konfigurationsverzeichnisse zu löschen. Das könnt ihr gefahrlos tun. Schaut auch in eurer /etc/rc.conf.local nach, ob nginx und php-fpm noch in euren Startscripts auftauchen.
Fertig!

Compiliert mit musl-gcc ist das statisch gelinkte Programm auch schön klein:
‑rwx—— 1 hardy hardy 5704 Jul 23 09:01 website*
Meines Wissens funktioniert musl nicht unter OpenBSD, zumindest konnte ich bisher keinen Hinweis darauf finden. Startet das Programm denn überhaupt?
Natürlich nicht mit Deinem Mulu-Mulu-BSD. Mit Linux selbstverständlich ja.
Verstehe. Textverständnis ist ja auch euer Feind.
Die Anleitung gilt nicht für Linuux??? DU SCHwEIN!!!!!1111elf
Tscha, taugt halt nix.
Unter Linux müsste man sich irgendeines zusammengekleckerten Drittanbieterservers (nginx oder so) bedienen
und den pledge()-Aufruf aus dem Code entfernen, dann sollte es allerdings auch gehen.Nachtrag: Ich habe den Code oben mal geändert, jetzt sollte er auch unter anderen Systemen problemlos funktionieren.