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" ;
148 }
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 }
174 }
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;
211 }
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 }
236 }
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 }
285 }
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);
311 }
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);
352 }
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;
376 }
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";
404 exit(1);
405 }
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;
427 }
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();
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 }
566 }
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);
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 }
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) }
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);
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;
718 }
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;
745 }
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;
763 }
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;
786 }
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
810 }
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;
826 }
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;
845 }
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;
863 }
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;
880 }
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;
896 }
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;
915 }
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");
932 # generate xml string
933 my $msg_xml = &create_xml_string($msg_hash);
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};
942 } else {
943 daemon_log("$address not known, neither as server nor as client", 1);
944 return 1;
945 }
946 }
948 # create ciphering object
949 my $act_cipher = &create_ciphering($passwd);
951 # encrypt xml msg
952 my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
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 }
973 # send xml msg
974 print $socket $crypted_msg."\n";
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;
993 }
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 }
1021 # send xml msg
1022 print $socket $crypted_msg."\n";
1024 close $socket;
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;
1033 }
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];
1052 #
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);
1058 #
1059 # } elsif (exists $known_clients->{$source}) {
1060 # &add_content2known_clients(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
1061 #
1062 # } else {
1063 # daemon_log("ERROR: $source not known, neither in known_daemons nor in known_clients", 1)
1064 # }
1065 #
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) = @_;
1078 #
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) = @_;
1120 #
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;
1124 #
1125 # # number of known clients
1126 # my $nu_clients = keys %$known_clients;
1127 #
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 # }
1134 #
1135 # # number of actual activ clients
1136 # my $act_nu_clients = $nu_clients;
1137 #
1138 # daemon_log("number of actual activ clients: $act_nu_clients", 5);
1139 # daemon_log("number of maximal allowed clients: $max_clients", 5);
1140 #
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];
1151 #
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);
1157 #
1158 # # return acknowledgement to client
1159 # $out_hash = &create_xml_hash("registered", $server_address, $source);
1160 # &send_msg_hash2address($out_hash, $source);
1161 #
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);
1165 #
1166 # # give the new client his ldap config
1167 # &new_ldap_config($source);
1168 #
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);
1186 #
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 # }
1244 #
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 # }
1268 #
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 # }
1305 #
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 # }
1311 #
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 # }
1318 #
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);
1324 #
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;
1340 #
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');
1344 #
1345 #
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);
1355 #
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 # }
1363 #
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 # }
1373 #
1374 # # process the event result
1375 #
1376 #
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;
1405 }
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;
1423 }
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";
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;
1478 }
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;
1524 }
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;
1557 }
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;
1577 }
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";
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;
1633 }
1636 #=== FUNCTION ================================================================
1637 # NAME:
1638 # PARAMETERS:
1639 # RETURNS:
1640 # DESCRIPTION:
1641 #===============================================================================
1642 sub clean_up_known_clients {
1643 my ($address) = @_ ;
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;
1658 }
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;
1713 }
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 ) };
1756 }
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";
1762 }
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 }
1792 }
1794 # register at bus
1795 if ($no_bus > 0) {
1796 $bus_activ = "off"
1797 }
1798 if($bus_activ eq "on") {
1799 daemon_log(" ", 1);
1800 ®ister_at_bus();
1801 }
1804 # start arp fifo
1805 if ($no_arp > 0) {
1806 $arp_activ = "off";
1807 }
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: $!" ;
1815 vec($rbits, fileno $arp_fifo, 1) = 1;
1816 }
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;
1833 }
1834 }
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 ) {
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 }
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);
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;
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 }
1972 }