[Perl] Eigen WHOIS-server bouwen

Pagina: 1
Acties:

  • maartenba
  • Registratie: November 2001
  • Laatst online: 18-08 11:39
Een dik jaar geleden voor intern gebruik een whois-servertje geschreven in Perl (code: zie verder).
Uiteindelijk is het ding nooit in gebruik geraakt, maar nu willen we het toch gaan gebruiken. Probleem is: start je dit vanuit Linux shell, dan stopt de server als je uitlogt...

De vraag is nu: hoe krijg je dit als lopend proces uit de shell, en hoe herstart je bij fouten ?
D.m.v. wat zoeken op Google ben ik op het "forken" van processen gekomen, maar daar loop ik vast... Iemand die me op het juiste pad kan zetten ?

De huidige code:
code:
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/perl

#########################################################################
## Settings
$bufferlen=20000;   ## Buffer length
$password="xxxx";   ## Server shutdown password

$dbname = "xxxxx"; # the database's name
$dbusername = "xxxxx"; # the database's username
$dbpassword = "xxxxx"; # the database's password

#########################################################################
use IO::Socket;
use IO::Select;
use DBI;

$SIG{PIPE}='IGNORE';
$serversock=new IO::Socket::INET(Listen=>1,LocalPort=>43);
$childsock=new IO::Select($serversock);
$/="\0";

#########################################################################
## Connect to the database
$dbh = DBI->connect("DBI:mysql:database=$dbname", "$dbusername", "$dbpassword") || die "Error: Couldn't open database !\n";

#########################################################################
## Start the server
&serve;

$bytestransferred=0;
$numrequests=0;

#########################################################################
## Server
sub serve {
    %in_str = ();   ## Incoming data
    %in_sock = ();  ## Incoming sockets
    %ack = ();  ## Incoming acks
    $num_sock = -1; ## Socket count
    $stat_started = localtime;  ## Server start time

    print "Server started ($stat_started)\n";

    ## Keep switching sockets to listen to
    while(@sock=$childsock->can_read) {
        foreach(@sock) {
            ## New connection
            if($_==$serversock) {
                $num_sock++;

                $newclient=$serversock->accept;
                $childsock->add($newclient);

                $in_str{$newclient} = "";
                $in_sock{$newclient} = $num_sock;
            } else {
                ## Get incoming data
                my $R=sysread($_, $i, $bufferlen);

                ## Incoming socket still active ?
                ## If not -> disconnect
                if ($R==0) {
                    $T=syswrite($_, '', $bufferlen);
                    if ($T==undef) {
                        &disconnect_sock($_);
                    }
                } else {
                    ## Add incoming data to socket string
                    if (length($i)<=2) {
                        if (($i ne "\n") && ($i ne "\r") && ($i ne "\r\n") && ($i ne "\n\r")) {
                            $in_str{$_} = $in_str{$_}."$i";
                        } else {
                            if ($in_str{$_} ne "") {
                                &parse_in($_,$in_str{$_});
                                $in_str{$_}="";
                            }
                        }
                    } else {
                        &parse_in($_,$i);
                        $in_str{$_}="";
                    }
                }
            }
        }
    }
}


#########################################################################
## Send message to socket
sub send {
    my($sock,$message) = @_;

    $bytestransferred += length($message);

    if ($message !~ /\0$/) {
        $message.="\0";
    }
    syswrite($sock, $message,$bufferlen);
}

#########################################################################
## Broadcast message
sub broadcast {
    my($message) = @_;
    foreach $C ($childsock->handles) {
        &send($C,$message);
    }
}

#########################################################################
## Parser
sub parse_in {
    my($sock,$data) = @_;

    $bytestransferred += length($data);

    ## Action to do:
    if ($data =~ /^exit/i) {        ## Disconnect client
        &disconnect_sock($sock);
    } elsif ($data =~ /^stop $password/i) { ## Stop server
        &stop_server();
    } else {
        &whois($sock,$data);
        &disconnect_sock($sock);
    }
}

#########################################################################
## Disconnect
sub disconnect_sock {
    my($sock) = @_;

    $childsock->remove($sock);
    $in_sock{$sock} = undef;
}

#########################################################################
## Stop server
sub stop_server {
    &broadcast("Server stopped");
    $stat_stop = localtime;
    print "Server stopped ($stat_stop)\n";
    print "Transferred $bytestransferred bytes\n";
    print "$numrequests requests served\n";

    # Close the DB connection
    $dbh->disconnect || die "Error: Couldn't close database !\n";

    exit(1);
}

#########################################################################
## Whois
sub whois {
    my($sock,$domain) = @_;

    $numrequests++;

    &send($sock,"WHOIS v1.0");
    &send($sock,"");

    $query = "
        SELECT `domain`
        FROM `whois`
        WHERE `domain` = '$domain'
    ";
    
    my $sth=$dbh->prepare($query); # prepare the database for the SQL query
    $sth->execute; # execute the SQL statement

    my ($db_domain) = $sth->fetchrow_array;

    if ($db_domain eq $domain) {
        $status = "REGISTERED";
    } else {
        $status = "FREE";
    }

    &send($sock,"$domain: $status");
}

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:56
maartenba schreef op 24 July 2003 @ 23:18:
Probleem is: start je dit vanuit Linux shell, dan stopt de server als je uitlogt...
Niet als je 'm met nohup start. (Zie: "man nohup")
De vraag is nu: hoe krijg je dit als lopend proces uit de shell, en hoe herstart je bij fouten?
In principe heet dat "daemonizen". Google maar eens op Perl en daemonize. In feite ben je met een simpele fork al klaar, al is 't in Linux gebruikelijk om dubbel te forken (ik kan de preciese procedure en redenen wel nazoeken, als je geïnteresseerd bent), om bepaalde afhankelijkheden van de terminal kwijt te raken. Ik weet niet meer exact wat het nut was.

Wat betreft het levend houden van je programma, kun je twee processen laten draaien. Een proces start een child proces dat het echte werk doet en als de child afsluit, start de parent proces een nieuwe child op. Kijk voor zulke functionaliteit naar de SIGCHLD signal en de Perl signal manual pages ("man perlsig").

Overigens hoef je dit allemaal niet zelf te schrijven. Je kunt ook bestaande utillities gebruiken, zoals bijvoorbeeld inetd ("man inetd"), die specifiek toegespitst is op het runnen van internet services, of iets als Dan Bernstein's supervise utillity uit de daemontools collectie.

edit:
Je merkt wel dat de moraal van dit verhaal is dat je heel veel informatie uit Google en de manual pages van de standaard UNIX (of Linux) utillities kunt halen! ;)

[ Voor 11% gewijzigd door Soultaker op 24-07-2003 23:35 ]


  • maartenba
  • Registratie: November 2001
  • Laatst online: 18-08 11:39
Soultaker schreef op 24 juli 2003 @ 23:33:
Je merkt wel dat de moraal van dit verhaal is dat je heel veel informatie uit Google en de manual pages van de standaard UNIX (of Linux) utillities kunt halen! ;)
Je moet het aleen weten vinden :) Eerst maar eens de inetd-aanpak proberen, deze lijkt me de makkelijkste oplossing, die waarschijnlijk even goed werkt als andere oplossingen...

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:56
maartenba schreef op 24 July 2003 @ 23:36:
Je moet het aleen weten vinden :) Eerst maar eens de inetd-aanpak proberen, deze lijkt me de makkelijkste oplossing, die waarschijnlijk even goed werkt als andere oplossingen...
Ik zou zelf zeggen dat het gebruik van supervise het makkelijkst is, zeker als je je script niet kunt detachen (daemonizen). Voor het automatisch starten bij het opstarten is inetd natuurlijk weer handig (anders moet je daar een rc-scriptje voor maken).

  • maartenba
  • Registratie: November 2001
  • Laatst online: 18-08 11:39
Heb hem in xinetd gestopt, en "works like a charm"...
Hartelijk bedankt !

Verwijderd

Heb proggie even laten draaien hier maar hij lijkt sowieso geen incoming data te zien, je kunt er wel naar connecten, but thats all... :) Zal wel aan mij liggen....

  • maartenba
  • Registratie: November 2001
  • Laatst online: 18-08 11:39
Probeer eens eerst een woordje te typen gevolgd door return :)

Verwijderd

maartenba schreef op 25 July 2003 @ 18:42:
Probeer eens eerst een woordje te typen gevolgd door return :)
Nope...
Pagina: 1