Subject: Re: Hanging ftpd processes

From: Anders Thulin <[email-masked]>
Date: Fri, 7 Mar 1997 07:33:04 +0100 (MET)

David Greenman:
>    How about instead of setting an alarm(0) (clearing the alarm) when exiting
> the command loop to process a command, set a larger alarm time like 24 hours?

  Or rather make it configurable (from the ftpaccess file) to whatever
is suitable for the site. 

Anders Thulin      [email-masked]       013-23 55 32
Telia Engineering AB, Teknikringen 2B, S-583 30 Linkoping, Sweden

---

From: Albert Lunde <[email-masked]>
Date: Fri, 7 Mar 1997 23:58:04 -0600

Below is a perl 5 script I've been using to kill old ftpd
processes. I was written for HP/UX 10.20 and is likely full of OS- and
site-specific code.

It makes a detailed attempt to compute elapsed time from the ps output, to
distingush anonymous from real users, to distingush idle vs. active
sessions, and applies different time limits to accordingly. It reports by
mail when it kills a process.

Huristics are used that can sometimes fail.

- - cut here - -
#!/opt/local/bin/perl -w

#1234567890123456789012345678901234567890123456789012345678901234567890123456
#     UID   PID  PPID  C    STIME TTY       TIME COMMAND
#    root     0     0  0  Dec 13  ?         7:03 swapper
#    root     1     0  0  Dec 13  ?         0:00 init
#    root     2     0  0  Dec 13  ?        15:25 vhand
#    root 28753   660  0 15:40:52 ?         0:00 ftpd:
pppb519.pppp.ap.so-net.or.jp: anonymous/mozilla@: IDLE
#    root 28730   660  0 15:39:39 ?         0:00 ftpd:
highwood.library.nwu.edu: jhobbs: IDLE
# webserv 28774 13362  0 15:42:04 ?         0:00 /opt/local/etc/httpd/httpd
# webserv 28743 13362  0 15:40:14 ?         0:00 /opt/local/etc/httpd/httpd
#    root 13362     1  0 02:01:24 ?         0:17 /opt/local/etc/httpd/httpd
#    root 28792 18503  4 15:43:14 ttyp1     0:00 ps -ef

sub parse_ps {
# parse one line in ps output
	my($line)=@_;
        my(@result)=();
	my($etotal);
	my($stotal);
	my($thensec)="";
	my($user,$pid,$ppid,$c,$stimecol,$tty,$etime,$cmd);
	if ($line =~ /\s*([a-z0-9\-]+)\s+(\d+)\s+(\d+)\s+(\d+)/){
	    $user=$1;
	    $pid=$2;
	    $ppid=$3;
	    $c=$4;
	    $stimecol=substr($line,24,8);
#            $stimecol="23:59:58";
	    $stimecol =~ s/^\s+//;
	    $stimecol =~ s/\s+$//;
	    my($tail)=substr($line,33);
	    $tail=~ /\s*(\S+)\s*(\S+)\s*(.+)/;
	    $tty=$1;
	    $etime=$2;
	    $cmd=$3;
	    $cmd =~ s/\s+$//;
#
	    if($etime=~ /(\d+)\:(\d+)/){
		my($emin)=$1;
		my($esec)=$2;
		$etotal=$esec+(60*$emin);
	    }else{
		$etotal="";
	    };
#
	    if($stimecol =~ /(\d+)\:(\d+)\:(\d+)/){
		my($shr)=$1;
		my($smin)=$2;
		my($ssec)=$3;
#               need to parse this to get elapsed time to present
		$thensec=$ssec+60*($smin+(60*$shr));
#		printf "thensec=$thensec\n";
		if($thensec>$nowsec){
		    $thensec=$thensec-86400;
		};
#		printf "thensec=$thensec\n";
	       $stotal=$nowsec-$thensec;
	    }else{
		$thensec="";
		$stotal=43200;
	    };
#
#	    my($etmin)=int($etotal/60);
#	    my($stmin)=int($stotal/60);
#	     print "user=$user pid=$pid ppid=$ppid c=$c
stime=\"$stimecol\"\ntty=$tty time=$etime cmd=$cmd\nnowsec=$nowsec
thensec=$thensec stotal=$stotal $stmin min etotal=$etotal $etmin min \n";
	    @result=($user,$pid,$ppid,$stotal,$tty,$etotal,$cmd);
         }
	return @result;
}

# main program
# run ftpwho and look for old procs
# kill after 1 hr if IDLE, 3 hr always for anonymous
# kill after 2 hr if IDLE, 4 hr always for others

@now=localtime(time);
$nowsec=$now[0]+60*($now[1]+(60*$now[2]));
my(@report)=();
my($dorep)=0;
my($mailto)="Albert-Lunde\@nwu.edu";

open(WHO,"/local-adm/bin/ftpwho|")||die("ftpwho failed");

#while(<>){
while(<WHO>){
 chomp;
 next if /^Service class/;
 next if /^\s+\-/;
 next if /^\s*$/;
 print "$_\n";
 my($mess)="";
 my($user,$pid,$ppid,$stotal,$tty,$etotal,$cmd)=&parse_ps($_);
 my($stmin)=int(($stotal/60)+0.5);

 next if $cmd =~ /^\s*ftp:/;
 my($idle)=0;
 my($anon)=0;
 $idle=1 if ($cmd =~ /IDLE$/);
 $idle=1 if ($cmd =~ /IDL$/);
 $idle=1 if ($cmd =~ /ID$/);

 $anon=1 if $cmd =~ /^\s*ftpd:\s+\S+\s+anonym/;

 print "idle=$idle anon=$anon connect time=$stmin min\n";

 my($killme)=0;
 if($anon){
     if($idle){
#        kill IDLE after 1 hr
	 $killme=($stmin>60);
     }else{
#        kill anything after 3 hr
	 $killme=($stmin>180);
     }
 }else{
     if($idle){
#        kill IDLE after 2 hr
	 $killme=($stmin>120);
     }else{
#        kill anything after 4 hr
	 $killme=($stmin>240);
     }
 }

 if($killme){
     $dorep=1;
     my($mess)=$_;
     $mess=~ s/^\s+//;
     $mess=~ s/\s+$//;
     $mess="KILL: ".$mess;
     print "$mess\n";
     push @report,"idle=$idle anon=$anon connect time=$stmin min";
     push @report,$mess;
     my($doit)="/usr/bin/kill $pid";
     print "Do: $doit\n";
     system($doit);
 }
}

if($dorep){
#    report results by mail
    open (MAIL, "|/usr/sbin/sendmail -oi -t -f root");

#   print MAIL "Return_path: <root\@pubweb.nwu.edu>\n";
    print MAIL "From: root\@pubweb.nwu.edu\n";
    print MAIL "To: $mailto\n";
    print MAIL "Subject: pubweb ftp process killed cronjob\n";
    print MAIL "\n\n";
    print join("\n",@report),"\n";
    print MAIL join("\n",@report),"\n";
    close (MAIL);
};

close(WHO);
#
- - cut here - -

---
    Albert Lunde                      [email-masked]