Code

Apply patch for #6281 from LHM
[gosa.git] / trunk / gosa-si / gosa-si-client
index 82112fb7cd39a848e30388a6cc930941aeb049db..5ce0d368fc429a0d9661857f1952bbafb510f8e3 100755 (executable)
@@ -60,6 +60,7 @@ my @servers;
 my $gotoHardwareChecksum;
 my $gosa_si_client_fifo;
 my %files_to_watch;
+my $servers_string;
 $verbose= 1;
 
 # globalise variables which are used in imported events
@@ -73,9 +74,15 @@ our $client_mac_address;
 our $client_dnsname;
 our $client_force_hostname;
 our $server_key;
+our $opts_dnslookup;
+
+our $FIFO_FD = undef;
 
 # default variables
 our $REGISTERED = 0;
+our $REGISTRATION_IN_PROGRESS = 0;
+our $REGISTRATION_TRIES = 0;
+our $FAILED_CRYPTO = 0;
 
 # path to fifo for non-gosa-si-client messages to gosa-si-server
 $gosa_si_client_fifo = "/var/run/gosa-si-client.socket";
@@ -86,6 +93,10 @@ $gosa_si_client_fifo = "/var/run/gosa-si-client.socket";
 my $delay_set_time = 10;
 our $prg= basename($0);
 
+# Threshold for the max number of wrong crypted packages, when triggered
+# a new server is choosen
+my $max_failed_crypto_messages = 5;
+
 # all n seconds the client reports logged_in users to gosa-si-server
 my $trigger_logged_in_users_report_delay = 600;
 
@@ -114,11 +125,12 @@ my $fai_log_dir = "/var/log/fai";
      "force-hostname"    => [\$client_force_hostname, "false"],
     },
 "server" => {
-    "ip"          => [\$server_ip, "127.0.0.1"],
+    "ip"          => [\$servers_string, "127.0.0.1"],
     "port"         => [\$server_port, "20081"],
     "key"          => [\$server_key, ""],
     "timeout"      => [\$server_timeout, 10],
     "key-lifetime" => [\$server_key_lifetime, 600], 
+    "dns-lookup"           => [\$opts_dnslookup, "true"],
     },
 
 );
@@ -163,7 +175,6 @@ sub check_cmdline_param () {
     }
 }
 
-
 #===  FUNCTION  ================================================================
 #         NAME: check_pid
 #   PARAMETERS:
@@ -212,15 +223,43 @@ sub check_pid {
     }
 }
 
+sub clean_shutdown
+{
+    unlink($pid_file) if (-w $pid_file);
+    unlink($gosa_si_client_fifo) if (-S $gosa_si_client_fifo);
+}
 
-sub sig_int_handler {
+sub sig_int_or_term_handler
+{
     my ($signal) = @_;
+    daemon_log("Got SIG${signal} - shutting down gosa-si-client", 1);
+    clean_shutdown();
+    POE::Kernel->signal('client_session', 'KILL');
+    POE::Kernel->signal('gosa-si-client', 'KILL');
+    return 1;
+}
+
+sub sig_warn_handler
+{
+    my @loc = caller(0);
+    daemon_log( "SIGWARN line " . $loc[2] . ": " . $_[0], 1 );
+    return 1;
+}
        
-    daemon_log("shutting down gosa-si-client", 1);
-    system("kill `ps -C gosa-si-client -o pid=`");
+sub sig_die_handler
+{
+    my @loc = caller(0);
+    daemon_log( "SIGDIE line " . $loc[2] . ": " . $_[0], 1 );
+    clean_shutdown();
+    return 1;
 }
-$SIG{INT} = \&sig_int_handler;
 
+$SIG{'INT'} = \&sig_int_or_term_handler;
+$SIG{'TERM'} = \&sig_int_or_term_handler;
+$SIG{'__WARN__'} = \&sig_warn_handler;
+$SIG{'__DIE__'} = \&sig_die_handler;
+$SIG{'USR1'} = 'IGNORE';
+$SIG{'USR2'} = 'IGNORE';
 
 #===  FUNCTION  ================================================================
 #         NAME:  logging
@@ -317,7 +356,6 @@ sub get_mac {
 }
 
 
-
 #===  FUNCTION  ================================================================
 #         NAME:  get_local_mac_for_remote_ip
 #   PARAMETERS:  none (takes server_ip from global variable)
@@ -460,7 +498,7 @@ sub send_msg_to_target {
         if ($REGISTERED == 1) {
             $REGISTERED = 0;        # if server is not available, cause reregistering
             daemon_log("INFO: cause reregistering at gosa-si-server", 5); 
-            $global_kernel->yield('register_at_gosa_si_server');
+            $global_kernel->post('client_session', 'register_at_gosa_si_server');
 
         }
         $error++;
@@ -536,14 +574,15 @@ sub open_socket {
 #===============================================================================
 sub register_at_gosa_si_server {
   my ($kernel) = $_[KERNEL];
-  my $try_to_register = 0;
-  
+
        # if client is already registered, stop registration process    
        if ($REGISTERED) {
                $kernel->delay('register_at_gosa_si_server');
 
        # client is not registered, start registration process
        } else {
+        $REGISTRATION_IN_PROGRESS = 1;
+
                # clear all other triggered events and wait till registration was successful
        $kernel->delay('trigger_new_key');
 
@@ -552,21 +591,20 @@ sub register_at_gosa_si_server {
 
        my $events = join( ",", keys %{$event_hash} );
        while(1) {
-            $try_to_register++;
-
-                       # after one complete round through all server, stop trying to register           
-            if( $try_to_register > @servers )  { last; }
+            $REGISTRATION_TRIES++;
 
             # fetch first gosa-si-server from @servers
             # append shifted gosa-si-server at the end of @servers, so looking for servers never stop if
             # a registration never occured
             my $server = shift(@servers);
-            push( @servers, $server );
+            push(@servers, $server);
+            ($server_ip = $server) =~ s/:.*$//;
+            $server_address = $server;
 
             # Check if our ip is resolvable - if not: don't try to register
                        if(!(defined($server) && $server =~ m/^[0-9\.]*?:.*$/)) {
                                &main::daemon_log("ERROR: Server with address '".defined($server)?$server:""."' is invalid!", 1);
-                               if (length(@servers) == 1) {
+                               if (scalar(@servers) == 1) {
                                        &main::daemon_log("ERROR: No valid servers found!", 1);
                                        exit(1);
                                }
@@ -610,9 +648,9 @@ sub register_at_gosa_si_server {
                        my $res = &send_msg_hash_to_target($register_hash, $server, $default_server_key);
 
                        # if delivery of registration msg succeed
-                       if($res eq "0") {
+                       if(defined($res) and $res eq "0") {
                        # reset try_to_register
-                               $try_to_register = 0;
+                               $REGISTRATION_TRIES = 0;
 
                        # Set fixed client address and mac address
                                $client_ip= &get_local_ip_for_remote_ip(sprintf("%s", $server =~ /^([0-9\.]*?):.*$/));
@@ -622,15 +660,13 @@ sub register_at_gosa_si_server {
                 last;
 
                        # delivery of registration msg failed   
-                       } else {
-                       # wait 1 sec until trying to register again
-                               sleep(1);
-                               next;
+                       } elsif($REGISTRATION_TRIES >= scalar(@servers)) {
+                               last;
                        }
 
     } # end of while
        # one circle through all servers finished and no registration succeed
-       if ( $try_to_register >= @servers )  {
+       if ( $REGISTRATION_TRIES >= (scalar(@servers)) )  {
                        &write_to_file("gosa-si-no-server-available", $fai_logpath);
                        $kernel->delay_set('register_at_gosa_si_server', $delay_set_time);
        
@@ -788,8 +824,16 @@ sub trigger_logged_in_users_report {
                        @logged_in_user_list = split(/\s/, $result);
                }
                
-        system("echo 'CURRENTLY_LOGGED_IN ".join(" ", @logged_in_user_list)."' > /var/run/gosa-si-client.socket"); 
+       $FIFO_FD = undef
+           if (! defined $FIFO_FD && ! defined open($FIFO_FD, '>', $gosa_si_client_fifo));
+       if (! defined $FIFO_FD) {
+           daemon_log("ERROR: unable to open fifo for writing: $!", 1);
+           $kernel->delay_set('trigger_logged_in_users_report', 30);
+       }
+       else {
+           print($FIFO_FD 'CURRENTLY_LOGGED_IN ' . join(" ", @logged_in_user_list));
         $kernel->delay_set('trigger_logged_in_users_report', $trigger_logged_in_users_report_delay);
+       }
     } else {
         # try it in 10 sec again
         $kernel->delay_set('trigger_logged_in_users_report', 10);
@@ -847,9 +891,8 @@ sub trigger_seen_messages {
             my $send_error = &send_msg_to_target($confirm_msg, $server_address, $server_key);
 
             # Delete file
-            if (not $send_error) {
-                system("rm $goto_dir/$goto_file");
-            }
+            unlink("$goto_dir/$goto_file")
+                if (! $send_error);
         }
     }
 
@@ -898,7 +941,7 @@ sub fifo_got_record {
     &send_msg_to_target($clmsg, $server_address, $server_key, "CLMSG_$header");
 
     # if installation finished, save all log files 
-    if ($file_record eq "TASKBEGIN finish") {
+    if ($file_record eq "TASKBEGIN savelog") {
         &save_fai_log($fai_log_dir); 
     }
 
@@ -909,6 +952,7 @@ sub fifo_got_record {
 sub save_fai_log {
     my ($fai_log_dir) = @_ ;
     my $FAI_DIR;
+    my $fai_action;
 
        # Directory for log files after a softupdate
     my $log_dir = File::Spec->catdir($fai_log_dir, "localhost/last");
@@ -920,6 +964,11 @@ sub save_fai_log {
                                daemon_log("ERROR: cannot open directory $log_dir", 1);
                                return; 
                }
+        $fai_action = "install";
+    }
+    else {
+        # If we already have a logdir, we can assume that this is a softupdate
+        $fai_action = "softupdate";
     }
 
     opendir($FAI_DIR, "$log_dir");
@@ -949,27 +998,18 @@ sub save_fai_log {
     my $all_log_string = join("\n", @log_list); 
     my $msg_hash = &create_xml_hash("CLMSG_save_fai_log", $client_address, $server_address, $all_log_string);
     &add_content2xml_hash($msg_hash, "macaddress", $client_mac_address);
+    &add_content2xml_hash($msg_hash, "fai_action", $fai_action);
     my $msg = &create_xml_string($msg_hash);
     &send_msg_to_target($msg, $server_address, $server_key, "CLMSG_save_fai_log");
 
 }
 
 
-sub sig_handler {
-       my ($kernel, $signal) = @_[KERNEL, ARG0] ;
-       daemon_log("0 INFO got signal '$signal'", 1); 
-       $kernel->sig_handled();
-       return;
-}
-
-
 sub _start {
     my ($kernel, $heap) = @_[KERNEL, HEAP];
     $kernel->alias_set('client_session');
     $global_kernel = $kernel;       # this is used to throw events at each point of the skript
  
-    $kernel->sig(USR1 => "sig_handler");
-
     # force a registration at a gosa-si-server
     $kernel->yield('register_at_gosa_si_server');
     
@@ -993,6 +1033,17 @@ sub _start {
 }
 
 
+sub _stop {
+    my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
+    delete $heap->{'services'};
+    delete $heap->{'watchers'};
+    $kernel->alias_remove($heap->{alias});
+    $kernel->alarm_remove_all();
+    $kernel->post($heap->{child_session}, '_stop');
+    close( $FIFO_FD ) if( defined $FIFO_FD );
+}
+
+
 sub _default {
     daemon_log("ERROR: can not handle incoming msg with header '$_[ARG0]'", 1);
     return;
@@ -1019,9 +1070,17 @@ sub server_input {
 
                        # if client is alread in a registration process, that means not registered, do nothing
                        # if not, cause re-registration
-                       if (not $REGISTERED) {
+                       if ($REGISTRATION_IN_PROGRESS) {
                                &daemon_log("WARNING: gosa-si-client is already in a registration process so ignore this message", 3);
                        } else {
+                if ($FAILED_CRYPTO > $max_failed_crypto_messages) {
+                    # Force usage of a new server
+                    daemon_log("Failed to decrypt a total of $max_failed_crypto_messages messages from the server. Trying to switch to another one", 3);
+                    $REGISTRATION_TRIES = $max_failed_crypto_messages;
+                    my $server = shift(@servers);
+                    push(@servers, $server);
+                }
+                $FAILED_CRYPTO++;
                                $REGISTERED = 0;
                                $kernel->post('client_session', 'register_at_gosa_si_server');
                        }
@@ -1033,6 +1092,9 @@ sub server_input {
     ######################
     # process incoming msg
     if( $error == 0 ) {
+        # Reset failed crypto messages state
+        $FAILED_CRYPTO = 0;
+
         my $header = @{$msg_hash->{header}}[0];
         my $source = @{$msg_hash->{source}}[0];
 
@@ -1062,6 +1124,7 @@ sub server_input {
             if( $answer =~ "<header>registered</header>") {
                 # set registered flag to true to stop sending further registered msgs
                 $REGISTERED = 1;
+                $REGISTRATION_IN_PROGRESS = 0;
             } 
             else {
                 $answer =~ /<header>(\S+)<\/header>/;
@@ -1082,6 +1145,91 @@ sub server_input {
     return;
 }
 
+sub find_servers {
+    # add gosa-si-server address from config file at first position of server list
+    my $server_check_cfg = Config::IniFiles->new( -file => $cfg_file );
+
+    # Parse servers string
+    my @conf_servers = split(',', $servers_string);
+
+    # Now search for fallback servers in the configuration
+    foreach my $cur_server (@conf_servers) {
+        # Remove spaces from the IP
+        $cur_server =~ s/\s//g;
+
+        my $ip = $cur_server;
+        if(not $cur_server =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
+            my $ip_address = inet_ntoa(scalar gethostbyname($ip));
+            if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
+                # Write ip address to $server_ip variable
+                $ip = $ip_address;
+            }
+        }
+        
+        my $server_addr = sprintf("%s:%s", $ip, $server_port);
+        if (not grep { $_ eq $server_addr } @servers) {
+           push(@servers, $server_addr);
+        }
+    }
+
+    my $servers_string = join(", ", @servers);
+    daemon_log("INFO: found servers in configuration file: $servers_string", 1);
+
+    # Last but not least search for fallback servers in the DNS
+    if (defined($opts_dnslookup) and $opts_dnslookup eq "true") {
+        my @tmp_servers;
+        if ( !$server_domain) {
+            # Try our DNS Searchlist
+            my @domain_list = &get_dns_domains();
+            my $tmp_domains;
+            my $error_string;
+            for my $domain (@domain_list) {
+                chomp($domain);
+                ($tmp_domains, $error_string) = &get_server_addresses($domain);
+                if(@$tmp_domains) {
+                    for my $tmp_server(@$tmp_domains) {
+                        push @tmp_servers, $tmp_server;
+                    }
+                }
+            }
+
+            if (0 == @tmp_servers) {
+                daemon_log("INFO: No servers found in DNS.", 1);
+            }
+            else {
+                my $servers_string = join(", ", @tmp_servers);
+                daemon_log("INFO: found servers in DNS: $servers_string", 1);
+            }
+        } else {
+            @tmp_servers = &get_server_addresses($server_domain);
+            if( 0 == @tmp_servers ) {
+            daemon_log("INFO: No servers found in DNS for domain '$server_domain'",1);
+            } 
+        }
+
+        if ( 0 != @tmp_servers ) {
+            foreach my $server_addr (@tmp_servers) {
+                if (not grep { $_ eq $server_addr } @servers) {
+                   push(@servers, $server_addr);
+                }
+            }
+        }
+    }
+   
+    if (0 == scalar(@servers)) {
+        daemon_log("ERROR: No servers found in the configuration or DNS.", 1);
+        exit(1);
+    }
+
+    # Define first server as server_ip
+    $server_ip = $servers[0];
+
+    # prepare variables
+    if( inet_aton($server_ip) ){ $server_ip = inet_ntoa(inet_aton($server_ip)); }
+    if (defined $server_ip && defined $server_port) {
+        $server_address = $server_ip.":".$server_port;
+    }
+}
 
 #==== MAIN = main ==============================================================
 #  parse commandline options
@@ -1097,34 +1245,6 @@ GetOptions("h|help" => \&usage,
 &read_configfile($cfg_file, %cfg_defaults);
 &check_pid;
 
-
-# forward error messages to logfile
-if ( ! $foreground ) {
-  open( STDIN,  '+>/dev/null' );
-  open( STDOUT, '+>&STDIN'    );
-  open( STDERR, '+>&STDIN'    );
-}
-
-# Just fork, if we are not in foreground mode
-if( ! $foreground ) { 
-    chdir '/'                 or die "Can't chdir to /: $!";
-    $pid = fork;
-    setsid                    or die "Can't start a new session: $!";
-    umask 0;
-} else { 
-    $pid = $$; 
-}
-
-# Do something useful - put our PID into the pid_file
-if( 0 != $pid ) {
-    open( LOCK_FILE, ">$pid_file" );
-    print LOCK_FILE "$pid\n";
-    close( LOCK_FILE );
-    if( !$foreground ) { 
-        exit( 0 ) 
-    };
-}
-
 # parse head url and revision from svn
 my $client_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
 $client_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
@@ -1153,6 +1273,52 @@ daemon_log("INFO: ".$client_status_hash->{$client_status}.": $client_revision",
 # delete old DBsqlite lock files
 system('rm -f /tmp/gosa_si_lock*gosa-si-client*');
 
+# (re-)create FIFO
+if (-e $gosa_si_client_fifo) {
+    daemon_log("INFO: $gosa_si_client_fifo exists - deleting", 5);
+    if (1 != unlink($gosa_si_client_fifo)) {
+        daemon_log("ERROR: unable to delete '$gosa_si_client_fifo': $!", 1);
+        exit( 1 );
+    }
+}
+if (! defined POSIX::mkfifo($gosa_si_client_fifo, "0600")) {
+    daemon_log("ERROR: failed creating fifo: $!", 1);
+    exit( 1 );
+}
+
+# Just fork, if we are not in foreground mode
+if( ! $foreground ) {
+    if (! chdir('/')) {
+        daemon_log("ERROR: Can't chdir to /: $!");
+        exit( 1 );
+    }
+    umask( 0 );
+    $pid = fork;
+} else { 
+    $pid = $$; 
+}
+
+if( 0 != $pid ) {
+    # Parent: put our PID into the $pid_file
+    open( LOCK_FILE, ">$pid_file" );
+    print LOCK_FILE "$pid\n";
+    close( LOCK_FILE );
+    if( !$foreground ) {
+        exit( 0 );
+    }
+}
+else {
+    # Child
+    open( STDIN,  '+>/dev/null' );
+    open( STDOUT, '+>&STDIN'    );
+    open( STDERR, '+>&STDIN'    );
+    if (! POSIX::setsid()) {
+        deamon_log("ERROR: Can't start a new session: $!", 1);
+        exit( 1 );
+    }
+    $poe_kernel->has_forked() if ($poe_kernel->can('has_forked'));
+}
+
 # detect ip and mac address and complete host address
 $client_address = $client_ip.":".$client_port;
 my $network_interface= &get_interface_for_ip($client_ip);
@@ -1191,74 +1357,17 @@ POE::Component::Server::TCP->new(
 );
 daemon_log("INFO: start socket for incoming xml messages at port '$client_port' ", 1);
 
-
-# prepare variables
-if( inet_aton($server_ip) ){ $server_ip = inet_ntoa(inet_aton($server_ip)); }
-if (defined $server_ip && defined $server_port) {
-    $server_address = $server_ip.":".$server_port;
-}
 $xml = new XML::Simple();
 $default_server_key = $server_key;
 
-
-# add gosa-si-server address from config file at first position of server list
-my $server_check_cfg = Config::IniFiles->new( -file => $cfg_file );
-my $server_check = (defined($server_check_cfg))?$server_check_cfg->val( "server", "ip"):undef;
-if( defined $server_check ) {
-       unshift(@servers, $server_address);
-       my $servers_string = join(", ", @servers);
-       daemon_log("INFO: found servers in configuration file: $servers_string", 1);
-} else {
-       my @tmp_servers;
-       if ( !$server_domain) {
-               # Try our DNS Searchlist
-               my @domain_list = &get_dns_domains();
-               my $tmp_domains;
-               my $error_string;
-               for my $domain (@domain_list) {
-                       chomp($domain);
-                       ($tmp_domains, $error_string) = &get_server_addresses($domain);
-                       if(@$tmp_domains) {
-                               for my $tmp_server(@$tmp_domains) {
-                                       push @tmp_servers, $tmp_server;
-                               }
-                       }
-               }
-               if (0 == @tmp_servers) {
-                       my $log_string = "no gosa-si-server found!";
-                       $log_string .= "\n\tdetermined domains out of /etc/resolv.conf: ".join(", ", @domain_list) if (@domain_list);
-                       $log_string .= "\n\tdetermined server addresses in domains: ".join(", ",@$tmp_domains) if (defined($tmp_domains));
-                       daemon_log("ERROR: $log_string", 1) if (defined($log_string));
-                       daemon_log("ERROR: $error_string", 1) if (defined($error_string));
-                       daemon_log("ERROR: please specify a gosa-si-server address or a domain in config file", 1);
-                       kill 2, $$;
-               }
-       } else {
-               @tmp_servers = &get_server_addresses($server_domain);
-               if( 0 == @tmp_servers ) {
-                       daemon_log("ERROR: no gosa-si-server found in DNS for domain '$server_domain'",1);
-                       daemon_log("ERROR: please specify a gosa-si-server address or a domain in config file", 1);
-                       kill 2, $$;
-               } 
-       }
-
-       foreach my $server (@tmp_servers) { 
-               unshift(@servers, $server); 
-       }
-       my $servers_string = join(", ", @servers);
-       daemon_log("INFO: found servers in DNS: $servers_string", 1);
-}
-
-
-# open fifo for non-gosa-si-client-msgs to gosa-si-server
-POSIX::mkfifo("$gosa_si_client_fifo", "0600");
-
+# Find servers from config and DNS
+&find_servers;
 
 POE::Session->create(
        inline_states => {
                _start => \&_start, 
+        _stop => \&_stop,
         _default => \&_default,
-        sig_handler => \&sig_handler,
         register_at_gosa_si_server => \&register_at_gosa_si_server,
 
         # trigger periodical tasks