New features, and bugfix for faimond

Jonas Eriksson zqad at hpc2n.umu.se
Wed Aug 29 10:17:16 CEST 2007


On Tue, Aug 28, 2007 at 09:31:47PM +0200 Thomas Lange wrote:
> >>>>> On Tue, 28 Aug 2007 17:53:35 +0200, Jonas Eriksson <zqad at hpc2n.umu.se> said:
> 
> OK, I'm wondering, because the command die normally kill the whole
> script. I did not test it but from the code it should die IMO.

The model for this is not always that obvious, but it's a short
and simple way to solve the problem. 

http://www.sunsite.ualberta.ca/Documentation/Misc/perl-5.6.1/pod/perlfunc/eval.html
has a quite interesting write-up on the subject if you are
intresed in more detailed information.
 
>     > So, how should i design the patch? Should the standard behaviour
>     > of faimond be to not fork and not print timestamps?
> Yep. 
> 
> Ah now I understand. It seems that the Proc::Daemon creates a new
> process for every connection. Then the die command will not kill the
> daemon but IMO it a little bit overhead. But anyhow, if you like to
> use Proc::Daemon then do it.

No, Proc::Daemon just forks twice, closes all file descripors
and all things that is supposed to happen when daemonizing.

> The only thing I need for faimond-gui is that faimond prints the
> messages from all install clients without timestamps to stdout. If this
> is possible (or the default) I'm happy with your faimond version. 
> Please check the indentation of the code. Sometimes you are using 8
> spaces instead of two. 

Fixed in included patch. Had some problems with vim settings, but
it should all be ok now.

> Please tell them to fill out the FAI questionnaire.

I will ask them to do that, and be sure to do it myself. Actually
I've missed it totally, but it's a good idea and will honour it.

>     > in this case should start an own instance of faimond and read
> AFAIK it's not possible that tow process are listening on the same
> port.

Well, not at the same time. I was probably just thinking our a
bit too loud, and will not interfere with the faimond-gui
development.

> And please update the man page for faimond (send me a patch for this).

Ah, the documentation. All to easy to forget. This diff includes
the faimond man page as well.

The updates to faimond required some changes in the
init.d-script, so I'm including an updated version of that as
well.

I have continued to work on some security measures, and added a
-i option to send the ip address of the connecting host instead
of the reported hostname to fai-chboot. This does not affect the
default behaviour, but makes other hosts unable to change status
for other hosts then itself, granted that you use the same ip
when fai:ing as live and supply faimond with -i.

Also, I discovered a serious security hole in the faimond when
using -b:
1. Start "faimond -b"
2. Telnet to the computer running faimond and type:
   ";echo TASKEND install 0", without " and press enter.
3. Note the extra line that the "faimond -b"-terminal outputs.
   Imagine what would happen if you replace "echo" with "reboot"
	 (Don't try this part on a production server).

I've solved this in my latest faimond-patch by instead sending a
list to the system()-call. If we pass a list to system() it will
just use the list as an argument vector.

Given this, I recommend a security update as soon as possible.

Best regards,
Jonas

-------------------------------------
- Jonas Eriksson, zqad at hpc2n.umu.se -
- sysadmin @ hpc2n.umu.se           -
-------------------------------------
-------------- next part --------------
Index: bin/faimond
===================================================================
--- bin/faimond	(revision 4532)
+++ bin/faimond	(working copy)
@@ -1,4 +1,5 @@
 #!/usr/bin/perl -w
+# vim:et:ts=2:sw=2:
 
 # $Id$
 #*********************************************************************
@@ -14,38 +15,126 @@
 use strict;
 use Socket;
 use Getopt::Std;
+use Proc::Daemon;
 
 $| = 1;
-my $port;
-our ($opt_b,$opt_h,$opt_p);
+my ($port, $timeout, $daemon, $timestamp);
+my $pidfile = '/var/run/faimond.pid';
+my $logfile = '-';
+my $daemonlogfile = '/var/log/faimond.log';
+my $useip;
+
+our ($opt_b,$opt_h,$opt_p,$opt_l,$opt_t,$opt_d,$opt_P,$opt_T,$opt_i);
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+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_deadly(@) {
+  # Use the die-handler
+  signal_die('Caught deadly signal ' . shift() . "\n");
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 sub server_init() {
+  logline("FAI monitoring daemon starting..\n") or die "log: $!";
 
+  # Init signals
+  $SIG{INT} = \&signal_deadly;
+  $SIG{QUIT} = \&signal_deadly;
+  $SIG{TERM} = \&signal_deadly;
+  $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';
+
+  if ($daemon) {
+    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);
+    }
+    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";
+      if ($useip) {
+        $cname = $ip;
+      }
+      system('fai-chboot', '-d', $cname);
+      logline("$ip:$port: Disabling pxelinux configuration for $cname\n") or die "log: $!";
+    }
+    logline("$ip:$port: $inp") or die "log: $!";
   }
 }
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -54,21 +143,56 @@
   print << "EOF";
 faimond, FAI monitor daemon.
 
-   Copyright (C) 2003-2007 by Thomas Lange
+    Copyright (C) 2003-2007 by Thomas Lange
 
-Usage: faimond [OPTION]
+Usage: faimond [OPTIONS]
 
-   -b                   Call fai-chboot to change boot parameter.
-   -p PORT              Set port to listen to. Default is 4711.
+    -b                  Call fai-chboot to change boot parameter.
+    -p PORT             Set port to listen to. Default is 4711.
+    -l FILE             Logfile. Default is standard out and
+                        '$daemonlogfile' in daemon mode.
+    -t TIMEOUT          Timeout for bad clients. 0 to disable.
+    -d                  Daemon mode.
+    -P FILE             PID-file. Default is '$pidfile'.
+                        Used only if starting in daemon mode.
+    -T                  Print timestamps in the log.
+    -i                  When using -b: send IP of client to fai-choot
+                        instead of the hostname the host reports.
 
 EOF
   exit 0;
 }
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
-getopts('bhp:') || usage;
+getopts('bhTp:l:t:dP:i') || usage;
 $opt_h && usage;
 $port = $opt_p || 4711;
+$timeout = $opt_t || 5;
+$daemon = $opt_d || 0;
+$timestamp = $opt_T || 0;
+$useip = $opt_i || 0;
 
+if (defined($opt_P)) {
+  $pidfile = $opt_P;
+}
+
+if (defined($opt_d)) {
+  # If in daemon mode, use standard daemon log file
+  $logfile = $daemonlogfile;
+}
+
+if (defined($opt_l)) {
+  $logfile = $opt_l;
+}
+
+# Constuct a $logfile that open can take as an argument
+if ($logfile eq '-') {
+  $logfile = ">&STDOUT";
+}
+else {
+  $logfile = ">>$logfile";
+}
+
+
 server_init;
 big_loop;
Index: man/faimond.8
===================================================================
--- man/faimond.8	(revision 4532)
+++ man/faimond.8	(working copy)
@@ -17,6 +17,28 @@
 .B "-b "
 Call fai-chboot for the install client if it task install was finished
 properly. Default is not to call fai-chboot.
+.TP
+.B "-i"
+Use IP address of connecting host when calling fai-chboot. This can only be used
+if the hosts has the same IP when installning as when running.
+.TP
+.B "-l FILE"
+Logfile. Default is standard out in non-daemon mode and "/var/log/faimond.log"
+in daemon mode.
+.TP
+.B "-t TIMEOUT"
+Timeout for bad clients. 0 to disable.
+.TP
+.B "-d"
+Daemon mode.
+.TP
+.B "-P FILE"
+PID-file. Default is "/var/run/faimond.pid". Used only if starting in daemon
+mode.
+.TP
+.B "-T"
+Print timestamps in the log.
+.TP
 .SH NOTES
 Normally, the output will be piped to a GUI interface which displays
 all information.
-------------- 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 -d on installed clients
#   Add "-T" to add timestamps to the log
#   Add "-i" to use the connectors ip adress instead of reported host
#   name.
#   -d is required for daemon mode
FAIMOND="-d -T"

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


More information about the linux-fai-devel mailing list