#!/usr/bin/perl #=============================================================================== # # FILE: gosa-si-bus # # USAGE: ./gosa-si-bus # # DESCRIPTION: # # OPTIONS: --- # REQUIREMENTS: --- # BUGS: --- # NOTES: # AUTHOR: (Andreas Rettenberger), # COMPANY: # VERSION: 1.0 # CREATED: 12.09.2007 08:54:41 CEST # REVISION: --- #=============================================================================== use strict; use warnings; use Getopt::Long; use Config::IniFiles; use POSIX; use Time::HiRes qw( gettimeofday ); use POE qw(Component::Server::TCP); use Data::Dumper; use Crypt::Rijndael; use IO::Socket::INET; use NetAddr::IP; use XML::Simple; use MIME::Base64; use File::Basename; use Digest::MD5 qw(md5 md5_hex md5_base64); use utf8; use GOSA::GosaSupportDaemon; use GOSA::DBsqlite; my ($cfg_file, $default_cfg_file, %cfg_defaults, $foreground, $verbose, $pid_file, $procid, $pid, $log_file,); my ($bus_address, $bus_key, $bus_ip, $bus_port, $bus_mac_address); my ($bus_known_server_db, $bus_known_server_file_name, $bus_known_clients_db, $bus_known_clients_file_name); my $xml; our $prg= basename($0); $foreground = 0 ; %cfg_defaults = ( "general" => { "log_file" => [\$log_file, "/var/run/".$prg.".log"], "pid_file" => [\$pid_file, "/var/run/".$prg.".pid"], }, "bus" => { "key" => [\$bus_key, "secret-bus-password"], "ip" => [\$bus_ip, "0.0.0.0"], "port" => [\$bus_port, "20080"], "known-servers" => [\$bus_known_server_file_name, "/var/lib/gosa-si/bus-servers.db"], "known-clients" => [\$bus_known_clients_file_name, "/var/lib/gosa-si/bus-clients.db"], }, ); #=== FUNCTIONS = functions ===================================================== #=== FUNCTION ================================================================ # NAME: check_cmdline_param # PARAMETERS: # RETURNS: # DESCRIPTION: #=============================================================================== sub check_cmdline_param () { my @error_l; my $error = 0; if( !$cfg_file ) { $cfg_file = "/etc/gosa-si/bus.conf"; } if( not -f $cfg_file ) { push(@error_l, "can not find file '$cfg_file'"); $error++; } if( not -r $cfg_file) { push(@error_l, "can not read file '$cfg_file'"); $error++; } if( $error > 0 ) { &usage( "", 1 ); print STDERR join("\n", @error_l); print STDERR "\n"; exit( -1 ); } } #=== FUNCTION ================================================================ # NAME: read_configfile # PARAMETERS: cfg_file - string - # RETURNS: nothing # DESCRIPTION: read cfg_file and set variables #=============================================================================== sub read_configfile { my $cfg; if( defined( $cfg_file) && ( length($cfg_file) > 0 )) { if( -r $cfg_file ) { $cfg = Config::IniFiles->new( -file => $cfg_file ); } else { print STDERR "Couldn't read config file!"; } } else { $cfg = Config::IniFiles->new() ; } foreach my $section (keys %cfg_defaults) { foreach my $param (keys %{$cfg_defaults{ $section }}) { my $pinfo = $cfg_defaults{ $section }{ $param }; ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] ); } } } #=== FUNCTION ================================================================ # NAME: check_pid # PARAMETERS: nothing # RETURNS: nothing # DESCRIPTION: handels pid processing #=============================================================================== sub check_pid { $pid = -1; # Check, if we are already running if( open(LOCK_FILE, "<$pid_file") ) { $pid = ; if( defined $pid ) { chomp( $pid ); if( -f "/proc/$pid/stat" ) { my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/; if( $0 eq $stat ) { close( LOCK_FILE ); exit -1; } } } close( LOCK_FILE ); unlink( $pid_file ); } # create a syslog msg if it is not to possible to open PID file if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) { my($msg) = "Couldn't obtain lockfile '$pid_file' "; if (open(LOCK_FILE, '<', $pid_file) && ($pid = )) { chomp($pid); $msg .= "(PID $pid)\n"; } else { $msg .= "(unable to read PID)\n"; } if( ! ($foreground) ) { openlog( $0, "cons,pid", "daemon" ); syslog( "warning", $msg ); closelog(); } else { print( STDERR " $msg " ); } exit( -1 ); } } #=== FUNCTION ================================================================ # NAME: usage # PARAMETERS: nothing # RETURNS: nothing # DESCRIPTION: print out usage text to STDERR #=============================================================================== sub usage { print STDERR << "EOF" ; usage: $prg [-hvf] [-c config] -h : this (help) message -c : config file -f : foreground, process will not be forked to background -v : be verbose (multiple to increase verbosity) EOF print "\n" ; } #=== FUNCTION ================================================================ # NAME: logging # PARAMETERS: level - string - default 'info' # msg - string - # facility - string - default 'LOG_DAEMON' # RETURNS: # DESCRIPTION: #=============================================================================== sub daemon_log { # log into log_file my( $msg, $level ) = @_; if(not defined $msg) { return } if(not defined $level) { $level = 1 } if(defined $log_file){ open(LOG_HANDLE, ">>$log_file"); if(not defined open( LOG_HANDLE, ">>$log_file" )) { print STDERR "cannot open $log_file: $!"; return } chomp($msg); if($level <= $verbose){ my ($seconds, $minutes, $hours, $monthday, $month, $year, $weekday, $yearday, $sommertime) = localtime(time); $hours = $hours < 10 ? $hours = "0".$hours : $hours; $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes; $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds; my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); $month = $monthnames[$month]; $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday; $year+=1900; my $log_msg = "$month $monthday $hours:$minutes:$seconds $prg $msg\n"; print LOG_HANDLE $log_msg; if( $foreground ) { print STDERR $log_msg; } } close( LOG_HANDLE ); } } #=== FUNCTION ================================================================ # NAME: get_ip # PARAMETERS: interface name (i.e. eth0) # RETURNS: (ip address) # DESCRIPTION: Uses ioctl to get ip address directly from system. #=============================================================================== sub get_ip { my $ifreq= shift; my $result= ""; my $SIOCGIFADDR= 0x8915; # man 2 ioctl_list my $proto= getprotobyname('ip'); socket SOCKET, PF_INET, SOCK_DGRAM, $proto or die "socket: $!"; if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) { my ($if, $sin) = unpack 'a16 a16', $ifreq; my ($port, $addr) = sockaddr_in $sin; my $ip = inet_ntoa $addr; if ($ip && length($ip) > 0) { $result = $ip; } } return $result; } #=== FUNCTION ================================================================ # NAME: get_interface_for_ip # PARAMETERS: ip address (i.e. 192.168.0.1) # RETURNS: array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else # DESCRIPTION: Uses proc fs (/proc/net/dev) to get list of interfaces. #=============================================================================== sub get_interface_for_ip { my $result; my $ip= shift; if ($ip && length($ip) > 0) { my @ifs= &get_interfaces(); if($ip eq "0.0.0.0") { $result = "all"; } else { foreach (@ifs) { my $if=$_; if(get_ip($if) eq $ip) { $result = $if; last; } } } } return $result; } #=== FUNCTION ================================================================ # NAME: get_interfaces # PARAMETERS: none # RETURNS: (list of interfaces) # DESCRIPTION: Uses proc fs (/proc/net/dev) to get list of interfaces. #=============================================================================== sub get_interfaces { my @result; my $PROC_NET_DEV= ('/proc/net/dev'); open(PROC_NET_DEV, "<$PROC_NET_DEV") or die "Could not open $PROC_NET_DEV"; my @ifs = ; close(PROC_NET_DEV); # Eat first two line shift @ifs; shift @ifs; chomp @ifs; foreach my $line(@ifs) { my $if= (split /:/, $line)[0]; $if =~ s/^\s+//; push @result, $if; } return @result; } #=== FUNCTION ================================================================ # NAME: get_mac # PARAMETERS: interface name (i.e. eth0) # RETURNS: (mac address) # DESCRIPTION: Uses ioctl to get mac address directly from system. #=============================================================================== sub get_mac { my $ifreq= shift; my $result; if ($ifreq && length($ifreq) > 0) { if($ifreq eq "all") { if(defined($bus_ip)) { $result = &get_local_mac_for_remote_ip($bus_ip); } elsif ($bus_mac_address && length($bus_mac_address) > 0 && !($bus_mac_address eq "00:00:00:00:00:00")){ $result = &client_mac_address; } else { $result = "00:00:00:00:00:00"; } } else { my $SIOCGIFHWADDR= 0x8927; # man 2 ioctl_list # A configured MAC Address should always override a guessed value if ($bus_mac_address and length($bus_mac_address) > 0 and not($bus_mac_address eq "00:00:00:00:00:00")) { $result= $bus_mac_address; } else { socket SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('ip') or die "socket: $!"; if(ioctl SOCKET, $SIOCGIFHWADDR, $ifreq) { my ($if, $mac)= unpack 'h36 H12', $ifreq; if (length($mac) > 0) { $mac=~ m/^([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$/; $mac= sprintf("%s:%s:%s:%s:%s:%s", $1, $2, $3, $4, $5, $6); $result = $mac; } } } } } return $result; } #=== FUNCTION ================================================================ # NAME: get_local_mac_for_remote_ip # PARAMETERS: none (takes server_ip from global variable) # RETURNS: (ip address from interface that is used for communication) # DESCRIPTION: Uses ioctl to get routing table from system, checks which entry # matches (defaultroute last). #=============================================================================== sub get_local_mac_for_remote_ip { my $server_ip= shift; my $result= "00:00:00:00:00:00"; if($server_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) { my $PROC_NET_ROUTE= ('/proc/net/route'); open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE") or die "Could not open $PROC_NET_ROUTE"; my @ifs = ; close(PROC_NET_ROUTE); # Eat header line shift @ifs; chomp @ifs; foreach my $line(@ifs) { my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line); my $destination; my $mask; my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination); $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d)); ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask); $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d)); if(new NetAddr::IP($server_ip)->within(new NetAddr::IP($destination, $mask))) { # destination matches route, save mac and exit $result= &get_mac($Iface); last; } } } else { daemon_log("get_local_mac_for_remote_ip was called with a non-ip parameter: $server_ip", 1); } return $result; } sub bus_matches { my $target = shift; my $target_ip = sprintf("%s", $target =~ /^([0-9\.]*?):.*$/); my $result = 0; if($bus_ip eq $target_ip) { $result= 1; } elsif ($bus_ip eq "0.0.0.0") { if ($target_ip eq "127.0.0.1") { $result= 1; } else { my $PROC_NET_ROUTE= ('/proc/net/route'); open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE") or die "Could not open $PROC_NET_ROUTE"; my @ifs = ; close(PROC_NET_ROUTE); # Eat header line shift @ifs; chomp @ifs; foreach my $line(@ifs) { my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line); my $destination; my $mask; my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination); $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d)); ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask); $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d)); if(new NetAddr::IP($target_ip)->within(new NetAddr::IP($destination, $mask))) { # destination matches route, save mac and exit $result= 1; last; } } } } else { &main::daemon_log("Target ip $target_ip does not match bus ip $bus_ip",1); } return $result; } #=== FUNCTION ================================================================ # NAME: create_passwd # PARAMETERS: nothing # RETURNS: new_passwd - string # DESCRIPTION: creates a 32 bit long random passwd out of "a".."z","A".."Z",0..9 #=============================================================================== sub create_passwd { my $new_passwd = ""; for(my $i=0; $i<31; $i++) { $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))] } return $new_passwd; } sub create_ciphering { my ($passwd) = @_; if((!defined($passwd)) || length($passwd)==0) { $passwd = ""; } $passwd = substr(md5_hex("$passwd") x 32, 0, 32); my $iv = substr(md5_hex('GONICUS GmbH'),0, 16); my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC()); $my_cipher->set_iv($iv); return $my_cipher; } sub encrypt_msg { my ($msg, $key) = @_; my $my_cipher = &create_ciphering($key); my $len; { use bytes; $len= 16-length($msg)%16; } $msg = "\0"x($len).$msg; $msg = $my_cipher->encrypt($msg); chomp($msg = &encode_base64($msg)); # there are no newlines allowed inside msg $msg=~ s/\n//g; return $msg; } sub decrypt_msg { my ($msg, $key) = @_ ; $msg = &decode_base64($msg); my $my_cipher = &create_ciphering($key); $msg = $my_cipher->decrypt($msg); $msg =~ s/\0*//g; return $msg; } sub send_msg_hash2address { my ($msg_hash, $address, $encrypt_key) = @_ ; my $msg = &create_xml_string($msg_hash); my $header = @{$msg_hash->{'header'}}[0]; &send_msg_to_target($msg, $address, $encrypt_key, $header); return; } sub send_msg_to_target { my ($msg, $address, $encrypt_key, $msg_header) = @_ ; my $error = 0; my $header; my $new_status; my $act_status; my ($sql_statement, $res); if( $msg_header ) { $header = "'$msg_header'-"; } else { $header = ""; } # encrypt xml msg my $crypted_msg = &encrypt_msg($msg, $encrypt_key); # opensocket my $socket = &open_socket($address); if( !$socket ) { daemon_log("cannot send ".$header."msg to $address , host not reachable", 1); $error++; } if( $error == 0 ) { # send xml msg print $socket $crypted_msg."\n"; daemon_log("send ".$header."msg to $address", 1); daemon_log("message:\n$msg", 8); } # close socket in any case if( $socket ) { close $socket; } if( $error > 0 ) { $new_status = "down"; } else { $new_status = $msg_header; } # known_clients $sql_statement = "SELECT * FROM bus_known_clients WHERE hostname='$address'"; $res = $bus_known_clients_db->select_dbentry($sql_statement); if( keys(%$res) > 0 ) { $act_status = $res->{1}->{'status'}; if( $act_status eq "down" ) { $sql_statement = "DELETE FROM bus_known_clients WHERE hostname='$address'"; $res = $bus_known_clients_db->del_dbentry($sql_statement); daemon_log("WARNING: failed 2x to send msg to host '$address', delete host from bus_known_clients", 3); } else { $sql_statement = "UPDATE bus_known_clients SET status='$new_status' WHERE hostname='$address'"; $res = $bus_known_clients_db->update_dbentry($sql_statement); daemon_log("INFO: set '$address' from status '$act_status' to '$new_status'", 5); } } # known_server $sql_statement = "SELECT * FROM bus_known_server WHERE hostname='$address'"; $res = $bus_known_server_db->select_dbentry($sql_statement); if( keys(%$res) > 0) { $act_status = $res->{1}->{'status'}; if( $act_status eq "down" ) { $sql_statement = "DELETE FROM bus_known_clients WHERE hostname='$address'"; $res = $bus_known_clients_db->del_dbentry($sql_statement); daemon_log("WARNING: failed 2x to a send msg to host '$address', delete host from bus_known_server", 3); } else { $sql_statement = "UPDATE bus_known_server SET status='$new_status' WHERE hostname='$address'"; $res = $bus_known_server_db->update_dbentry($sql_statement); daemon_log("INFO: set '$address' from status '$act_status' to '$new_status'", 5) } } return; } #=== FUNCTION ================================================================ # NAME: open_socket # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000 # [PeerPort] string necessary if port not appended by PeerAddr # RETURNS: socket IO::Socket::INET # DESCRIPTION: open a socket to PeerAddr #=============================================================================== sub open_socket { my ($PeerAddr, $PeerPort) = @_ ; if(defined($PeerPort)){ $PeerAddr = $PeerAddr.":".$PeerPort; } my $socket; $socket = new IO::Socket::INET(PeerAddr => $PeerAddr, Porto => "tcp", Type => SOCK_STREAM, Timeout => 5, ); if(not defined $socket) { return; } &daemon_log("open_socket: $PeerAddr", 7); return $socket; } sub check_key_and_xml_validity { my ($crypted_msg, $module_key) = @_; #print STDERR "crypted_msg:$crypted_msg\n"; #print STDERR "modul_key:$module_key\n"; my $msg; my $msg_hash; eval{ $msg = &decrypt_msg($crypted_msg, $module_key); &main::daemon_log("decrypted_msg: \n$msg", 8); $msg_hash = $xml->XMLin($msg, ForceArray=>1); # check header my $header_l = $msg_hash->{'header'}; if( 1 != @{$header_l} ) { die 'no or more headers specified'; } my $header = @{$header_l}[0]; if( 0 == length $header) { die 'header has length 0'; } # check source my $source_l = $msg_hash->{'source'}; if( 1 != @{$source_l} ) { die 'no or more sources specified'; } my $source = @{$source_l}[0]; if( 0 == length $source) { die 'source has length 0'; } # check target my $target_l = $msg_hash->{'target'}; if( 1 != @{$target_l} ) { die 'no or more targets specified '; } my $target = @{$target_l}[0]; if( 0 == length $target) { die 'target has length 0 '; } }; if($@) { &main::daemon_log("WARNING: do not understand the message or msg is not gosa-si envelope conform:", 5); &main::daemon_log("$@", 8); } return ($msg, $msg_hash); } sub input_from_new_server { no strict "refs"; my ($input) = @_ ; my ($msg, $msg_hash); daemon_log("bus_known_server host_name: new host", 7); daemon_log("bus_known_server host_key: $bus_key", 7); # check if module can open msg envelope with key ($msg, $msg_hash) = &check_key_and_xml_validity($input, $bus_key); if( (!$msg) || (!$msg_hash) ) { daemon_log("Incoming message is not from a new gosa-si-server", 5); } return ($msg, $msg_hash); } sub input_from_known_server { my ($input, $remote_ip) = @_ ; my ($msg, $msg_hash); my $sql_statement= "SELECT * FROM bus_known_server"; my $query_res = $bus_known_server_db->select_dbentry( $sql_statement ); while( my ($hit_num, $hit) = each %{ $query_res } ) { my $host_name = $hit->{hostname}; if( not $host_name =~ "^$remote_ip") { next; } my $host_key = $hit->{hostkey}; daemon_log("bus_known_server host_name: $host_name", 7); daemon_log("bus_known_server host_key: $host_key", 7); # check if module can open msg envelope with module key my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key); if( (!$tmp_msg) || (!$tmp_msg_hash) ) { next; } else { $msg = $tmp_msg; $msg_hash = $tmp_msg_hash; last; } } if( (!$msg) || (!$msg_hash) ) { daemon_log("Incoming message is not from a known gosa-si-server", 5); } return ($msg, $msg_hash); } sub _start { my $kernel = $_[KERNEL]; $kernel->alias_set('gosa_si_bus_session'); return; } sub _default { daemon_log("ERROR: can not handle incoming msg with header '$_[ARG0]'", 1); return; } sub bus_input { my ($kernel, $heap, $input, $wheel, $session) = @_[KERNEL, HEAP, ARG0, ARG1, SESSION]; my ($msg, $msg_hash); my $error = 0; daemon_log("Incoming msg:\n$input\n", 8); # msg is from a new gosa-si-server ($msg, $msg_hash) = &input_from_new_server($input); # msg is from a gosa-si-server or gosa-si-bus if(( !$msg ) || ( !$msg_hash ) ){ ($msg, $msg_hash) = &input_from_known_server($input, $heap->{'remote_ip'}); } # an error occurred if(( !$msg ) || ( !$msg_hash )){ $error++; } if( $error == 0) { my @target_l = @{$msg_hash->{'target'}}; my $source = @{$msg_hash->{'source'}}[0]; my $header = @{$msg_hash->{header}}[0]; my $target_string = join(",", @target_l); daemon_log("got msg '$header' with target '$target_string' from ".$heap->{'remote_ip'}, 3); if( 1 == length(@target_l) && &bus_matches($target_l[0]) ) { # msg is for bus #print STDERR "msg is for bus\n"; $kernel->post('gosa_si_bus_session', $header, $msg, $msg_hash); } else { # msg is for someone else, deliver it #print STDERR "msg is for someone else\n"; foreach my $target (@target_l) { if( $target =~ /(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}:\d+)/ ) { # target is a ip address my ($sql_statement, $query_res); $sql_statement = "SELECT * FROM bus_known_server WHERE hostname='$target'"; $query_res = $bus_known_server_db->select_dbentry( $sql_statement ); if( 1 == keys(%$query_res) ) { my $host_name = $query_res->{1}->{'hostname'}; my $host_key = $query_res->{1}->{'hostkey'}; &send_msg_to_target($msg, $host_name, $host_key, $header); next; } $sql_statement = "SELECT * FROM bus_known_clients WHERE hostname='$target'"; $query_res = $bus_known_clients_db->select_dbentry( $sql_statement ); if( 1 == keys(%$query_res) ) { my $host_name = $query_res->{1}->{'hostname'}; my $server_name = $query_res->{1}->{'registered'}; # fetch correct key for server my $sql_statement = "SELECT * FROM bus_known_server WHERE hostname='$server_name'"; my $query_res = $bus_known_server_db->select_dbentry( $sql_statement ); my $server_key = $query_res->{1}->{'hostkey'}; &send_msg_to_target($msg, $server_name, $server_key, $header); next; } daemon_log("ERROR:unknown host, can not send message '$header' to target '$target'", 1); } elsif( $target =~ /([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/ ) { # target is a mac address my $sql_statement = "SELECT * FROM bus_known_clients WHERE macaddress LIKE '$target'"; my $query_res = $bus_known_clients_db->select_dbentry( $sql_statement ); if( 1 > keys(%{$query_res})) { daemon_log("ERROR: there are more than one hosts in bus_known_clients_db with mac address '$target'", 1); } elsif( 0 == keys(%{$query_res})) { daemon_log("WARNING: no host found in bus_known_clients_db with mac address '$target'", 3); } else { my $host_name = $query_res->{1}->{'hostname'}; my $server_name = $query_res->{1}->{'registered'}; my $out_msg = $msg; $out_msg =~ s/$target<\/target>/$host_name<\/target>/; # fetch correct key for server my $sql_statement = "SELECT * FROM bus_known_server WHERE hostname='$server_name'"; my $query_res = $bus_known_server_db->select_dbentry( $sql_statement ); my $server_key = $query_res->{1}->{'hostkey'}; &send_msg_to_target($out_msg, $server_name, $server_key, $header); } } else { daemon_log("ERROR: target address '$target' does not match neiter ". "to ip address nor to mac address, can not send msg", 1); } } } } } sub here_i_am { my ( $msg, $msg_hash ) = @_[ ARG0, ARG1 ]; my $source = @{$msg_hash->{'source'}}[0]; my $target = @{$msg_hash->{'target'}}[0]; my $new_key = &create_passwd(); # create bus_known_server entry my $add_hash = { table=>"bus_known_server", primkey=>"hostname", hostname=>$source, status=>"registered", hostkey=>$bus_key, }; $bus_known_server_db->add_dbentry($add_hash); # create outgoing msg my $out_hash = &create_xml_hash("new_key", $target, $source, $new_key); &send_msg_hash2address($out_hash, $source, $bus_key); # change hostkey, reason my $where_str= " WHERE hostname='$source'"; my $update_str= " SET hostkey='$new_key'"; my $sql_statement= "UPDATE bus_known_server $update_str $where_str"; $bus_known_server_db->update_dbentry($sql_statement); } sub confirm_new_key { my ( $msg, $msg_hash ) = @_[ ARG0, ARG1 ]; my $source = @{$msg_hash->{'source'}}[0]; daemon_log("'$source' confirms new key", 3); } sub new_client { my ($msg, $msg_hash) = @_[ ARG0, ARG1 ]; my $new_client = @{$msg_hash->{'new_client'}}[0]; my $source = @{$msg_hash->{'source'}}[0]; my $mac_address = @{$msg_hash->{'macaddress'}}[0]; my $act_timestamp = @{$msg_hash->{'timestamp'}}[0]; my $add_hash = { table => "bus_known_clients", primkey=>"hostname", hostname=>$new_client, status=>'activ', registered=>$source, macaddress=>$mac_address, timestamp=>$act_timestamp, }; $bus_known_clients_db->add_dbentry($add_hash); daemon_log("add new client '$new_client' to bus_known_clients_db", 3); } #==== MAIN = main ============================================================== # parse commandline options Getopt::Long::Configure( "bundling" ); GetOptions("h|help" => \&usage, "c|config=s" => \$cfg_file, "f|foreground" => \$foreground, "v|verbose+" => \$verbose, ); # read and set config parameters &check_cmdline_param ; &read_configfile; &check_pid; $SIG{CHLD} = 'IGNORE'; # 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 ) }; } # restart daemon log file if(-e $log_file ) { unlink $log_file } daemon_log(" ", 1); daemon_log("started!", 1); # delete old DBsqlite lock files system('rm -f /tmp/gosa_si_lock*gosa-si-bus*'); #prepare other variables $xml = new XML::Simple(); $bus_address = "$bus_ip:$bus_port"; # detect ip and mac address and complete host address my $network_interface= &get_interface_for_ip($bus_ip); $bus_mac_address= &get_mac($network_interface); daemon_log("gosa-si-bus ip address detected: $bus_ip", 1); daemon_log("gosa-si-bus mac address detected: $bus_mac_address", 1); # connect to bus_known_server_db my @server_col_names = ('hostname', 'status', 'hostkey', 'timestamp'); $bus_known_server_db = GOSA::DBsqlite->new($bus_known_server_file_name); $bus_known_server_db->create_table('bus_known_server', \@server_col_names); my @clients_col_names = ('hostname', 'status', 'registered', 'macaddress', 'timestamp'); $bus_known_clients_db = GOSA::DBsqlite->new($bus_known_clients_file_name); $bus_known_clients_db->create_table('bus_known_clients', \@clients_col_names); # create socket for incoming xml messages POE::Component::Server::TCP->new( Alias => 'gosa-si-bus_socket', Port => $bus_port, ClientInput => \&bus_input, ); daemon_log("start socket for incoming xml messages at port '$bus_port' ", 1); # start session POE::Session->create( inline_states => { _start => \&_start, _default => \&_default, here_i_am => \&here_i_am, confirm_new_key => \&confirm_new_key, new_client => \&new_client, } ); POE::Kernel->run(); exit;