ubuntuusers.de

Perl

Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:

Dieser Artikel ist mit keiner aktuell unterstützten Ubuntu-Version getestet! Bitte teste diesen Artikel für eine Ubuntu-Version, welche aktuell unterstützt wird. Dazu sind die Hinweise zum Testen von Artikeln zu beachten.

Perl ist eine vielseitige Skriptsprache, die vor allem vor der starken Verbreitung von PHP häufig für die serverseitige Implementierung von Webapplikationen wurde und auch heute noch einen tauglichen Kandidaten für diesen Anwendungsfall darstellt.

nginx ist ein mittlerweile sehr beliebter und schlanker Webserver, der die FastCGI-Schnittstelle unterstützt und sich damit für die serverseitige Programmierung an jede Programmiersprache binden lässt, die diese Schnittstelle bedienen kann.

Dieser Artikel beschreibt eine Möglichkeit, wie sich Perl und nginx miteinander kombinieren lassen. Dabei steht die Verwendung von Paketen aus den offiziellen Paketquellen im Vordergrund. Eine hier nicht näher beschriebene Alternative ist der Bezug der genannten Perl-Module von CPAN, was sich für diejenigen anbietet, die sich der möglichen Risiken und Wechselwirkungen beim Einsatz von Paketen und Programmen aus Fremdquellen bewusst sind und dennoch eine neuere/andere Version benötigen.

Achtung!

Mit der Anbindung von Perl über die FastCGI-Schnittstelle kann der gesamte Sprachumfang Perls einschließlich des Zugriffs auf Systemfunktionen genutzt werden. Dies sollte bei der Vergabe von Berechtigungen und der Auswahl der Dienstkonten berücksichtigt werden.

Installation

Für den Aufbau der hier dargestellten Umgebung ist neben eine installierten nginx Webserver die Installation des folgenden Pakets notwendig[1]:

  • libfcgi-perl

Befehl zum Installieren der Pakete:

sudo apt-get install libfcgi-perl 

Oder mit apturl installieren, Link: apt://libfcgi-perl

Konfiguration

Die Konfiguration der zuvor installierten Pakete erfolgt in mehreren Schritten.

Zuerst entsteht ein Wrapper um das soeben installierte FCGI-Modul, der dieses an einen Netzwerksocket bindet. Im nächsten Schritt erfolgt die Anpassung von nginx zur Nutzung dieses Netzwerksockets für Perl-Skripte. Zum Abschluss wird eine Service Unit eingerichtet, um den Wrapper steuern zu können.

Perl FCGI-Wrapper

Der Perl FCGI-Wrapper sorgt dafür, dass das FCGI-Modul an einen Socket gebunden wird und dort dauerhaft auf Aufträge wartet. Grundsätzlich ist eine Bindung sowohl über einen Netzwerk-Socket (wie hier beschrieben) als auch einen Unix Domain Socket möglich.

Das hier aufgeführte Skript bindet das FCGI-Modul an den Port 9000 der lokalen Loopback-Adresse (127.0.0.1). Die Website, von der dieses Skript abgeleitet ist, ist nicht mehr direkt verfügbar, daher an dieser Stelle ein Link 🇬🇧 auf die Wayback Machine des Internet Archive 🇬🇧.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/perl

use FCGI;
use Socket;
use POSIX qw(setsid);

require 'syscall.ph';

&daemonize;

#this keeps the program alive or something after exec'ing perl scripts
END() { } BEGIN() { }
*CORE::GLOBAL::exit = sub { die "fakeexit\nrc=".shift()."\n"; };
eval q{exit};
if ($@) {
    exit unless $@ =~ /^fakeexit/;
};

&main;

sub daemonize() {
    chdir '/'                 or die "Can't chdir to /: $!";
    defined(my $pid = fork)   or die "Can't fork: $!";
    exit if $pid;
    setsid                    or die "Can't start a new session: $!";
    umask 0;
}

sub main {
        $socket = FCGI::OpenSocket( "127.0.0.1:9000", 10 ); #use IP sockets
        $request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket );
        if ($request) { request_loop()};
            FCGI::CloseSocket( $socket );
}

sub request_loop {
        while( $request->Accept() >= 0 ) {

           #processing any STDIN input from WebServer (for CGI-POST actions)
           $stdin_passthrough ='';
           $req_len = 0 + $req_params{'CONTENT_LENGTH'};
           if (($req_params{'REQUEST_METHOD'} eq 'POST') && ($req_len != 0) ){
                my $bytes_read = 0;
                while ($bytes_read < $req_len) {
                        my $data = '';
                        my $bytes = read(STDIN, $data, ($req_len - $bytes_read));
                        last if ($bytes == 0 || !defined($bytes));
                        $stdin_passthrough .= $data;
                        $bytes_read += $bytes;
                }
            }

            #running the cgi app
            if ( (-x $req_params{SCRIPT_FILENAME}) &&  #can I execute this?
                 (-s $req_params{SCRIPT_FILENAME}) &&  #Is this file empty?
                 (-r $req_params{SCRIPT_FILENAME})     #can I read this file?
            ){
        pipe(CHILD_RD, PARENT_WR);
        my $pid = open(KID_TO_READ, "-|");
        unless(defined($pid)) {
            print("Content-type: text/plain\r\n\r\n");
                        print "Error: CGI app returned no output - ";
                        print "Executing $req_params{SCRIPT_FILENAME} failed !\n";
            next;
        }
        if ($pid > 0) {
            close(CHILD_RD);
            print PARENT_WR $stdin_passthrough;
            close(PARENT_WR);

            while(my $s = <KID_TO_READ>) { print $s; }
            close KID_TO_READ;
            waitpid($pid, 0);
        } else {
                    foreach $key ( keys %req_params){
                       $ENV{$key} = $req_params{$key};
                    }
                    # cd to the script's local directory
                    if ($req_params{SCRIPT_FILENAME} =~ /^(.*)\/[^\/]+$/) {
                            chdir $1;
                    }

            close(PARENT_WR);
            close(STDIN);
            #fcntl(CHILD_RD, F_DUPFD, 0);
            syscall(&SYS_dup2, fileno(CHILD_RD), 0);
            #open(STDIN, "<&CHILD_RD");
            exec($req_params{SCRIPT_FILENAME});
            die("exec failed");
        }
            }
            else {
                print("Content-type: text/plain\r\n\r\n");
                print "Error: No such CGI app - $req_params{SCRIPT_FILENAME} may not ";
                print "exist or is not executable by this process.\n";
            }

        }
}

Gespeichert wird das Perl-Skript unter /usr/local/bin/perl-fastcgi - oder einem beliebigen anderen einprägsamen Namen[3][4]. Der neue Name muss in der später angelegten Service Unit ebenso angepasst werden. Mit dem Befehl

chmod 755 /usr/local/bin/perl-fastcgi 

wird dafür Sorge getragen, dass alle das Skript ausführen dürfen, aber nur root Änderungen am Skript vornehmen darf[2][4][5].

Testweise sollte der Aufruf von

/usr/local/bin/perl-fastcgi 

einen neuen Prozess erzeugen, der auf Port 9000 lauscht.

Nach dem Start des Wrappers ist der erste Schritt zum Ziel abgeschlossen: Es können Perl-Skripte über die FastCGI-Schnittstelle an den wartenden Interpreter übergeben werden.

nginx

Nachdem mit dem FastCGI-Wrapper im letzten Abschnitt ein Prozess geschaffen wurde, der Perl-Skripte verarbeiten kann, wird nginx in diesem Abschnitt so konfiguriert, dass dieser Webserver die Skripte auch an den Prozess übergibt.

nginx ist ein sehr vielseitig konfigurierbarer Webserver. Die unten stehende Konfiguration ist daher also als absolutes Minimum zu verstehen und sollte in jedem Fall an die konkreten Anforderungen angepasst werden. Da die Konfiguration der Standardseite geändert wird, ist die Originaldatei entsprechend zu sichern.

Die zu ändernde Datei heißt /etc/nginx/sites-available/default und erhält den Inhalt[3][4]:

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	root /var/www/html;

	index index.html index.htm index.pl index.nginx-debian.html;

	server_name _;

	location / {
		try_files $uri $uri/ =404;
	}

	location ~ \.pl$ {
		include fastcgi.conf;
		fastcgi_pass 127.0.0.1:9000;
	}
}

Nach dem Austausch des Inhalts dieser Datei wird der nginx-Dienst mit

sudo systemctl restart nginx 

neu gestartet[2]. Sollte der Perl-Prozess vom vorherigen Abschnitt noch laufen, könnten jetzt bereits pl-Dateien im Verzeichnis /var/www/html und dessen Unterverzeichnissen ausgeführt werden.

Zum Testen kann eine kleine index.pl mit nachfolgendem Inhalt im zuvor genannten Verzeichnis und folgendem Inhalt erstellt werden[3][4]:

#!/usr/bin/env perl

print "Content-type: text/html\n\n";

print <<HTML;
<html>

<head>
	<title>Perl funktioniert!</title>
</head>

<body>
	<h1>Perl funktioniert!</h1>
</body>

</html>
HTML

Das Skript ist vor dem Test ausführbar zu machen[5]:

sudo chmod +x /var/www/html/index.pl 

Ein Aufruf des Rechners im Webbrowser sollte feststellen, dass Perl funktioniert. Falls nicht, ist nochmals zu prüfen, ob die Dateirechte und -namen passen.

Nach diesem Test kann der Perl-Prozess selbst beendet werden.

Service Unit

Damit der Wrapper auch als Systemdienst verwaltet werden kann, fehlt nun noch die Anlage einer Service-Unit für systemd. Auch hier gibt es wieder mehrere Detaillierungsgrade der Konfiguration, wobei die abgebildete Unit-Definition einen rudimentären Dienst aufbauen hilft.

Die Service-Unit wird hier der Einheitlichkeit halber perl-fastcgi.service genannt und im Verzeichnis /etc/systemd/system abgelegt[2][3]. Nach der Anlage ist wieder darauf zu achten, dass die Dateiberechtigungen den Dienst vor unberechtigten Änderungen schützen[5].

[Unit]
Description=Daemonize Perl FastCGI wrapper
After=network-online.target

[Service]
ExecStart=/usr/local/bin/perl-fastcgi
Type=forking
User=www-data

[Install]
WantedBy=multi-user.target

Mit den Befehlen

sudo systemctl daemon-reload
sudo systemctl start perl-fastcgi 

wird die neu angelegte Service-Unit eingelesen und der Dienst gestartet[2]. Sollte etwas nicht funktionieren, sollte zuerst überprüft werden, ob der Wrapper wie in der Direktive ExecStart genannt wurde.

  • nginx - Hauptartikel hier im Wiki zu nginx

  • Perl - Hauptartikel hier im Wiki zu Perl

Diese Revision wurde am 5. Mai 2021 16:18 von noisefloor erstellt.
Die folgenden Schlagworte wurden dem Artikel zugewiesen: Perl, Server, ungetestet