New features, and bugfix for faimond

Jonas Eriksson zqad at hpc2n.umu.se
Tue Aug 28 16:02:36 CEST 2007


On Tue, Aug 28, 2007 at 11:52:06AM +0200 Thomas Lange wrote:
> >>>>> On Fri, 24 Aug 2007 15:34:51 +0200, Jonas Eriksson <zqad at hpc2n.umu.se> said:
> 
>     > faimond, bad clients-patch:
>     > * Add a timeout, so that "bad clients" that are locking up the
>     >   faimond will be disconnected after a given timeout. I see this
>     >   as a bugfix since bad clients may lock up the faimond
>     >   indefinitely.
> Did you ever had this problem? I'm wondering if this could happen,
> since the install clients useses the nc command to send messages to
> the faimond. This is tcp and very short, so there should be no
> problems IMO.

Correct, there should not be any problems. Although, in my
experience it most often is problems anyway. Besides, it does not
have to be fai that connects, but perhaps a network probe. Even
though these tools often disconnect right away, the res could get
lost and faimond have to wait for the lower protocol-level to
timeout. This is somewhat a shortening of this timeout to cope
with unforseen problems.

> I would suggest that faimond will not die with a message, but that it
> just closes the socket connection. Can you prepare a patch for this?

This signal is just used to abort the read(). Since the die make
script fall through the read, the client is disconnected and the
daemon keeps on running.

> Also no timestamp should be printed by default. faimond was created as
> a very simple daemon, mainly as the socket receiving part for the
> faimond-gui which is currently under development.

I must say that I am a bit sceptical to let the faimond-gui lead
the development of faimond. My belief is that most sites will use
faimond without the GUI.

Although, it is not a problem to add a switch for the added
timestamp. I've done so in the attached patch by adding -T to
activate timestamping.

>     > faimond, bad clients+better deamon behaviour-path:
>     > * Use Proc::Daemon to fork a real daemon unless given the
>     >   argument -d (debug). Added support for logfiles and pidfiles.
>     >   This means that the fai-server package will depend on the
>     >   debian package libproc-daemon-perl.
> I prefer not using another package if we do not striclty need it.

I figured this could be a problem. The reason for using the
Proc:Daemon package is to avoid reinventing the wheel and by
that also having more code with potential bugs. It also makes the
code more readable, IMHO.

>     > fai-mirror, hosts in package_config:
>     > * Add support for host-files and not just classes in
>     >   package_config-dir.
> This patch is now included.

Thanks!

Additional changes/contributions:

Last time i forgot to include the faimond init.d-script. By
having this, the daemon can behave more debian-esque and make it
easier to use since it then will be working as every other
package.

Now, I'm not sure if the init.d-script should be started by
default..so I leave this descision as en exercise to the reader.

This patch also contains further updates. I've continued to
develope the daemon-like behaviour, and added better signal
handling, and fixed a bug with the log file that i happened to
avoid triggering when testing last week. For example, when
sending a TERM, INT or QUIT to the daemon, this is logged and the
pid file is removed before exiting. It also checks for running
processes identified by the pid file before starting; if the
pid in the pid file is running faimond logs an error and exits.

Best regards,
Jonas Eriksson

-------------------------------------
- Jonas Eriksson, zqad at hpc2n.umu.se -
- sysadmin @ hpc2n.umu.se           -
-------------------------------------
-------------- next part --------------
#! /bin/sh
# /etc/init.d/faimond: start/stop/restart faimond
# vim:et:sw=4:ts=4:

PATH=/bin:/sbin:/usr/bin:/usr/sbin

pidfile=/var/run/faimond.pid
binpath=/usr/sbin/faimond

test -x $binpath || exit 0

# Options for start/restart of the daemon
#   Add "-b" to let faimond do a fai-chboot -r on installed clients
#   Add "-T" to add timestamps to the log
FAIMOND=""

start() {
    echo -n "Starting fai monitoring daemon: faimond"
    start-stop-daemon --start --quiet --exec $binpath -- $FAIMOND
    echo "."
}

stop() {
    echo -n "Stopping fai monitoring daemon: faimond"
    start-stop-daemon --stop --pidfile $pidfile
    echo "."
}

case "$1" in
    start)
        start
    ;;
    stop)
        stop
    ;;
    restart)
        stop
        sleep 1
        start
    ;;
    *)
        echo "Usage: /etc/init.d/faimond {start|stop|restart}"
        exit 1
esac

exit 0
-------------- next part --------------
Index: bin/faimond
===================================================================
--- bin/faimond	(revision 4532)
+++ bin/faimond	(working copy)
@@ -10,42 +10,123 @@
 # Universitaet zu Koeln
 #
 #*********************************************************************
+# vim:et:ts=2:sw=2:
 
 use strict;
 use Socket;
 use Getopt::Std;
+use Proc::Daemon;
 
 $| = 1;
-my $port;
-our ($opt_b,$opt_h,$opt_p);
+my ($port, $timeout, $debug, $timestamp);
+my $pidfile = '/var/run/faimond.pid';
+my $logfile = '/var/log/faimond.log';
+
+our ($opt_b,$opt_h,$opt_p,$opt_l,$opt_t,$opt_d,$opt_P,$opt_T);
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub logline(@) {
+  open(LOGFILE, $logfile) or return 0;
+  print LOGFILE (scalar localtime(), ' - ') if ($timestamp);
+  print LOGFILE @_ or return 0;
+  close(LOGFILE);
+  return 1;
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub signal_die(@) {
+  logline(@_);
+  unlink($pidfile);
+  exit(1);
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub signal_warn(@) {
+  logline(@_) or die "log: $!";
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub signal_int(@) {
+  # Use the die-handler
+	signal_die('Caught deadly signal ' . shift() . "\n");
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 sub server_init() {
+  logline("FAI monitoring daemon starting..\n") or die "log: $!";
+  unless ($debug) {
+		if (-e $pidfile) {
+			# Pid file already exists. Check if it's a valid pid.
+			open(PIDFILE, '<', "$pidfile") or die "open $pidfile: $!";
+			my $pid = <PIDFILE>;
+			chomp($pid);
+			if ($pid ne '') {
+				# Kill -0 exits with value 0 if pid is alive
+				system("kill -0 $pid 2> /dev/null");
+				if ($? == 0) {
+					logline("Pidfile $pidfile exists and contains an existing pid. Exiting.\n");
+					exit(1);
+				}
+			}
+			close(PIDFILE);
+		}
+    $SIG{INT} = \&signal_int;
+    $SIG{QUIT} = \&signal_int;
+    $SIG{TERM} = \&signal_int;
+    $SIG{__DIE__} = \&signal_die;
+    $SIG{__WARN__} = \&signal_warn;
+		# HUP is usually used to reopen log files. This is not a problem
+		# in this design.
+    $SIG{HUP} = 'IGNORE';
+    Proc::Daemon::Init;
+    umask 022;
 
+    open(PIDFILE, '>', "$pidfile") or die "open $pidfile: $!";
+    print PIDFILE $$ or die "print $pidfile: $!";
+    close(PIDFILE);
+  }
+
+  # Listen
   my $proto = getprotobyname('tcp');
   socket(SERVER, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
-  setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or die "setsock: $!";
+  setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or die "setsockopt: $!";
 
   my $paddr = sockaddr_in($port, INADDR_ANY);
 
   bind(SERVER, $paddr) or die "bind: $!";
   listen(SERVER, SOMAXCONN) or die "listen: $!";
-  print "FAI monitoring daemon started on port $port\n";
+  logline("FAI monitoring daemon started on port $port with pid $$\n") or die "log: $!";
 }
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 sub big_loop() {
 
   # accept a connection, print message received and close
-  my ($client_addr,$inp);
+  my ($client_addr);
   while ($client_addr = accept(CLIENT, SERVER)) {
-    $inp = <CLIENT>;
+    my ($port, $iaddr) = sockaddr_in($client_addr);
+    my $ip = inet_ntoa($iaddr);
+
+    my $inp;
+
+    eval {
+      local $SIG{__DIE__};
+      local $SIG{__WARN__};
+      local $SIG{'ALRM'} = sub { die("Timeout"); };
+
+      alarm($timeout);
+      $inp = <CLIENT>;
+      alarm(0);
+    };
+
     close CLIENT;
 
+    if (!defined($inp) || $inp eq '') {
+      # Client did not send anything, or alarm went off
+      logline("$ip:$port: No data or timeout.\n") or die "log: $!"; 
+      next;
+    }
+
     if ($inp =~ /^(\S+)\s+TASKEND install 0/ && $opt_b) {
       my $cname = $1;
       system("fai-chboot -d $cname");
       # warn "Disabling pxelinux configuration for $cname\n";
-   }
-    print "$inp";
+    }
+    logline("$ip:$port: $inp") or die "log: $!";
   }
 }
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -60,15 +141,41 @@
 
    -b                   Call fai-chboot to change boot parameter.
    -p PORT              Set port to listen to. Default is 4711.
+   -l FILE              Logfile. Default is '$logfile'. Supply - for
+                        standard out.
+   -t TIMEOUT           Timeout for bad clients. 0 to disable.
+   -d                   Debug (don't fork).
+   -P FILE              PID-file. Default is '$pidfile'. Used only if
+                        starting in daemon mode.
+   -T                   Print timestamps in the log.
 
 EOF
   exit 0;
 }
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
-getopts('bhp:') || usage;
+getopts('bhTp:l:t:dP:') || usage;
 $opt_h && usage;
 $port = $opt_p || 4711;
+$timeout = $opt_t || 5;
+$debug = $opt_d || 0;
+$timestamp = $opt_T || 0;
 
+if (defined($opt_P)) {
+  $pidfile = $opt_P;
+}
+
+if (defined($opt_l)) {
+  if ($opt_l eq '-') {
+    $logfile = ">&STDOUT";
+  }
+  else {
+    $logfile = ">>$opt_l";
+  }
+}
+else {
+  $logfile = ">>$logfile";
+}
+
 server_init;
 big_loop;


More information about the linux-fai-devel mailing list