3 # Custom wrapper around the CVS pserver for Diogenes
5 # Copyright 2003-2004 Jeremy Lainé
9 use Fcntl
qw(:DEFAULT
:flock);
11 use POSIX
qw(strftime
waitpid WNOHANG WIFEXITED
);
13 my $package = "cvs.pl";
19 # get command-line arguments
21 if ( not getopts
('dhp:r:fms:', \
%opts) or $opts{'h'}) {
26 # check we have a valid port
27 if ($opts{'p'} !~ /^[0-9]+$/) {
28 print("Error : no port or invalid port specified!\n");
33 # check we have a cvsroot
35 print("Error : no CVS repository was specified!\n");
39 $opts{'r'} =~ s/^(.*)\/$/$1/;
40 if (! -d
"$opts{'r'}/CVSROOT") {
41 print("Error : no CVS repository found at $opts{'r'}\n");
45 # check we have a valid suicide delay
46 if ($opts{'s'} !~ /^[0-9]*$/) {
47 print("Error : invalid suicide delay!\n");
56 # debugging information
69 if (open(LOG
,">> $opts{'r'}/CVSROOT/serverlog"))
71 my $hdr = strftime
("%a %b %e %H:%M:%S",gmtime)." [$daemon]";
75 print LOG
"$hdr $msg\n";
81 # add a user to the passwd file
85 my $pwfile = $opts{'r'}."/CVSROOT/passwd";
87 # read the password file, strip out current user
89 if (open(FH
,"< $pwfile")) {
91 @lines = grep !/^$user(:.*)?(:*)?$/,@lines;
95 # add user to password file
96 my @pwuid = getpwuid($<);
97 push @lines, $user . "::" . $pwuid[0] . "\n";
98 sysopen(FH
, $pwfile, O_WRONLY
| O_CREAT
)
99 or die("Can't open passwd file!");
101 or die("Can't lock passwd file!");
109 # the main loop of the server
113 socket(Server
, PF_INET
, SOCK_STREAM
, getprotobyname('tcp'));
115 # so we can restart our server quickly
116 setsockopt(Server
, SOL_SOCKET
, SO_REUSEADDR
, 1);
118 # build socket address
119 my $addr = sockaddr_in
($opts{'p'}, 127.0.0.1);
121 # bind and start listening
122 if (!bind(Server
, $addr) or !listen(Server
,SOMAXCONN
)) {
123 print "Error : could not bind and listen to port $opts{'p'}!\n";
129 # if necessary, fork to background
130 if ($opts{'f'} && fork) {
135 # store the PID of the parent process
138 # select single-shot or full server
140 &log("forked daemon bound");
143 # set up zombie reaper and suicide timer
145 while (waitpid(-1,&WNOHANG
) != -1) {
146 &WIFEXITED
($?
) and $children--;
149 # if he have no more children, start suicide timer
150 if (($$ == $daemon) && ($children == 0)) {
156 &log("reached inactivity limit of $opts{s} seconds, returning");
160 # if set, start suicide timer
163 # full-blown server that forks for each request
164 for (my $conn = 0;; $conn++) {
165 # we can get interrupted system calls
166 # ignore these and loop back
167 if (!accept(Client
,Server
)) {
171 # stop inactivity timer
174 &debug
("forking child to handle request");
176 if (my $cpid = fork) {
178 # parent process, closes unused handle
182 } elsif (defined $cpid) {
186 &serveClient
(\
*Client
);
191 print "Could not fork serveClient!";
199 &log("single-request daemon bound");
200 # accepts a single connection, then returns
201 accept(Client
,Server
);
203 &serveClient
(\
*Client
);
211 # wraps around an instance of "cvs pserver"
215 my $cvsroot = $opts{'r'};
218 chomp(my $in = <$client>);
219 if ($in !~ /^BEGIN AUTH REQUEST$/) {
220 &log("client did not send BEGIN AUTH REQUEST, returning!");
224 chomp($in = <$client>);
225 if ($in !~ /^$cvsroot$/) {
230 chomp(my $user = <$client>);
232 # password, discarded
235 chomp($in = <$client>);
236 if ($in !~ /^END AUTH REQUEST$/) {
241 # add the user to the passwd file
242 &log("authenticated $user");
250 local(*Reader
,*Writer
);
252 # create bidirectional pipe
253 if ( !open2
(\
*Reader
, \
*Writer
, "cvs -f --allow-root $cvsroot pserver") ) {
258 print Writer
"BEGIN AUTH REQUEST\n$cvsroot\n$user\nA\nEND AUTH REQUEST\n";
260 # process input and output
261 if (my $pid = fork) {
264 # process client input
265 while (my $inn = <$client>)
273 } elsif (defined $pid) {
276 # feed output back to client
277 while (my $out = <Reader
>)
287 die("Could not fork!");
293 # display program syntax
296 "[ This is $package (v$version), Diogenes's wrapper around the CVS pserver ]\n\n",
298 " $package (-h | -p <port> -r <cvsroot>) [options]\n\n",
300 " -h - display this help message\n",
302 " -p <port> - listen on port <port>\n",
303 " -r <cvsroot> - use the local CVS repository <cvsroot>\n",
305 " -d - debugging mode\n",
306 " -f - fork to background\n",
307 " -m - serve multiple client requests instead of dying after first client\n",
308 " -s <seconds> - suicide after <seconds> seconds of inactivity\n";