Code

027814b80a50c84c481ce95cf9e50a37a23935b9
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl libipc-shareable-perl libdata-dumper-simple-perl
12 #         BUGS:  ---
13 #        NOTES:
14 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
15 #      COMPANY:
16 #      VERSION:  1.0
17 #      CREATED:  12.09.2007 08:54:41 CEST
18 #     REVISION:  ---
19 #===============================================================================
22 use strict;
23 use warnings;
24 use Getopt::Long;
25 use Config::IniFiles;
26 use POSIX;
27 use Time::HiRes qw( gettimeofday );
29 use Fcntl;
30 use IO::Socket::INET;
31 use Crypt::Rijndael;
32 use MIME::Base64;
33 use Digest::MD5  qw(md5 md5_hex md5_base64);
34 use XML::Simple;
35 use Data::Dumper;
36 use Sys::Syslog qw( :DEFAULT setlogsock);
37 use Cwd;
38 use File::Spec;
39 use IPC::Shareable qw( :lock);
40 IPC::Shareable->clean_up_all;
42 use lib "/etc/gosa-si/modules";
43 my $modules_path = "/etc/gosa-si/modules";
45 my ($cfg_file, %cfg_defaults, $foreground, $verbose, $ping_timeout, $no_bus);
46 my ($bus, $msg_to_bus, $bus_cipher);
47 my ($server, $server_mac_address, $server_events);
48 my ($gosa_server);
49 my ($known_daemons, $shmda, $known_clients, $shmcl, $known_modules);
50 my ($max_clients);
51 my ($pid_file, $procid, $pid, $log_file);
52 my (%free_child, %busy_child, $child_max, $child_min, %child_alive_time, $child_timeout);
53 my ($arp_activ, $arp_fifo, $arp_fifo_path, $no_arp);
55 # variables declared in config file are always set to 'our'
56 our (%cfg_defaults, $log_file, $pid_file, 
57     $bus_activ, $bus_passwd, $bus_ip, $bus_port,
58     $server_activ, $server_ip, $server_port, $server_passwd, $max_clients,
59     $arp_activ, $arp_fifo_path,
60     $gosa_activ, $gosa_passwd, $gosa_ip, $gosa_port, $gosa_timeout,
61 );
63 # additional variable which should be globaly accessable
64 our $xml;
65 our $server_address;
66 our $bus_address;
67 our $gosa_address;
69 # specifies the verbosity of the daemon_log
70 $verbose = 0 ;
72 # if foreground is not null, script will be not forked to background
73 $foreground = 0 ;
75 # specifies the timeout seconds while checking the online status of a registrating client
76 $ping_timeout = 5;
78 $no_bus = 0;
80 $no_arp = 0;
82 # holds all other gosa-sd as well as the gosa-sd-bus
83 our $known_daemons = {};
84 our $shmda = tie($known_daemons, 'IPC::Shareable', undef, {create => 1, 
85                                                             exclusive => 1, 
86                                                             mode => 0666, 
87                                                             destroy => 1,
88                                                             });
89 # holds all registrated clients
90 our $known_clients = {};
91 our $shmcl = tie($known_clients, 'IPC::Shareable', undef, {create => 1, 
92                                                             exclusive => 1, 
93                                                             mode => 0666, 
94                                                             destroy => 1,
95                                                             });
98 %cfg_defaults =
99 ("general" =>
100     {"log_file" => [\$log_file, "/var/run/".$0.".log"],
101     "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
102     "child_max" => [\$child_max, 10],
103     "child_min" => [\$child_min, 3],
104     "child_timeout" => [\$child_timeout, 180],
105    },
106 "bus" =>
107     {"bus_activ" => [\$bus_activ, "on"],
108     "bus_passwd" => [\$bus_passwd, ""],
109     "bus_ip" => [\$bus_ip, ""],
110     "bus_port" => [\$bus_port, "20080"],
111     },
112 "server" =>
113     {"server_activ" => [\$server_activ, "on"],
114     "server_ip" => [\$server_ip, ""],
115     "server_port" => [\$server_port, "20081"],
116     "server_passwd" => [\$server_passwd, ""],
117     "max_clients" => [\$max_clients, 100],
118     },
119 "arp" =>
120     {"arp_activ" => [\$arp_activ, "on"],
121     "arp_fifo_path" => [\$arp_fifo_path, "/var/run/gosa-si/arp-notify"],
122     },
123 "gosa" =>
124     {"gosa_activ" => [\$gosa_activ, "on"],
125     "gosa_ip" => [\$gosa_ip, ""],
126     "gosa_port" => [\$gosa_port, "20082"],
127     "gosa_passwd" => [\$gosa_passwd, "none"],
128     },
129     );
132 #===  FUNCTION  ================================================================
133 #         NAME:  usage
134 #   PARAMETERS:  nothing
135 #      RETURNS:  nothing
136 #  DESCRIPTION:  print out usage text to STDERR
137 #===============================================================================
138 sub usage {
139     print STDERR << "EOF" ;
140 usage: $0 [-hvf] [-c config]
142            -h        : this (help) message
143            -c <file> : config file
144            -f        : foreground, process will not be forked to background
145            -v        : be verbose (multiple to increase verbosity)
146 EOF
147     print "\n" ;
151 #===  FUNCTION  ================================================================
152 #         NAME:  read_configfile
153 #   PARAMETERS:  cfg_file - string -
154 #      RETURNS:  nothing
155 #  DESCRIPTION:  read cfg_file and set variables
156 #===============================================================================
157 sub read_configfile {
158     my $cfg;
159     if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
160         if( -r $cfg_file ) {
161             $cfg = Config::IniFiles->new( -file => $cfg_file );
162         } else {
163             print STDERR "Couldn't read config file!";
164         }
165     } else {
166         $cfg = Config::IniFiles->new() ;
167     }
168     foreach my $section (keys %cfg_defaults) {
169         foreach my $param (keys %{$cfg_defaults{ $section }}) {
170             my $pinfo = $cfg_defaults{ $section }{ $param };
171             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
172         }
173     }
177 #===  FUNCTION  ================================================================
178 #         NAME:  logging
179 #   PARAMETERS:  level - string - default 'info'
180 #                msg - string -
181 #                facility - string - default 'LOG_DAEMON'
182 #      RETURNS:  nothing
183 #  DESCRIPTION:  function for logging
184 #===============================================================================
185 sub daemon_log {
186 # log into log_file
187     my( $msg, $level ) = @_;
188     if(not defined $msg) { return }
189     if(not defined $level) { $level = 1 }
190     if(defined $log_file){
191         open(LOG_HANDLE, ">>$log_file");
192         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
193             print STDERR "cannot open $log_file: $!";
194             return }
195             chomp($msg);
196             if($level <= $verbose){
197                 print LOG_HANDLE "$level $msg\n";
198                 if(defined $foreground) { print $msg."\n" }
199             }
200     }
201     close( LOG_HANDLE );
202 #log into syslog
203 #    my ($msg, $level, $facility) = @_;
204 #    if(not defined $msg) {return}
205 #    if(not defined $level) {$level = "info"}
206 #    if(not defined $facility) {$facility = "LOG_DAEMON"}
207 #    openlog($0, "pid,cons,", $facility);
208 #    syslog($level, $msg);
209 #    closelog;
210 #    return;
214 #===  FUNCTION  ================================================================
215 #         NAME:  check_cmdline_param
216 #   PARAMETERS:  nothing
217 #      RETURNS:  nothing
218 #  DESCRIPTION:  validates commandline parameter
219 #===============================================================================
220 sub check_cmdline_param () {
221     my $err_config;
222     my $err_counter = 0;
223     if( not defined( $cfg_file)) {
224         #$err_config = "please specify a config file";
225         #$err_counter += 1;
226         my $cwd = getcwd;
227         my $name = "/etc/gosa-si/server.conf";
228         $cfg_file = File::Spec->catfile( $cwd, $name );
229     }
230     if( $err_counter > 0 ) {
231         &usage( "", 1 );
232         if( defined( $err_config)) { print STDERR "$err_config\n"}
233         print STDERR "\n";
234         exit( -1 );
235     }
239 #===  FUNCTION  ================================================================
240 #         NAME:  check_pid
241 #   PARAMETERS:  nothing
242 #      RETURNS:  nothing
243 #  DESCRIPTION:  handels pid processing
244 #===============================================================================
245 sub check_pid {
246     $pid = -1;
247     # Check, if we are already running
248     if( open(LOCK_FILE, "<$pid_file") ) {
249         $pid = <LOCK_FILE>;
250         if( defined $pid ) {
251             chomp( $pid );
252             if( -f "/proc/$pid/stat" ) {
253                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
254                 if( $0 eq $stat ) {
255                     close( LOCK_FILE );
256                     exit -1;
257                 }
258             }
259         }
260         close( LOCK_FILE );
261         unlink( $pid_file );
262     }
264     # create a syslog msg if it is not to possible to open PID file
265     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
266         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
267         if (open(LOCK_FILE, '<', $pid_file)
268                 && ($pid = <LOCK_FILE>))
269         {
270             chomp($pid);
271             $msg .= "(PID $pid)\n";
272         } else {
273             $msg .= "(unable to read PID)\n";
274         }
275         if( ! ($foreground) ) {
276             openlog( $0, "cons,pid", "daemon" );
277             syslog( "warning", $msg );
278             closelog();
279         }
280         else {
281             print( STDERR " $msg " );
282         }
283         exit( -1 );
284     }
288 #===  FUNCTION  ================================================================
289 #         NAME:  get_ip_and_mac 
290 #   PARAMETERS:  nothing
291 #      RETURNS:  (ip, mac) 
292 #  DESCRIPTION:  executes /sbin/ifconfig and parses the output, the first occurence 
293 #                of a inet address is returned as well as the mac address in the line
294 #                above the inet address
295 #===============================================================================
296 sub get_ip_and_mac {
297     my $ip = "0.0.0.0.0"; # Defualt-IP
298     my $mac = "00:00:00:00:00:00";  # Default-MAC
299     my @ifconfig = qx(/sbin/ifconfig);
300     foreach(@ifconfig) {
301         if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
302             $mac = "$1:$2:$3:$4:$5:$6";
303             next;
304         }
305         if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
306             $ip = "$1.$2.$3.$4";
307             last;
308         }
309     }
310     return ($ip, $mac);
315 #===  FUNCTION  ================================================================
316 #         NAME:  import_modules
317 #   PARAMETERS:  module_path - string - abs. path to the directory the modules are stored
318 #      RETURNS:  nothing
319 #  DESCRIPTION:  each file in module_path which ends with '.pm' is imported by "require 'file';"
320 #===============================================================================
321 sub import_modules {
322     daemon_log(" ", 1);
324     if (not -e $modules_path) {
325         daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
326     }
328     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
329     while (defined (my $file = readdir (DIR))) {
330         if (not $file =~ /(\S*?).pm$/) {
331             next;
332         }
333         eval { require $file; };
334         if ($@) {
335             daemon_log("ERROR: gosa-sd could not load module $file", 1);
336             daemon_log("$@", 5);
337             next;
338         }
339         my $mod_name = $1;
340         my $module_tag_hash = eval( $mod_name.'::get_module_tags()' );
341         $known_modules->{$mod_name} = $module_tag_hash;
343         daemon_log("load module $mod_name", 1);
344     }   
346     # for debugging
347     #while ( my ($module, $tag_hash) = each(%$known_modules)) {
348     #    print "\tmodule: $module"."\n";   
349     #    print "\ttags: ".join(", ", keys(%$tag_hash))."\n";
350     #}
351     close (DIR);
355 #===  FUNCTION  ================================================================
356 #         NAME:  register_at_bus
357 #   PARAMETERS:  nothing
358 #      RETURNS:  nothing
359 #  DESCRIPTION:  creates an entry in known_daemons and send a 'here_i_am' msg to bus
360 #===============================================================================
361 sub register_at_bus {
363     # create known_daemons entry
364     &create_known_daemon($bus_address);
365     &add_content2known_daemons(hostname=>$bus_address, status=>"register_at_bus", passwd=>$bus_passwd);
367     my $msg_hash = &create_xml_hash("here_i_am", "$server_ip:$server_port", $bus_address);
368     my $answer = "";
369     $answer = &send_msg_hash2address($msg_hash, $bus_address);
370     if ($answer == 0) {
371         daemon_log("register at bus: $bus_address", 1);
372     } else {
373         daemon_log("unable to send 'register'-msg to bus: $bus_address", 1);
374     }
375     return;
379 #===  FUNCTION  ================================================================
380 #         NAME:  sig_int_handler
381 #   PARAMETERS:  signal - string - signal arose from system
382 #      RETURNS:  noting
383 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
384 #===============================================================================
385 sub sig_int_handler {
386     my ($signal) = @_;
387     if($server){
388         close($server);
389         daemon_log("daemon server closed", 1);
390     }
391     if( -p $arp_fifo_path ) {
392         close $arp_fifo  ;
393         unlink($arp_fifo_path) ;
394         daemon_log("ARP_FIFO closed", 1) ;
395     }
397     if($gosa_server){
398         close($gosa_server);
399         daemon_log("gosa server closed", 1);
400     }
402     print STDERR "$signal\n";
403     
404     exit(1);
406 $SIG{INT} = \&sig_int_handler;
409 #===  FUNCTION  ================================================================
410 #         NAME:  activating_child
411 #   PARAMETERS:  msg - string - incoming message
412 #                host - string - host from which the incomming message comes
413 #      RETURNS:  nothing
414 #  DESCRIPTION:  handels the distribution of incoming messages to working childs
415 #===============================================================================
416 sub activating_child {
417     my ($msg, $host, $client) = @_;
418     my $child = &get_processing_child();
419     my $pipe_wr = $$child{'pipe_wr'};
420     my $pipe_rd = $$child{'pipe_rd'};
421     $$child{client_ref} = $client;
422     daemon_log("activating: childpid:$$child{'pid'}", 5);
424     print $pipe_wr $msg.".".$host."\n";
426     return;
430 #===  FUNCTION  ================================================================
431 #         NAME:  get_processing_child
432 #   PARAMETERS:  nothing
433 #      RETURNS:  child - hash - holding the process id and the references to the pipe
434 #                               handles pipe_wr and pipe_rd
435 #  DESCRIPTION:  handels the forking, reactivating and keeping alive tasks
436 #===============================================================================
437 sub get_processing_child {
438     my $child;
440     while(my ($key, $val) = each(%free_child)) {
441         my $exitus_pid = waitpid($key, WNOHANG);
442         if($exitus_pid != 0) {
443             delete $free_child{$key};
444         }
445         daemon_log("free child:$key", 5);
446     }
447     # check @free_child and @busy_child
448     my $free_len = scalar(keys(%free_child));
449     my $busy_len = scalar(keys(%busy_child));
450     daemon_log("free children $free_len, busy children $busy_len", 5);
452     # if there is a free child, let the child work
453     if($free_len > 0){
454         my @keys = keys(%free_child);
455         $child = $free_child{$keys[0]};
456         if(defined $child) {
457             $busy_child{$$child{'pid'}} = $child ;
458             delete $free_child{$$child{'pid'}};
459         }
460         return $child;
461     }
463     # no free child, try to fork another one
464     if($free_len + $busy_len < $child_max) {
466         daemon_log("not enough children, create a new one", 5);
468         # New pipes for communication
469         my( $PARENT_wr, $PARENT_rd );
470         my( $CHILD_wr, $CHILD_rd );
471         pipe( $CHILD_rd,  $PARENT_wr );
472         pipe( $PARENT_rd, $CHILD_wr  );
473         $PARENT_wr->autoflush(1);
474         $CHILD_wr->autoflush(1);
476         ############
477         # fork child
478         ############
479         my $child_pid = fork();
480         
481         #CHILD
482         if($child_pid == 0) {
483             # Close unused pipes
484             close( $CHILD_rd );
485             close( $CHILD_wr );
486             while( 1 ) {
487                 my $rbits = "";
488                 vec( $rbits, fileno $PARENT_rd , 1 ) = 1;
489                 my $nf = select($rbits, undef, undef, $child_timeout);
490                 if($nf < 0 ) {
491                     die "select(): $!\n";
492                 } elsif (! $nf) {
493                     # if already child_min childs are alive, then leave loop
494                     $free_len = scalar(keys(%free_child));
495                     $busy_len = scalar(keys(%busy_child));
496                     if($free_len + $busy_len >= $child_min) {
497                         last;
498                     } else {
499                         redo;
500                     }
501                 }
503                 # a job for a child arise
504                 if ( vec $rbits, fileno $PARENT_rd, 1 ) {
505                     # read everything from pipe
506                     my $msg = "";
507                     $PARENT_rd->blocking(0);
508                     while(1) {
509                         my $read = <$PARENT_rd>;
510                         if(not defined $read) { last}
511                         $msg .= $read;
512                     }
514                     ######################################
515                     # forward msg to all imported modules 
516                     no strict "refs";
517                     my $answer;
518                     while( my ($module, $tag_hash) = each(%$known_modules)) {
519                         #if(exists $known_modules->{$module}->{server_packages}) {
520                             my $tmp = &{ $module."::process_incoming_msg" }($msg);
521                             if (defined $tmp) {
522                                 $answer = $tmp;
523                             }
524                         #}
525                     }        
527                     #&print_known_daemons();
528                     #&print_known_clients();
530                     daemon_log("processing of msg finished", 5);
532                     if (defined $answer) {
533                         print $PARENT_wr $answer."\n";
534                         daemon_log("\t$answer", 5);
535                         daemon_log(" ", 5);
536                     } else {
537                         print $PARENT_wr "done"."\n";
538                         daemon_log(" ", 5);
539                     }
540                     redo;
541                 }
542             }
543             # childs leaving the loop are allowed to die
544             exit(0);
547         #PARENT
548         } else {
549             # Close unused pipes
550             close( $PARENT_rd );
551             close( $PARENT_wr );
553             # add child to child alive hash
554             my %child_hash = (
555                     'pid' => $child_pid,
556                     'pipe_wr' => $CHILD_wr,
557                     'pipe_rd' => $CHILD_rd,
558                     'client_ref' => "",
559                     );
561             $child = \%child_hash;
562             $busy_child{$$child{'pid'}} = $child;
563             return $child;
564         }
565     }
569 #===  FUNCTION  ================================================================
570 #         NAME:  process_incoming_msg
571 #   PARAMETERS:  crypted_msg - string - incoming crypted message
572 #      RETURNS:  nothing
573 #  DESCRIPTION:  handels the proceeded distribution to the appropriated functions
574 #===============================================================================
575 sub process_incoming_msg {
576     my ($crypted_msg) = @_;
577     if(not defined $crypted_msg) {
578         daemon_log("function 'process_incoming_msg': got no msg", 7);
579     }
580     $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
581     $crypted_msg = $1;
582     my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
583     daemon_log("msg from host:", 1);
584     daemon_log("\t$host", 1);
585     #daemon_log("crypted msg:", 7);
586     #daemon_log("\t$crypted_msg", 7);
588     # collect addresses from possible incoming clients
589     my @valid_keys;
590     my @host_keys = keys %$known_daemons;
591     foreach my $host_key (@host_keys) {    
592         if($host_key =~ "^$host") {
593             push(@valid_keys, $host_key);
594         }
595     }
596     my @client_keys = keys %$known_clients;
597     foreach my $client_key (@client_keys) {
598         if($client_key =~ "^$host"){
599             push(@valid_keys, $client_key);
600         }
601     }
602     push(@valid_keys, $server_address);
603     
604     my $l = @valid_keys;
605     my ($msg, $msg_hash);
606     my $msg_flag = 0;    
608     # determine the correct passwd for deciphering of the incoming msgs
609     foreach my $host_key (@valid_keys) {
610         eval{
611             daemon_log( "key: $host_key", 7);
612             my $key_passwd;
613             if (exists $known_daemons->{$host_key}) {
614                 $key_passwd = $known_daemons->{$host_key}->{passwd};
615             } elsif (exists $known_clients->{$host_key}) {
616                 $key_passwd = $known_clients->{$host_key}->{passwd};
617             } elsif ($host_key eq $server_address) {
618                 $key_passwd = $server_passwd;
619             } 
620             daemon_log("key_passwd: $key_passwd", 7);
621             my $key_cipher = &create_ciphering($key_passwd);
622             $msg = &decrypt_msg($crypted_msg, $key_cipher);
623             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
624         };
625         if($@) {
626             daemon_log("key raise error", 7);
627             $msg_flag += 1;
628         } else {
629             last;
630         }
631     } 
632     
633     if($msg_flag >= $l)  {
634         daemon_log("ERROR: do not understand the message:", 1);
635         daemon_log("\t$msg", 1);
636         return;
637     }
639     # process incoming msg
640     my $header = &get_content_from_xml_hash($msg_hash, "header");
641     my $source = @{$msg_hash->{source}}[0];
643     daemon_log("header from msg:", 1);
644     daemon_log("\t$header", 1);
645     daemon_log("msg to process:", 5);
646     daemon_log("\t$msg", 5);
648     my @targets = @{$msg_hash->{target}};
649     my $len_targets = @targets;
650     if ($len_targets == 0){     
651         daemon_log("ERROR: no target specified for msg $header", 1);
653     } elsif ($len_targets == 1){
654         # we have only one target symbol
656         my $target = $targets[0];
657         daemon_log("msg is for:", 7);
658         daemon_log("\t$target", 7);
660         if ($target eq $server_address) {
661             # msg is for server
662             if ($header eq 'new_passwd'){ &new_passwd($msg_hash)}
663             elsif ($header eq 'here_i_am') { &here_i_am($msg_hash)}
664             elsif ($header eq 'who_has') { &who_has($msg_hash) }
665             elsif ($header eq 'who_has_i_do') { &who_has_i_do($msg_hash)}
666             elsif ($header eq 'update_status') { &update_status($msg_hash) }
667             elsif ($header eq 'get_load') { &execute_actions($msg_hash)}
668             else { daemon_log("ERROR: no function assigned to this msg", 5) }
670         
671        } elsif ($target eq "*") {
672             # msg is for all clients
674             my @target_addresses = keys(%$known_clients);
675             foreach my $target_address (@target_addresses) {
676                 if ($target_address eq $source) { next; }
677                 $msg_hash->{target} = [$target_address];
678                 &send_msg_hash2address($msg_hash, $target_address);
679             }           
680         } else {
681             # msg is for one client
683             if (exists $known_clients->{$target}) {
684                 # target is known
686                 &send_msg_hash2address($msg_hash, $target);
687             } else {
688                 # target is not known
690                 daemon_log("ERROR: target $target is not known in known_clients", 1);
691             }
692         }
693     } else {
694         # we have multiple target symbols
696         my $target_string = join(", ", @targets);
697         daemon_log("msg is for:", 7);
698         daemon_log("\t$target_string", 7);
699         
700         my $target_address;
701         foreach $target_address (@targets) {
702             if (exists $known_clients->{$target_address}) {
703                 # target_address is known
705                 &send_msg_hash2address($msg_hash, $target_address);
706                 daemon_log("server forwards msg $header to client $target_address", 3);
707             } else {
708                 # target is not known
710                 daemon_log("ERROR: target $target_address is not known in known_clients", 1);
711             }
712         }
715     }
717    return;
721 #===  FUNCTION  ================================================================
722 #         NAME:  open_socket
723 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
724 #                [PeerPort] string necessary if port not appended by PeerAddr
725 #      RETURNS:  socket IO::Socket::INET
726 #  DESCRIPTION:  open a socket to PeerAddr
727 #===============================================================================
728 sub open_socket {
729     my ($PeerAddr, $PeerPort) = @_ ;
730     if(defined($PeerPort)){
731         $PeerAddr = $PeerAddr.":".$PeerPort;
732     }
733     my $socket;
734     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
735             Porto => "tcp" ,
736             Type => SOCK_STREAM,
737             Timeout => 5,
738             );
739     if(not defined $socket) {
740         return;
741     }
742     daemon_log("open_socket:", 7);
743     daemon_log("\t$PeerAddr", 7);
744     return $socket;
748 #===  FUNCTION  ================================================================
749 #         NAME:  open_fifo
750 #   PARAMETERS:  $fifo_path
751 #      RETURNS:  0: FIFO couldn"t be setup, 1: FIFO setup correctly
752 #  DESCRIPTION:  creates a FIFO at $fifo_path
753 #===============================================================================
754 sub open_fifo {
755     my ($fifo_path) = @_ ;
756     if( -p $fifo_path ) {
757         daemon_log("FIFO at $fifo_path already exists! Is being deleted!", 1);
758         unlink($fifo_path);
759     }
760     POSIX::mkfifo($fifo_path, 0666) or die "can't mkfifo $fifo_path: $!";
761     daemon_log( "FIFO started at $fifo_path", 1) ;
762     return 1;
766 #===  FUNCTION  ================================================================
767 #         NAME:  read_from_socket
768 #   PARAMETERS:  socket fh - 
769 #      RETURNS:  result string - readed characters from socket
770 #  DESCRIPTION:  reads data from socket in 16 byte steps
771 #===============================================================================
772 sub read_from_socket {
773     my ($socket) = @_;
774     my $result = "";
776     $socket->blocking(1);
777     $result = <$socket>;
779     $socket->blocking(0);
780     while ( my $char = <$socket> ) {
781         if (not defined $char) { last }
782         $result .= $char;
783     }
785     return $result;
789 #===  FUNCTION  ================================================================
790 #         NAME:  create_xml_hash
791 #   PARAMETERS:  header - string - message header (required)
792 #                source - string - where the message come from (required)
793 #                target - string - where the message should go to (required)
794 #                [header_value] - string - something usefull (optional)
795 #      RETURNS:  hash - hash - nomen est omen
796 #  DESCRIPTION:  creates a key-value hash, all values are stored in a array
797 #===============================================================================
798 sub create_xml_hash {
799     my ($header, $source, $target, $header_value) = @_;
800     my $hash = {
801             header => [$header],
802             source => [$source],
803             target => [$target],
804             $header => [$header_value],
805     };
806     #daemon_log("create_xml_hash:", 7),
807     #chomp(my $tmp = Dumper $hash);
808     #daemon_log("\t$tmp", 7);
809     return $hash
813 #===  FUNCTION  ================================================================
814 #         NAME:  create_xml_string
815 #   PARAMETERS:  xml_hash - hash - hash from function create_xml_hash
816 #      RETURNS:  xml_string - string - xml string representation of the hash
817 #  DESCRIPTION:  transform the hash to a string using XML::Simple module
818 #===============================================================================
819 sub create_xml_string {
820     my ($xml_hash) = @_ ;
821     my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
822     $xml_string =~ s/[\n]+//g;
823     #daemon_log("create_xml_string:",7);
824     #daemon_log("$xml_string\n", 7);
825     return $xml_string;
829 #===  FUNCTION  ================================================================
830 #         NAME:  add_content2xml_hash
831 #   PARAMETERS:  xml_ref - ref - reference to a hash from function create_xml_hash
832 #                element - string - key for the hash
833 #                content - string - value for the hash
834 #      RETURNS:  nothing
835 #  DESCRIPTION:  add key-value pair to xml_ref, if key alread exists, then append value to list
836 #===============================================================================
837 sub add_content2xml_hash {
838     my ($xml_ref, $element, $content) = @_;
839     if(not exists $$xml_ref{$element} ) {
840         $$xml_ref{$element} = [];
841     }
842     my $tmp = $$xml_ref{$element};
843     push(@$tmp, $content);
844     return;
848 #===  FUNCTION  ================================================================
849 #         NAME:  get_content_from_xml_hash
850 #   PARAMETERS:  xml_ref - ref - reference of the xml hash
851 #                element - string - key of the value you want
852 #      RETURNS:  value - string - if key is either header, target or source
853 #                value - list - for all other keys in xml hash
854 #  DESCRIPTION:
855 #===============================================================================
856 sub get_content_from_xml_hash {
857     my ($xml_ref, $element) = @_ ;
858     my $result = $xml_ref->{$element};
859     if( $element eq "header" || $element eq "target" || $element eq "source") {
860         return @$result[0];
861     }
862     return @$result;
866 #===  FUNCTION  ================================================================
867 #         NAME:  encrypt_msg
868 #   PARAMETERS:  msg - string - message to encrypt
869 #                my_cipher - ref - reference to a Crypt::Rijndael object
870 #      RETURNS:  crypted_msg - string - crypted message
871 #  DESCRIPTION:  crypts the incoming message with the Crypt::Rijndael module
872 #===============================================================================
873 sub encrypt_msg {
874     my ($msg, $my_cipher) = @_;
875     if(not defined $my_cipher) { print "no cipher object\n"; }
876     $msg = "\0"x(16-length($msg)%16).$msg;
877     my $crypted_msg = $my_cipher->encrypt($msg);
878     chomp($crypted_msg = &encode_base64($crypted_msg));
879     return $crypted_msg;
883 #===  FUNCTION  ================================================================
884 #         NAME:  decrypt_msg
885 #   PARAMETERS:  crypted_msg - string - message to decrypt
886 #                my_cipher - ref - reference to a Crypt::Rijndael object
887 #      RETURNS:  msg - string - decrypted message
888 #  DESCRIPTION:  decrypts the incoming message with the Crypt::Rijndael module
889 #===============================================================================
890 sub decrypt_msg {
891     my ($crypted_msg, $my_cipher) = @_ ;
892     $crypted_msg = &decode_base64($crypted_msg);
893     my $msg = $my_cipher->decrypt($crypted_msg); 
894     $msg =~ s/\0*//g;
895     return $msg;
899 #===  FUNCTION  ================================================================
900 #         NAME:  create_ciphering
901 #   PARAMETERS:  passwd - string - used to create ciphering
902 #      RETURNS:  cipher - object
903 #  DESCRIPTION:  creates a Crypt::Rijndael::MODE_CBC object with passwd as key
904 #===============================================================================
905 sub create_ciphering {
906     my ($passwd) = @_;
907     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
908     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
910     #daemon_log("iv: $iv", 7);
911     #daemon_log("key: $passwd", 7);
912     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
913     $my_cipher->set_iv($iv);
914     return $my_cipher;
918 #===  FUNCTION  ================================================================
919 #         NAME:  send_msg_hash2address
920 #   PARAMETERS:  msg_hash - hash - xml_hash created with function create_xml_hash
921 #                PeerAddr string - socket address to send msg
922 #                PeerPort string - socket port, if not included in socket address
923 #      RETURNS:  nothing
924 #  DESCRIPTION:  ????
925 #===============================================================================
926 sub send_msg_hash2address {
927     my ($msg_hash, $address, $passwd) = @_ ;
929     # fetch header for logging
930     my $header = &get_content_from_xml_hash($msg_hash, "header");
931     
932     # generate xml string
933     my $msg_xml = &create_xml_string($msg_hash);
934     
935     # fetch the appropriated passwd from hash 
936     if(not defined $passwd) {
937         if(exists $known_daemons->{$address}) {
938             $passwd = $known_daemons->{$address}->{passwd};
939         } elsif(exists $known_clients->{$address}) {
940             $passwd = $known_clients->{$address}->{passwd};
941             
942         } else {
943             daemon_log("$address not known, neither as server nor as client", 1);
944             return 1;
945         }
946     }
947     
948     # create ciphering object
949     my $act_cipher = &create_ciphering($passwd);
950     
951     # encrypt xml msg
952     my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
953     
954     # opensocket
955     my $socket = &open_socket($address);
956     if(not defined $socket){
957         daemon_log( "cannot send '$header'-msg to $address , server not reachable", 5);
959         if (exists $known_clients->{$address}) {
960             if ($known_clients->{$address}->{status} eq "down") {
961                 # if status of not reachable client is already 'down', then delete client from known_clients
962                 &clean_up_known_clients($address);
964             } else {
965                 # update status to 'down'
966                 &update_known_clients(hostname=>$address, status=>"down");        
968             }
969         }
970         return 1;
971     }
972     
973     # send xml msg
974     print $socket $crypted_msg."\n";
975     
976     close $socket;
978     daemon_log("send '$header'-msg to $address", 1);
980     daemon_log("$msg_xml", 5);
982     #daemon_log("crypted message:",7);
983     #daemon_log("\t$crypted_msg", 7);
985     # update status of client in known_clients with last send msg
986     if(exists $known_daemons->{$address}) {
987         #&update_known_daemons();
988     } elsif(exists $known_clients->{$address}) {
989         &update_known_clients(hostname=>$address, status=>$header);
990     }
992     return 0;
996 #===  FUNCTION  ================================================================
997 #         NAME:  send_msg_hash2bus
998 #   PARAMETERS:  msg_hash - hash - xml_hash created with function create_xml_hash
999 #      RETURNS:  nothing
1000 #  DESCRIPTION:  ????
1001 #===============================================================================
1002 sub send_msg_hash2bus {
1003     my($msg_hash) = @_;
1005     # fetch header for logging
1006     my $header = &get_content_from_xml_hash($msg_hash, "header");    
1008     # generate xml string
1009     my $msg_xml = &create_xml_string($msg_hash);
1011     # encrypt xml msg 
1012     my $crypted_msg = &encrypt_msg($msg_xml, $bus_cipher);
1014     # open socket
1015     my $socket = &open_socket($bus_address);
1016     if(not defined $socket){
1017         daemon_log( "cannot send '$header'-msg to $bus_address , bus not reachable", 5);
1018         return;
1019     }
1020     
1021     # send xml msg
1022     print $socket $crypted_msg."\n";
1023     
1024     close $socket;
1025    
1027     daemon_log("send '$header'-msg to bus", 1);
1028     daemon_log("$msg_xml", 5);
1029     #daemon_log("crypted msg:",7);
1030     #daemon_log("\t$crypted_msg", 7);
1032     return;
1041 ##===  FUNCTION  ================================================================
1042 ##         NAME:  new_passwd
1043 ##   PARAMETERS:  msg_hash - ref - hash from function create_xml_hash
1044 ##      RETURNS:  nothing
1045 ##  DESCRIPTION:  process this incoming message
1046 ##===============================================================================
1047 #sub new_passwd {
1048 #    my ($msg_hash) = @_;
1049 #    
1050 #    my $source = &get_content_from_xml_hash($msg_hash, "source");
1051 #    my $passwd = (&get_content_from_xml_hash($msg_hash, "new_passwd"))[0];
1053 #    if (exists $known_daemons->{$source}) {
1054 #        &add_content2known_daemons(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
1055 #        $bus_cipher = &create_ciphering($passwd);
1056 #        my $hash = &create_xml_hash("confirm_new_passwd", "$server_ip:$server_port", "$source");
1057 #        &send_msg_hash2address($hash, $source);
1059 #    } elsif (exists $known_clients->{$source}) {
1060 #        &add_content2known_clients(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
1062 #    } else {
1063 #        daemon_log("ERROR: $source not known, neither in known_daemons nor in known_clients", 1)   
1064 #    }
1066 #    return;
1067 #}
1070 ##===  FUNCTION  ================================================================
1071 ##         NAME:  make ping
1072 ##   PARAMETERS:  address - string - address which should be pinged
1073 ##      RETURNS:  nothing
1074 ##  DESCRIPTION:  send ping message to address
1075 ##===============================================================================
1076 #sub make_ping {
1077 #    my ($msg_hash) = @_;
1079 #    my $source = &get_content_from_xml_hash($msg_hash, "source");
1080 #    my $target = &get_content_from_xml_hash($msg_hash, "target");
1081 #    
1082 #    print "make_ping:$source\n";
1083 #    my $out_hash = &create_xml_hash("ping", $target, $source);
1084 #    &send_msg_hash2address($out_hash, $source);
1085 #    return;
1086 #}
1089 ##===  FUNCTION  ================================================================
1090 ##         NAME:  got_ping
1091 ##   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
1092 ##      RETURNS:  nothing
1093 ##  DESCRIPTION:  process this incoming message
1094 ##===============================================================================
1095 #sub got_ping {
1096 #    my ($msg_hash) = @_;
1097 #    
1098 #    my $source = &get_content_from_xml_hash($msg_hash, 'source');
1099 #    my $target = &get_content_from_xml_hash($msg_hash, 'target');
1100 #    my $header = &get_content_from_xml_hash($msg_hash, 'header');    
1101 #    
1102 #    if(exists $known_daemons->{$source}) {
1103 #        &add_content2known_daemons(hostname=>$source, status=>$header);
1104 #    } else {
1105 #        &add_content2known_clients(hostname=>$source, status=>$header);
1106 #    }
1107 #    
1108 #    return;
1109 #}
1112 ##===  FUNCTION  ================================================================
1113 ##         NAME:  here_i_am
1114 ##   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
1115 ##      RETURNS:  nothing
1116 ##  DESCRIPTION:  process this incoming message
1117 ##===============================================================================
1118 #sub here_i_am {
1119 #    my ($msg_hash) = @_;
1121 #    my $source = &get_content_from_xml_hash($msg_hash, "source");
1122 #    my $mac_address = (&get_content_from_xml_hash($msg_hash, "mac_address"))[0]; 
1123 #    my $out_hash;
1125 #    # number of known clients
1126 #    my $nu_clients = keys %$known_clients;
1128 #    # check wether client address or mac address is already known
1129 #    if (exists $known_clients->{$source}) {
1130 #        daemon_log("WARNING: $source is already known as a client", 1);
1131 #        daemon_log("WARNING: values for $source are being overwritten", 1);   
1132 #        $nu_clients --;
1133 #    }
1135 #    # number of actual activ clients
1136 #    my $act_nu_clients = $nu_clients;
1138 #    daemon_log("number of actual activ clients: $act_nu_clients", 5);
1139 #    daemon_log("number of maximal allowed clients: $max_clients", 5);
1141 #    if($max_clients <= $act_nu_clients) {
1142 #        my $out_hash = &create_xml_hash("denied", $server_address, $source);
1143 #        &add_content2xml_hash($out_hash, "denied", "I_cannot_take_any_more_clients!");
1144 #        my $passwd = (&get_content_from_xml_hash($msg_hash, "new_passwd"))[0];
1145 #        &send_msg_hash2address($out_hash, $source, $passwd);
1146 #        return;
1147 #    }
1148 #    
1149 #    # new client accepted
1150 #    my $new_passwd = (&get_content_from_xml_hash($msg_hash, "new_passwd"))[0];
1152 #    # create known_daemons entry
1153 #    my $events = (&get_content_from_xml_hash($msg_hash, "events"))[0];
1154 #    &create_known_client($source);
1155 #    &add_content2known_clients(hostname=>$source, events=>$events, mac_address=>$mac_address, 
1156 #                                status=>"registered", passwd=>$new_passwd);
1158 #    # return acknowledgement to client
1159 #    $out_hash = &create_xml_hash("registered", $server_address, $source);
1160 #    &send_msg_hash2address($out_hash, $source);
1162 #    # notify registered client to bus
1163 #    $out_hash = &create_xml_hash("new_client", $server_address, $bus_address, $source);
1164 #    &send_msg_hash2bus($out_hash);
1166 #    # give the new client his ldap config
1167 #    &new_ldap_config($source);
1169 #    return;
1170 #}
1173 #===  FUNCTION  ================================================================
1174 #         NAME:  who_has
1175 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
1176 #      RETURNS:  nothing 
1177 #  DESCRIPTION:  process this incoming message
1178 #===============================================================================
1179 #sub who_has {
1180 #    my ($msg_hash) = @_ ;
1181 #    
1182 #    # what is your search pattern
1183 #    my $search_pattern = (&get_content_from_xml_hash($msg_hash, "who_has"))[0];
1184 #    my $search_element = (&get_content_from_xml_hash($msg_hash, $search_pattern))[0];
1185 #    daemon_log("who_has-msg looking for $search_pattern $search_element", 7);
1187 #    # scanning known_clients for search_pattern
1188 #    my @host_addresses = keys %$known_clients;
1189 #    my $known_clients_entries = length @host_addresses;
1190 #    my $host_address;
1191 #    foreach my $host (@host_addresses) {
1192 #        my $client_element = $known_clients->{$host}->{$search_pattern};
1193 #        if ($search_element eq $client_element) {
1194 #            $host_address = $host;
1195 #            last;
1196 #        }
1197 #    }
1198 #        
1199 #    # search was successful
1200 #    if (defined $host_address) {
1201 #        my $source = @{$msg_hash->{source}}[0];
1202 #        my $out_msg = &create_xml_hash("who_has_i_do", $server_address, $source, "mac_address");
1203 #        &add_content2xml_hash($out_msg, "mac_address", $search_element);
1204 #        &send_msg_hash2address($out_msg, $bus_address);
1205 #    }
1206 #    return;
1207 #}
1210 #sub who_has_i_do {
1211 #    my ($msg_hash) = @_ ;
1212 #    my $header = &get_content_from_xml_hash($msg_hash, "header");
1213 #    my $source = &get_content_from_xml_hash($msg_hash, "source");
1214 #    my $search_param = (&get_content_from_xml_hash($msg_hash, $header))[0];
1215 #    my $search_value = (&get_content_from_xml_hash($msg_hash, $search_param))[0];
1216 #    print "\ngot msg $header:\nserver $source has client with $search_param $search_value\n";
1217 #}
1220 #===  FUNCTION  ================================================================
1221 #         NAME:  update_status
1222 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
1223 #      RETURNS:  nothing
1224 #  DESCRIPTION:  process this incoming message
1225 #===============================================================================
1226 #sub update_status {
1227 #    my ($msg_hash) = @_;
1228 #    my $header = &get_content_from_xml_hash($msg_hash, "header");
1229 #    my $source = &get_content_from_xml_hash($msg_hash, "source");
1230 #    my $new_status = (&get_content_from_xml_hash($msg_hash, "update_status"))[0];
1231 #    
1232 #    # find the source
1233 #    my $act_known_hash;
1234 #    if (exists $known_daemons->{$source}) {
1235 #        
1236 #        &add_content2known_daemons(hostname=>$source, status=>$new_status);
1237 #    } elsif (exists $known_clients->{$source}) {
1238 #        &update_known_clients(hostname=>$source, status=>$new_status);
1239 #        #&add_content2known_clients(hostname=>$source, status=>$new_status);
1240 #    } else {
1241 #        daemon_log("ERROR: got $header-msg, but cannot find $source in my hashes, unable to update status", 1);
1242 #        return;
1243 #    }
1245 #   return;
1246 #}
1249 ##===  FUNCTION  ================================================================
1250 ##         NAME:  new_ldap_config
1251 ##   PARAMETERS:  address - string - ip address and port of a host
1252 ##      RETURNS:  nothing
1253 ##  DESCRIPTION:  send to address the ldap configuration found for dn gotoLdapServer
1254 ##===============================================================================
1255 #sub new_ldap_config {
1256 #    my ($address) = @_ ;
1257 #    
1258 #    if (not exists $known_clients->{$address}) {
1259 #        daemon_log("ERROR: $address does not exist in known_clients, cannot send him his ldap config", 1);
1260 #        return;
1261 #    }
1262 #    
1263 #    my $mac_address = $known_clients->{$address}->{"mac_address"};
1264 #    if (not defined $mac_address) {
1265 #        daemon_log("ERROR: no mac address found for client $address", 1);
1266 #        return;
1267 #    }
1269 #    # fetch dn
1270 #    my $goHard_cmd = "ldapsearch -x '(&(objectClass=goHard)(macAddress=00:11:22:33:44:57))' dn gotoLdapServer";
1271 #    my $dn;
1272 #    my @gotoLdapServer;
1273 #    open (PIPE, "$goHard_cmd 2>&1 |");
1274 #    while(<PIPE>) {
1275 #        chomp $_;
1276 #        # If it's a comment, goto next
1277 #        if ($_ =~ m/^[#]/) { next;}
1278 #        if ($_ =~ m/^dn: ([\S]+?)$/) {
1279 #            $dn = $1;
1280 #        } elsif ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
1281 #            push(@gotoLdapServer, $1);
1282 #        }
1283 #    }
1284 #    close(PIPE);
1285 #    
1286 #    # no dn found
1287 #    if (not defined $dn) {
1288 #        daemon_log("ERROR: no dn arose from command: $goHard_cmd", 1);
1289 #        return;
1290 #    }
1291 #    
1292 #    # no gotoLdapServer found
1293 #    my $gosaGroupOfNames_cmd = "ldapsearch -x '(&(objectClass=gosaGroupOfNames)(member=$dn))' gotoLdapServer";
1294 #    if (@gotoLdapServer == 0) {
1295 #        open (PIPE, "$gosaGroupOfNames_cmd 2>&1 |");
1296 #        while(<PIPE>) {
1297 #            chomp $_;
1298 #            if ($_ =~ m/^[#]/) { next; }
1299 #            if ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
1300 #                push(@gotoLdapServer, $1);
1301 #            }
1302 #        }
1303 #        close(PIPE);
1304 #    }
1306 #    # still no gotoLdapServer found
1307 #    if (@gotoLdapServer == 0) {
1308 #        daemon_log("ERROR: cannot find gotoLdapServer entry in command: $gosaGroupOfNames_cmd", 1);
1309 #        return;
1310 #    }
1312 #    # sort @gotoLdapServer and then split of ranking
1313 #    my @sorted_gotoLdapServer = sort(@gotoLdapServer);
1314 #    @gotoLdapServer = reverse(@sorted_gotoLdapServer);
1315 #    foreach (@gotoLdapServer) {
1316 #        $_ =~ s/^\d://;
1317 #    }
1319 #    my $t = join(" ", @gotoLdapServer);
1320
1321 #    my $out_hash = &create_xml_hash("new_ldap_config", $server_address, $address);
1322 #    map(&add_content2xml_hash($out_hash, "new_ldap_config", $_), @gotoLdapServer);
1323 #    &send_msg_hash2address($out_hash, $address);
1325 #    return;
1326 #}
1329 ##===  FUNCTION  ================================================================
1330 ##         NAME:  execute_actions
1331 ##   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
1332 ##      RETURNS:  nothing
1333 ##  DESCRIPTION:  invokes the script specified in msg_hash which is located under
1334 ##                /etc/gosad/actions
1335 ##===============================================================================
1336 #sub execute_actions {
1337 #    my ($msg_hash) = @_ ;
1338 #    my $configdir= '/etc/gosad/actions/';
1339 #    my $result;
1341 #    my $header = &get_content_from_xml_hash($msg_hash, 'header');
1342 #    my $source = &get_content_from_xml_hash($msg_hash, 'source');
1343 #    my $target = &get_content_from_xml_hash($msg_hash, 'target');
1346 #    if((not defined $source)
1347 #            && (not defined $target)
1348 #            && (not defined $header)) {
1349 #        daemon_log("ERROR: Entries missing in XML msg for gosad actions under /etc/gosad/actions");
1350 #    } else {
1351 #        my $parameters="";
1352 #        my @params = &get_content_from_xml_hash($msg_hash, $header);
1353 #        my $params = join(", ", @params);
1354 #        daemon_log("execute_actions: got parameters: $params", 5);
1356 #        if (@params) {
1357 #            foreach my $param (@params) {
1358 #                my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
1359 #                daemon_log("execute_actions: parameter -> value: $param -> $param_value", 7);
1360 #                $parameters.= " ".$param_value;
1361 #            }
1362 #        }
1364 #        my $cmd= $configdir.$header."$parameters";
1365 #        daemon_log("execute_actions: executing cmd: $cmd", 7);
1366 #        $result= "";
1367 #        open(PIPE, "$cmd 2>&1 |");
1368 #        while(<PIPE>) {
1369 #            $result.=$_;
1370 #        }
1371 #        close(PIPE);
1372 #    }
1374 #    # process the event result
1377 #    return;
1378 #}
1381 #===  FUNCTION  ================================================================
1382 #         NAME:  print_known_daemons
1383 #   PARAMETERS:  nothing
1384 #      RETURNS:  nothing
1385 #  DESCRIPTION:  nomen est omen
1386 #===============================================================================
1387 sub print_known_daemons {
1388     my ($tmp) = @_ ;
1389     print "####################################\n";
1390     print "# status of known_daemons\n";
1391     $shmda->shlock(LOCK_EX);
1392     my @hosts = keys %$known_daemons;
1393     foreach my $host (@hosts) {
1394         my $status = $known_daemons->{$host}->{status} ;
1395         my $passwd = $known_daemons->{$host}->{passwd};
1396         my $timestamp = $known_daemons->{$host}->{timestamp};
1397         print "$host\n";
1398         print "\tstatus:    $status\n";
1399         print "\tpasswd:    $passwd\n";
1400         print "\ttimestamp: $timestamp\n";
1401     }
1402     $shmda->shunlock(LOCK_EX);
1403     print "####################################\n";
1404     return;
1408 #===  FUNCTION  ================================================================
1409 #         NAME:  create_known_daemon
1410 #   PARAMETERS:  hostname - string - key for the hash known_daemons
1411 #      RETURNS:  nothing
1412 #  DESCRIPTION:  creates a dummy entry for hostname in known_daemons
1413 #===============================================================================
1414 sub create_known_daemon {
1415     my ($hostname) = @_;
1416     $shmda->shlock(LOCK_EX);
1417     $known_daemons->{$hostname} = {};
1418     $known_daemons->{$hostname}->{status} = "none";
1419     $known_daemons->{$hostname}->{passwd} = "none";
1420     $known_daemons->{$hostname}->{timestamp} = "none";
1421     $shmda->shunlock(LOCK_EX); 
1422     return;  
1426 #===  FUNCTION  ================================================================
1427 #         NAME:  add_content2known_daemons
1428 #   PARAMETERS:  hostname - string - ip address and port of host (required)
1429 #                status - string - (optional)
1430 #                passwd - string - (optional)
1431 #                mac_address - string - mac address of host (optional)
1432 #      RETURNS:  nothing
1433 #  DESCRIPTION:  nome est omen and updates each time the timestamp of hostname
1434 #===============================================================================
1435 sub add_content2known_daemons {
1436     my $arg = {
1437         hostname => undef, status => undef, passwd => undef,
1438         mac_address => undef, events => undef, 
1439         @_ };
1440     my $hostname = $arg->{hostname};
1441     my $status = $arg->{status};
1442     my $passwd = $arg->{passwd};
1443     my $mac_address = $arg->{mac_address};
1444     my $events = $arg->{events};
1446     if (not defined $hostname) {
1447         daemon_log("ERROR: function add_content2known_daemons is not invoked with requiered parameter 'hostname'", 1);
1448         return;
1449     }
1451     my ($seconds, $minutes, $hours, $monthday, $month,
1452     $year, $weekday, $yearday, $sommertime) = localtime(time);
1453     $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1454     $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1455     $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1456     $month+=1;
1457     $month = $month < 10 ? $month = "0".$month : $month;
1458     $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1459     $year+=1900;
1460     my $t = "$year$month$monthday$hours$minutes$seconds";
1461     
1462     $shmda->shlock(LOCK_EX);
1463     if (defined $status) {
1464         $known_daemons->{$hostname}->{status} = $status;
1465     }
1466     if (defined $passwd) {
1467         $known_daemons->{$hostname}->{passwd} = $passwd;
1468     }
1469     if (defined $mac_address) {
1470         $known_daemons->{$hostname}->{mac_address} = $mac_address;
1471     }
1472     if (defined $events) {
1473         $known_daemons->{$hostname}->{events} = $events;
1474     }
1475     $known_daemons->{$hostname}->{timestamp} = $t;
1476     $shmda->shlock(LOCK_EX);
1477     return;
1481 #===  FUNCTION  ================================================================
1482 #         NAME:  update_known_daemons
1483 #   PARAMETERS:  hostname - string - ip address and port of host (required)
1484 #                status - string - (optional)
1485 #                passwd - string - (optional)
1486 #                client - string - ip address and port of client (optional)
1487 #      RETURNS:  nothing
1488 #  DESCRIPTION:  nome est omen and updates each time the timestamp of hostname
1489 #===============================================================================
1490 sub update_known_daemons {
1491     my $arg = {
1492         hostname => undef, status => undef, passwd => undef,
1493         @_ };
1494     my $hostname = $arg->{hostname};
1495     my $status = $arg->{status};
1496     my $passwd = $arg->{passwd};
1498     if (not defined $hostname) {
1499         daemon_log("ERROR: function add_content2known_daemons is not invoked with requiered parameter 'hostname'", 1);
1500         return;
1501     }
1503     my ($seconds, $minutes, $hours, $monthday, $month,
1504     $year, $weekday, $yearday, $sommertime) = localtime(time);
1505     $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1506     $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1507     $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1508     $month+=1;
1509     $month = $month < 10 ? $month = "0".$month : $month;
1510     $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1511     $year+=1900;
1512     my $t = "$year$month$monthday$hours$minutes$seconds";
1514     $shmda->shlock(LOCK_EX);
1515     if (defined $status) {
1516         $known_daemons->{$hostname}->{status} = $status;
1517     }
1518     if (defined $passwd) {
1519         $known_daemons->{$hostname}->{passwd} = $passwd;
1520     }
1521     $known_daemons->{$hostname}->{timestamp} = $t;
1522     $shmda->shunlock(LOCK_EX);
1523     return;
1527 #===  FUNCTION  ================================================================
1528 #         NAME:  print_known_clients 
1529 #   PARAMETERS:  nothing
1530 #      RETURNS:  nothing
1531 #  DESCRIPTION:  nomen est omen
1532 #===============================================================================
1533 sub print_known_clients {
1535     print "####################################\n";
1536     print "# status of known_clients\n";
1537     $shmcl->shlock(LOCK_EX);
1538     my @hosts = keys %$known_clients;
1539     if (@hosts) {
1540         foreach my $host (@hosts) {
1541             my $status = $known_clients->{$host}->{status} ;
1542             my $passwd = $known_clients->{$host}->{passwd};
1543             my $timestamp = $known_clients->{$host}->{timestamp};
1544             my $mac_address = $known_clients->{$host}->{mac_address};
1545             my $events = $known_clients->{$host}->{events};
1546             print "$host\n";
1547             print "\tstatus:      $status\n";
1548             print "\tpasswd:      $passwd\n";
1549             print "\ttimestamp:   $timestamp\n";
1550             print "\tmac_address: $mac_address\n";
1551             print "\tevents:      $events\n";
1552         }
1553     }
1554     $shmcl->shunlock(LOCK_EX);
1555     print "####################################\n";
1556     return;
1560 #===  FUNCTION  ================================================================
1561 #         NAME:  create_known_client
1562 #   PARAMETERS:  hostname - string - key for the hash known_clients
1563 #      RETURNS:  nothing
1564 #  DESCRIPTION:  creates a dummy entry for hostname in known_clients
1565 #===============================================================================
1566 sub create_known_client {
1567     my ($hostname) = @_;
1568     $shmcl->shlock(LOCK_EX);
1569     $known_clients->{$hostname} = {};
1570     $known_clients->{$hostname}->{status} = "none";
1571     $known_clients->{$hostname}->{passwd} = "none";
1572     $known_clients->{$hostname}->{timestamp} = "none";
1573     $known_clients->{$hostname}->{mac_address} = "none";
1574     $known_clients->{$hostname}->{events} = "none";
1575     $shmcl->shunlock(LOCK_EX); 
1576     return;  
1580 #===  FUNCTION  ================================================================
1581 #         NAME:  add_content2known_clients
1582 #   PARAMETERS:  hostname - string - ip address and port of host (required)
1583 #                status - string - (optional)
1584 #                passwd - string - (optional)
1585 #                mac_address - string - (optional)
1586 #                events - string - event of client, executable skripts under /etc/gosac/events
1587 #      RETURNS:  nothing
1588 #  DESCRIPTION:  nome est omen and updates each time the timestamp of hostname
1589 #===============================================================================
1590 sub add_content2known_clients {
1591     my $arg = {
1592         hostname => undef, status => undef, passwd => undef,
1593         mac_address => undef, events => undef, 
1594         @_ };
1595     my $hostname = $arg->{hostname};
1596     my $status = $arg->{status};
1597     my $passwd = $arg->{passwd};
1598     my $mac_address = $arg->{mac_address};
1599     my $events = $arg->{events};
1601     if (not defined $hostname) {
1602         daemon_log("ERROR: function add_content2known_clients is not invoked with requiered parameter 'hostname'", 1);
1603         return;
1604     }
1606     my ($seconds, $minutes, $hours, $monthday, $month,
1607     $year, $weekday, $yearday, $sommertime) = localtime(time);
1608     $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1609     $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1610     $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1611     $month+=1;
1612     $month = $month < 10 ? $month = "0".$month : $month;
1613     $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1614     $year+=1900;
1615     my $t = "$year$month$monthday$hours$minutes$seconds";
1616     
1617     $shmcl->shlock(LOCK_EX);
1618     if (defined $status) {
1619         $known_clients->{$hostname}->{status} = $status;
1620     }
1621     if (defined $passwd) {
1622         $known_clients->{$hostname}->{passwd} = $passwd;
1623     }
1624     if (defined $mac_address) {
1625         $known_clients->{$hostname}->{mac_address} = $mac_address;
1626     }
1627     if (defined $events) {
1628         $known_clients->{$hostname}->{events} = $events;
1629     }
1630     $known_clients->{$hostname}->{timestamp} = $t;
1631     $shmcl->shlock(LOCK_EX);
1632     return;
1634  
1636 #===  FUNCTION  ================================================================
1637 #         NAME:  
1638 #   PARAMETERS:  
1639 #      RETURNS:  
1640 #  DESCRIPTION:  
1641 #===============================================================================    
1642 sub clean_up_known_clients {
1643     my ($address) = @_ ;
1644     
1645     if (not exists $known_clients->{$address}) {
1646         daemon_log("cannot prune known_clients from $address, client not known", 5);
1647         return;
1648     }
1650     delete $known_clients->{$address};
1652     # send bus a msg that address was deleted from known_clients
1653     my $out_hash = &create_xml_hash('delete_client', $server_address, $bus_address, $address);
1654     &send_msg_hash2bus($out_hash);
1656     daemon_log("client $address deleted from known_clients because of multiple down time", 3);
1657     return;
1661 #===  FUNCTION  ================================================================
1662 #         NAME:  update_known_clients
1663 #   PARAMETERS:  hostname - string - ip address and port of host (required)
1664 #                status - string - (optional)
1665 #                passwd - string - (optional)
1666 #                client - string - ip address and port of client (optional)
1667 #      RETURNS:  nothing
1668 #  DESCRIPTION:  nome est omen and updates each time the timestamp of hostname
1669 #===============================================================================
1670 sub update_known_clients {
1671     my $arg = {
1672         hostname => undef, status => undef, passwd => undef,
1673         mac_address => undef, events => undef,
1674         @_ };
1675     my $hostname = $arg->{hostname};
1676     my $status = $arg->{status};
1677     my $passwd = $arg->{passwd};
1678     my $mac_address = $arg->{mac_address};
1679     my $events = $arg->{events};
1681     if (not defined $hostname) {
1682         daemon_log("ERROR: function add_content2known_daemons is not invoked with requiered parameter 'hostname'", 1);
1683         return;
1684     }
1686     my ($seconds, $minutes, $hours, $monthday, $month,
1687     $year, $weekday, $yearday, $sommertime) = localtime(time);
1688     $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1689     $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1690     $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1691     $month+=1;
1692     $month = $month < 10 ? $month = "0".$month : $month;
1693     $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1694     $year+=1900;
1695     my $t = "$year$month$monthday$hours$minutes$seconds";
1697     $shmcl->shlock(LOCK_EX);
1698     if (defined $status) {
1699         $known_clients->{$hostname}->{status} = $status;
1700     }
1701     if (defined $passwd) {
1702         $known_clients->{$hostname}->{passwd} = $passwd;
1703     }
1704     if (defined $mac_address) {
1705         $known_clients->{$hostname}->{mac_address} = $mac_address; 
1706     }
1707     if (defined $events) {
1708         $known_clients->{$hostname}->{events} = $events;
1709     }
1710     $known_clients->{$hostname}->{timestamp} = $t;
1711     $shmcl->shunlock(LOCK_EX);
1712     return;
1721 #==== MAIN = main ==============================================================
1723 #  parse commandline options
1724 Getopt::Long::Configure( "bundling" );
1725 GetOptions("h|help" => \&usage,
1726         "c|config=s" => \$cfg_file,
1727         "f|foreground" => \$foreground,
1728         "v|verbose+" => \$verbose,
1729         "no-bus+" => \$no_bus,
1730         "no-arp+" => \$no_arp,
1731            );
1733 #  read and set config parameters
1734 &check_cmdline_param ;
1735 &read_configfile;
1736 &check_pid;
1737 &import_modules;
1739 $SIG{CHLD} = 'IGNORE';
1741 # restart daemon log file
1742 if(-e $log_file ) { unlink $log_file }
1743 daemon_log(" ", 1);
1744 daemon_log("gosa-si-server started!", 1);
1746 # Just fork, if we"re not in foreground mode
1747 if( ! $foreground ) { $pid = fork(); }
1748 else { $pid = $$; }
1750 # Do something useful - put our PID into the pid_file
1751 if( 0 != $pid ) {
1752     open( LOCK_FILE, ">$pid_file" );
1753     print LOCK_FILE "$pid\n";
1754 close( LOCK_FILE );
1755     if( !$foreground ) { exit( 0 ) };
1758 # detect own ip and mac address
1759 ($server_ip, $server_mac_address) = &get_ip_and_mac(); 
1760 if (not defined $server_ip) {
1761     die "EXIT: ip address of $0 could not be detected";
1763 daemon_log("server ip address detected: $server_ip", 1);
1764 daemon_log("server mac address detected: $server_mac_address", 1);
1766 # setup xml parser
1767 $xml = new XML::Simple();
1769 # create cipher object
1770 $bus_cipher = &create_ciphering($bus_passwd);
1771 $bus_address = "$bus_ip:$bus_port";
1773 # create reading and writing vectors
1774 my $rbits = my $wbits = my $ebits = "";
1776 # open server socket
1777 $server_address = "$server_ip:$server_port";
1778 if($server_activ eq "on"){
1779     daemon_log(" ", 1);
1780     $server = IO::Socket::INET->new(LocalPort => $server_port,
1781             Type => SOCK_STREAM,
1782             Reuse => 1,
1783             Listen => 20,
1784             ); 
1785     if(not defined $server){
1786         daemon_log("cannot be a tcp server at $server_port : $@");
1787     } else {
1788         daemon_log("start server: $server_address", 1);
1789         vec($rbits, fileno $server, 1) = 1;
1790         vec($wbits, fileno $server, 1) = 1;
1791     }
1794 # register at bus
1795 if ($no_bus > 0) {
1796     $bus_activ = "off"
1798 if($bus_activ eq "on") {
1799     daemon_log(" ", 1);
1800     &register_at_bus();
1804 # start arp fifo
1805 if ($no_arp > 0) {
1806     $arp_activ = "off";
1808 my $my_fifo;
1809 if($arp_activ eq "on") {
1810     daemon_log(" ", 1);
1811     $my_fifo = &open_fifo($arp_fifo_path);
1812     if($my_fifo == 0) { die "fifo file disappeared\n" }
1813     sysopen($arp_fifo, $arp_fifo_path, O_RDWR) or die "can't read from $arp_fifo: $!" ;
1814     
1815     vec($rbits, fileno $arp_fifo, 1) = 1;
1818 $gosa_address = "$gosa_ip:$gosa_port";
1819 # start gosa inferface fifos
1820 if ($gosa_activ eq "on") {
1821     daemon_log(" ",1);
1822     $gosa_server = IO::Socket::INET->new(LocalPort => $gosa_port,
1823             Type => SOCK_STREAM,
1824             Reuse => 1,
1825             Listen => 1,
1826             );
1827     if (not defined $gosa_server) {
1828         daemon_log("cannot start tcp server at $gosa_port for communication to gosa: $@", 1);
1829     } else {
1830         daemon_log("start server at for communication to gosa: $gosa_address", 1);
1831         vec($rbits, fileno $gosa_server, 1) = 1;
1832         
1833     }
1837 ###################################
1838 #everything ready, okay, lets start
1839 ###################################
1840 while(1) {
1842     # add all handles from the childs
1843     while ( my ($pid, $child_hash) = each %busy_child ) {
1844         
1845         # check whether process still exists
1846         my $exitus_pid = waitpid($pid, WNOHANG);
1847         if($exitus_pid != 0) {
1848             delete $busy_child{$pid};
1849             next;
1850         }
1851      
1852         # add child fhd to the listener    
1853         my $fhd = $$child_hash{'pipe_rd'};
1854         vec($rbits, fileno $fhd, 1) = 1;
1855     }
1857     my ($rout, $wout);
1858     my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
1860     # error handling
1861     if($nf < 0 ) {
1862     }
1864     # something is coming in
1865     if($server_activ eq "on" && vec($rout, fileno $server, 1)) {
1866         daemon_log(" ", 1);
1867         my $client = $server->accept();
1868         my $other_end = getpeername($client);
1869         if(not defined $other_end) {
1870             daemon_log("client cannot be identified: $!");
1871         } else {
1872             my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1873             my $actual_ip = inet_ntoa($iaddr);
1874             daemon_log("accept client at daemon socket from $actual_ip", 5);
1875             my $in_msg = &read_from_socket($client);
1876             if(defined $in_msg){
1877                 chomp($in_msg);
1878                 &activating_child($in_msg, $actual_ip);
1879             } else {
1880                 daemon_log("cannot read from $actual_ip", 5);
1881             }
1882         }
1883         close($client);
1884     }
1886     if($arp_activ eq "on" && vec($rout, fileno $arp_fifo, 1)) {
1887         my $in_msg = <$arp_fifo>;
1888         chomp($in_msg);
1889         print "arp_activ: msg: $in_msg\n";
1890         my $act_passwd = $known_daemons->{$bus_address}->{passwd};
1891         print "arp_activ: arp_passwd: $act_passwd\n";
1893         my $in_msg_hash = $xml->XMLin($in_msg, ForceArray=>1);
1895         my $target = &get_content_from_xml_hash($in_msg_hash, 'target');
1897         if ($target eq $server_address) { 
1898              print "arp_activ: forward to server\n";
1899             my $arp_cipher = &create_ciphering($act_passwd);
1900             my $crypted_msg = &encrypt_msg($in_msg, $arp_cipher);
1901             &activating_child($crypted_msg, $server_ip);
1902         } else {
1903             print "arp_activ: send to bus\n";
1904             &send_msg_hash2address($in_msg_hash, $bus_address);
1905         }
1906         print "\n";
1907     }
1909     if($gosa_activ eq "on" && vec($rout, fileno $gosa_server, 1)) {
1910         daemon_log(" ", 1);
1911         my $client = $gosa_server->accept();
1912         my $other_end = getpeername($client);
1913         if(not defined $other_end) {
1914             daemon_log("client cannot be identified: $!");
1915         } else {
1916             my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1917             my $actual_ip = inet_ntoa($iaddr);
1918             daemon_log("accept client at gosa socket from $actual_ip", 5);
1919             my $in_msg = <$client>;
1920             #my $in_msg = &read_from_socket($client);
1921             
1922             if(defined $in_msg){
1923                 chomp($in_msg);
1924                 &activating_child($in_msg, $actual_ip, $client);
1925             } else {
1926                 daemon_log("cannot read from $actual_ip", 5);
1927             }
1928         }
1929         #close($client);
1930     }
1932     # check all processing childs whether they are finished ('done') or 
1933     while ( my ($pid, $child_hash) = each %busy_child ) {
1934         my $fhd = $$child_hash{'pipe_rd'};
1936         if (vec($rout, fileno $fhd, 1) ) {
1937             daemon_log("process child $pid is ready to read", 5);
1939             $fhd->blocking(1);
1940             my $in_msg = <$fhd>;
1941             $fhd->blocking(0);
1942             my $part_in_msg;
1943             while ($part_in_msg = <$fhd>) {
1944                 if (not defined $part_in_msg) {
1945                     last;
1946                 }
1947                 $in_msg .= $part_in_msg;
1948             }
1949             chomp($in_msg);
1951             daemon_log("process child read: $in_msg", 5);
1952             if (not defined $in_msg) { 
1953                 next; 
1954             } elsif ($in_msg =~ "done") {
1955                 delete $busy_child{$pid};
1956                 $free_child{$pid} = $child_hash;
1957  
1958             } else {
1959                 my $act_client = $busy_child{$pid}{client_ref};
1960                 print $act_client $in_msg."\n";
1961                 my $act_pipe = $busy_child{$pid}{pipe_rd};
1962                 sleep(10);
1963                 close ($act_client);   
1964                 delete $busy_child{$pid};
1965                 $free_child{$pid} = $child_hash;
1967             }
1968         }
1969     }