8ae06b0d018d11a221a42b7a3898d4785a14339e
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/gosad/modules";
43 my $modules_path = "/etc/gosad/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, "tester"],
109 "bus_ip" => [\$bus_ip, "10.89.1.155"],
110 "bus_port" => [\$bus_port, "10001"],
111 },
112 "server" =>
113 {"server_activ" => [\$server_activ, "on"],
114 "server_ip" => [\$server_ip, ""],
115 "server_port" => [\$server_port, ""],
116 "server_passwd" => [\$server_passwd, ""],
117 "max_clients" => [\$max_clients, 100],
118 },
120 "arp" =>
121 {"arp_activ" => [\$arp_activ, "on"],
122 "arp_fifo_path" => [\$arp_fifo_path, "/tmp/arp_fifo"],
123 },
124 "gosa" =>
125 {"gosa_activ" => [\$gosa_activ, "on"],
126 "gosa_ip" => [\$gosa_ip, "10.89.1.155"],
127 "gosa_port" => [\$gosa_port, "9999"],
128 "gosa_passwd" => [\$gosa_passwd, "none"],
129 },
130 );
133 #=== FUNCTION ================================================================
134 # NAME: usage
135 # PARAMETERS: nothing
136 # RETURNS: nothing
137 # DESCRIPTION: print out usage text to STDERR
138 #===============================================================================
139 sub usage {
140 print STDERR << "EOF" ;
141 usage: $0 [-hvf] [-c config]
143 -h : this (help) message
144 -c <file> : config file
145 -f : foreground, process will not be forked to background
146 -v : be verbose (multiple to increase verbosity)
147 EOF
148 print "\n" ;
149 }
152 #=== FUNCTION ================================================================
153 # NAME: read_configfile
154 # PARAMETERS: cfg_file - string -
155 # RETURNS: nothing
156 # DESCRIPTION: read cfg_file and set variables
157 #===============================================================================
158 sub read_configfile {
159 my $cfg;
160 if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
161 if( -r $cfg_file ) {
162 $cfg = Config::IniFiles->new( -file => $cfg_file );
163 } else {
164 print STDERR "Couldn't read config file!";
165 }
166 } else {
167 $cfg = Config::IniFiles->new() ;
168 }
169 foreach my $section (keys %cfg_defaults) {
170 foreach my $param (keys %{$cfg_defaults{ $section }}) {
171 my $pinfo = $cfg_defaults{ $section }{ $param };
172 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
173 }
174 }
175 }
178 #=== FUNCTION ================================================================
179 # NAME: logging
180 # PARAMETERS: level - string - default 'info'
181 # msg - string -
182 # facility - string - default 'LOG_DAEMON'
183 # RETURNS: nothing
184 # DESCRIPTION: function for logging
185 #===============================================================================
186 sub daemon_log {
187 # log into log_file
188 my( $msg, $level ) = @_;
189 if(not defined $msg) { return }
190 if(not defined $level) { $level = 1 }
191 if(defined $log_file){
192 open(LOG_HANDLE, ">>$log_file");
193 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
194 print STDERR "cannot open $log_file: $!";
195 return }
196 chomp($msg);
197 if($level <= $verbose){
198 print LOG_HANDLE "$level $msg\n";
199 if(defined $foreground) { print $msg."\n" }
200 }
201 }
202 close( LOG_HANDLE );
203 #log into syslog
204 # my ($msg, $level, $facility) = @_;
205 # if(not defined $msg) {return}
206 # if(not defined $level) {$level = "info"}
207 # if(not defined $facility) {$facility = "LOG_DAEMON"}
208 # openlog($0, "pid,cons,", $facility);
209 # syslog($level, $msg);
210 # closelog;
211 # return;
212 }
215 #=== FUNCTION ================================================================
216 # NAME: check_cmdline_param
217 # PARAMETERS: nothing
218 # RETURNS: nothing
219 # DESCRIPTION: validates commandline parameter
220 #===============================================================================
221 sub check_cmdline_param () {
222 my $err_config;
223 my $err_counter = 0;
224 if( not defined( $cfg_file)) {
225 #$err_config = "please specify a config file";
226 #$err_counter += 1;
227 my $cwd = getcwd;
228 my $name = "gosa-sd.cfg";
229 $cfg_file = File::Spec->catfile( $cwd, $name );
230 print STDERR "no conf file specified\n try to use default: $cfg_file\n";
231 }
232 if( $err_counter > 0 ) {
233 &usage( "", 1 );
234 if( defined( $err_config)) { print STDERR "$err_config\n"}
235 print STDERR "\n";
236 exit( -1 );
237 }
238 }
241 #=== FUNCTION ================================================================
242 # NAME: check_pid
243 # PARAMETERS: nothing
244 # RETURNS: nothing
245 # DESCRIPTION: handels pid processing
246 #===============================================================================
247 sub check_pid {
248 $pid = -1;
249 # Check, if we are already running
250 if( open(LOCK_FILE, "<$pid_file") ) {
251 $pid = <LOCK_FILE>;
252 if( defined $pid ) {
253 chomp( $pid );
254 if( -f "/proc/$pid/stat" ) {
255 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
256 if( $0 eq $stat ) {
257 close( LOCK_FILE );
258 exit -1;
259 }
260 }
261 }
262 close( LOCK_FILE );
263 unlink( $pid_file );
264 }
266 # create a syslog msg if it is not to possible to open PID file
267 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
268 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
269 if (open(LOCK_FILE, '<', $pid_file)
270 && ($pid = <LOCK_FILE>))
271 {
272 chomp($pid);
273 $msg .= "(PID $pid)\n";
274 } else {
275 $msg .= "(unable to read PID)\n";
276 }
277 if( ! ($foreground) ) {
278 openlog( $0, "cons,pid", "daemon" );
279 syslog( "warning", $msg );
280 closelog();
281 }
282 else {
283 print( STDERR " $msg " );
284 }
285 exit( -1 );
286 }
287 }
290 #=== FUNCTION ================================================================
291 # NAME: get_ip_and_mac
292 # PARAMETERS: nothing
293 # RETURNS: (ip, mac)
294 # DESCRIPTION: executes /sbin/ifconfig and parses the output, the first occurence
295 # of a inet address is returned as well as the mac address in the line
296 # above the inet address
297 #===============================================================================
298 sub get_ip_and_mac {
299 my $ip = "0.0.0.0.0"; # Defualt-IP
300 my $mac = "00:00:00:00:00:00"; # Default-MAC
301 my @ifconfig = qx(/sbin/ifconfig);
302 foreach(@ifconfig) {
303 if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
304 $mac = "$1:$2:$3:$4:$5:$6";
305 next;
306 }
307 if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
308 $ip = "$1.$2.$3.$4";
309 last;
310 }
311 }
312 return ($ip, $mac);
313 }
317 #=== FUNCTION ================================================================
318 # NAME: import_modules
319 # PARAMETERS: module_path - string - abs. path to the directory the modules are stored
320 # RETURNS: nothing
321 # DESCRIPTION: each file in module_path which ends with '.pm' is imported by "require 'file';"
322 #===============================================================================
323 sub import_modules {
324 daemon_log(" ", 1);
326 if (not -e $modules_path) {
327 daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);
328 }
330 opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
331 while (defined (my $file = readdir (DIR))) {
332 if (not $file =~ /(\S*?).pm$/) {
333 next;
334 }
335 eval { require $file; };
336 if ($@) {
337 daemon_log("ERROR: gosa-sd could not load module $file", 1);
338 daemon_log("$@", 5);
339 next;
340 }
341 my $mod_name = $1;
342 my $module_tag_hash = eval( $mod_name.'::get_module_tags()' );
343 $known_modules->{$mod_name} = $module_tag_hash;
345 daemon_log("load module $mod_name", 1);
346 }
348 # for debugging
349 #while ( my ($module, $tag_hash) = each(%$known_modules)) {
350 # print "\tmodule: $module"."\n";
351 # print "\ttags: ".join(", ", keys(%$tag_hash))."\n";
352 #}
353 close (DIR);
354 }
357 #=== FUNCTION ================================================================
358 # NAME: register_at_bus
359 # PARAMETERS: nothing
360 # RETURNS: nothing
361 # DESCRIPTION: creates an entry in known_daemons and send a 'here_i_am' msg to bus
362 #===============================================================================
363 sub register_at_bus {
365 # create known_daemons entry
366 &create_known_daemon($bus_address);
367 &add_content2known_daemons(hostname=>$bus_address, status=>"register_at_bus", passwd=>$bus_passwd);
368 daemon_log("register at bus: $bus_address", 1);
370 my $msg_hash = &create_xml_hash("here_i_am", "$server_ip:$server_port", $bus_address);
371 &send_msg_hash2address($msg_hash, $bus_address);
372 return;
373 }
376 #=== FUNCTION ================================================================
377 # NAME: sig_int_handler
378 # PARAMETERS: signal - string - signal arose from system
379 # RETURNS: noting
380 # DESCRIPTION: handels tasks to be done befor signal becomes active
381 #===============================================================================
382 sub sig_int_handler {
383 my ($signal) = @_;
384 if($server){
385 close($server);
386 daemon_log("daemon server closed", 1);
387 }
388 if( -p $arp_fifo_path ) {
389 close $arp_fifo ;
390 unlink($arp_fifo_path) ;
391 daemon_log("ARP_FIFO closed", 1) ;
392 }
394 if($gosa_server){
395 close($gosa_server);
396 daemon_log("gosa server closed", 1);
397 }
399 print STDERR "$signal\n";
401 exit(1);
402 }
403 $SIG{INT} = \&sig_int_handler;
406 #=== FUNCTION ================================================================
407 # NAME: activating_child
408 # PARAMETERS: msg - string - incoming message
409 # host - string - host from which the incomming message comes
410 # RETURNS: nothing
411 # DESCRIPTION: handels the distribution of incoming messages to working childs
412 #===============================================================================
413 sub activating_child {
414 my ($msg, $host, $client) = @_;
415 my $child = &get_processing_child();
416 my $pipe_wr = $$child{'pipe_wr'};
417 my $pipe_rd = $$child{'pipe_rd'};
418 $$child{client_ref} = $client;
419 daemon_log("activating: childpid:$$child{'pid'}", 5);
421 print $pipe_wr $msg.".".$host."\n";
423 # if (defined $client) {
424 # my $rbits = "";
425 # vec($rbits, fileno $client, 1) = 1;
426 #
427 # my ($rout);
428 # my $nf = select($rout=$rbits, undef, undef, $gosa_timeout);
429 # if($gosa_activ eq "on" && vec($rout, fileno $gosa_server, 1)) {
430 #
431 # }
432 # }
433 return;
434 }
437 #=== FUNCTION ================================================================
438 # NAME: get_processing_child
439 # PARAMETERS: nothing
440 # RETURNS: child - hash - holding the process id and the references to the pipe
441 # handles pipe_wr and pipe_rd
442 # DESCRIPTION: handels the forking, reactivating and keeping alive tasks
443 #===============================================================================
444 sub get_processing_child {
445 my $child;
446 # checking %busy_child{pipe_wr} if msg is 'done', then set child from busy to free
447 # while(my ($key, $val) = each(%busy_child)) {
448 # # test ob prozess noch existiert
449 # my $exitus_pid = waitpid($key, WNOHANG);
450 # if($exitus_pid != 0) {
451 # delete $busy_child{$key};
452 # print "prozess:$key wurde aus busy_child entfernt\n";
453 # next;
454 # }
455 #
456 # # check wether process sitll works
457 # my $fh = $$val{'pipe_rd'};
458 # $fh->blocking(0);
459 # my $child_answer;
460 # if(not $child_answer = <$fh>) { next }
461 # chomp($child_answer);
462 # if($child_answer eq "done") {
463 # delete $busy_child{$key};
464 # $free_child{$key} = $val;
465 # }
466 # }
468 while(my ($key, $val) = each(%free_child)) {
469 my $exitus_pid = waitpid($key, WNOHANG);
470 if($exitus_pid != 0) {
471 delete $free_child{$key};
472 }
473 daemon_log("free child:$key", 5);
474 }
475 # check @free_child and @busy_child
476 my $free_len = scalar(keys(%free_child));
477 my $busy_len = scalar(keys(%busy_child));
478 daemon_log("free children $free_len, busy children $busy_len", 5);
480 # if there is a free child, let the child work
481 if($free_len > 0){
482 my @keys = keys(%free_child);
483 $child = $free_child{$keys[0]};
484 if(defined $child) {
485 $busy_child{$$child{'pid'}} = $child ;
486 delete $free_child{$$child{'pid'}};
487 }
488 return $child;
489 }
491 # no free child, try to fork another one
492 if($free_len + $busy_len < $child_max) {
494 daemon_log("not enough children, create a new one", 5);
496 # New pipes for communication
497 my( $PARENT_wr, $PARENT_rd );
498 my( $CHILD_wr, $CHILD_rd );
499 pipe( $CHILD_rd, $PARENT_wr );
500 pipe( $PARENT_rd, $CHILD_wr );
501 $PARENT_wr->autoflush(1);
502 $CHILD_wr->autoflush(1);
504 ############
505 # fork child
506 ############
507 my $child_pid = fork();
509 #CHILD
510 if($child_pid == 0) {
511 # Close unused pipes
512 close( $CHILD_rd );
513 close( $CHILD_wr );
514 while( 1 ) {
515 my $rbits = "";
516 vec( $rbits, fileno $PARENT_rd , 1 ) = 1;
517 my $nf = select($rbits, undef, undef, $child_timeout);
518 if($nf < 0 ) {
519 die "select(): $!\n";
520 } elsif (! $nf) {
521 # if already child_min childs are alive, then leave loop
522 $free_len = scalar(keys(%free_child));
523 $busy_len = scalar(keys(%busy_child));
524 if($free_len + $busy_len >= $child_min) {
525 last;
526 } else {
527 redo;
528 }
529 }
531 # a job for a child arise
532 if ( vec $rbits, fileno $PARENT_rd, 1 ) {
533 # read everything from pipe
534 my $msg = "";
535 $PARENT_rd->blocking(0);
536 while(1) {
537 my $read = <$PARENT_rd>;
538 if(not defined $read) { last}
539 $msg .= $read;
540 }
542 ######################################
543 # forward msg to all imported modules
544 no strict "refs";
545 my $answer;
546 while( my ($module, $tag_hash) = each(%$known_modules)) {
547 #if(exists $known_modules->{$module}->{server_packages}) {
548 my $tmp = &{ $module."::process_incoming_msg" }($msg);
549 if (defined $tmp) {
550 $answer = $tmp;
551 }
552 #}
553 }
555 &print_known_daemons();
556 &print_known_clients();
558 daemon_log("processing of msg finished", 5);
560 if (defined $answer) {
561 print $PARENT_wr $answer."\n";
562 daemon_log("\t$answer", 5);
563 daemon_log(" ", 5);
564 } else {
565 print $PARENT_wr "done"."\n";
566 daemon_log(" ", 5);
567 }
568 redo;
569 }
570 }
571 # childs leaving the loop are allowed to die
572 exit(0);
575 #PARENT
576 } else {
577 # Close unused pipes
578 close( $PARENT_rd );
579 close( $PARENT_wr );
581 # add child to child alive hash
582 my %child_hash = (
583 'pid' => $child_pid,
584 'pipe_wr' => $CHILD_wr,
585 'pipe_rd' => $CHILD_rd,
586 'client_ref' => "",
587 );
589 $child = \%child_hash;
590 $busy_child{$$child{'pid'}} = $child;
591 return $child;
592 }
593 }
594 }
597 #=== FUNCTION ================================================================
598 # NAME: process_incoming_msg
599 # PARAMETERS: crypted_msg - string - incoming crypted message
600 # RETURNS: nothing
601 # DESCRIPTION: handels the proceeded distribution to the appropriated functions
602 #===============================================================================
603 sub process_incoming_msg {
604 my ($crypted_msg) = @_;
605 if(not defined $crypted_msg) {
606 daemon_log("function 'process_incoming_msg': got no msg", 7);
607 }
608 $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
609 $crypted_msg = $1;
610 my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
611 daemon_log("msg from host:", 1);
612 daemon_log("\t$host", 1);
613 #daemon_log("crypted msg:", 7);
614 #daemon_log("\t$crypted_msg", 7);
616 # collect addresses from possible incoming clients
617 my @valid_keys;
618 my @host_keys = keys %$known_daemons;
619 foreach my $host_key (@host_keys) {
620 if($host_key =~ "^$host") {
621 push(@valid_keys, $host_key);
622 }
623 }
624 my @client_keys = keys %$known_clients;
625 foreach my $client_key (@client_keys) {
626 if($client_key =~ "^$host"){
627 push(@valid_keys, $client_key);
628 }
629 }
630 push(@valid_keys, $server_address);
632 my $l = @valid_keys;
633 my ($msg, $msg_hash);
634 my $msg_flag = 0;
636 # determine the correct passwd for deciphering of the incoming msgs
637 foreach my $host_key (@valid_keys) {
638 eval{
639 daemon_log( "key: $host_key", 7);
640 my $key_passwd;
641 if (exists $known_daemons->{$host_key}) {
642 $key_passwd = $known_daemons->{$host_key}->{passwd};
643 } elsif (exists $known_clients->{$host_key}) {
644 $key_passwd = $known_clients->{$host_key}->{passwd};
645 } elsif ($host_key eq $server_address) {
646 $key_passwd = $server_passwd;
647 }
648 daemon_log("key_passwd: $key_passwd", 7);
649 my $key_cipher = &create_ciphering($key_passwd);
650 $msg = &decrypt_msg($crypted_msg, $key_cipher);
651 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
652 };
653 if($@) {
654 daemon_log("key raise error", 7);
655 $msg_flag += 1;
656 } else {
657 last;
658 }
659 }
661 if($msg_flag >= $l) {
662 daemon_log("ERROR: do not understand the message:", 1);
663 daemon_log("\t$msg", 1);
664 return;
665 }
667 # process incoming msg
668 my $header = &get_content_from_xml_hash($msg_hash, "header");
669 my $source = @{$msg_hash->{source}}[0];
671 daemon_log("header from msg:", 1);
672 daemon_log("\t$header", 1);
673 daemon_log("msg to process:", 5);
674 daemon_log("\t$msg", 5);
676 my @targets = @{$msg_hash->{target}};
677 my $len_targets = @targets;
678 if ($len_targets == 0){
679 daemon_log("ERROR: no target specified for msg $header", 1);
681 } elsif ($len_targets == 1){
682 # we have only one target symbol
684 my $target = $targets[0];
685 daemon_log("msg is for:", 7);
686 daemon_log("\t$target", 7);
688 if ($target eq $server_address) {
689 # msg is for server
690 if ($header eq 'new_passwd'){ &new_passwd($msg_hash)}
691 elsif ($header eq 'here_i_am') { &here_i_am($msg_hash)}
692 elsif ($header eq 'who_has') { &who_has($msg_hash) }
693 elsif ($header eq 'who_has_i_do') { &who_has_i_do($msg_hash)}
694 elsif ($header eq 'update_status') { &update_status($msg_hash) }
695 #elsif ($header eq 'got_ping') { &got_ping($msg_hash)}
696 elsif ($header eq 'get_load') { &execute_actions($msg_hash)}
697 else { daemon_log("ERROR: no function assigned to this msg", 5) }
700 } elsif ($target eq "*") {
701 # msg is for all clients
703 my @target_addresses = keys(%$known_clients);
704 foreach my $target_address (@target_addresses) {
705 if ($target_address eq $source) { next; }
706 $msg_hash->{target} = [$target_address];
707 &send_msg_hash2address($msg_hash, $target_address);
708 }
709 } else {
710 # msg is for one client
712 if (exists $known_clients->{$target}) {
713 # target is known
715 &send_msg_hash2address($msg_hash, $target);
716 } else {
717 # target is not known
719 daemon_log("ERROR: target $target is not known in known_clients", 1);
720 }
721 }
722 } else {
723 # we have multiple target symbols
725 my $target_string = join(", ", @targets);
726 daemon_log("msg is for:", 7);
727 daemon_log("\t$target_string", 7);
729 my $target_address;
730 foreach $target_address (@targets) {
731 if (exists $known_clients->{$target_address}) {
732 # target_address is known
734 &send_msg_hash2address($msg_hash, $target_address);
735 daemon_log("server forwards msg $header to client $target_address", 3);
736 } else {
737 # target is not known
739 daemon_log("ERROR: target $target_address is not known in known_clients", 1);
740 }
741 }
744 }
746 return;
747 }
750 #=== FUNCTION ================================================================
751 # NAME: open_socket
752 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
753 # [PeerPort] string necessary if port not appended by PeerAddr
754 # RETURNS: socket IO::Socket::INET
755 # DESCRIPTION: open a socket to PeerAddr
756 #===============================================================================
757 sub open_socket {
758 my ($PeerAddr, $PeerPort) = @_ ;
759 if(defined($PeerPort)){
760 $PeerAddr = $PeerAddr.":".$PeerPort;
761 }
762 my $socket;
763 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
764 Porto => "tcp" ,
765 Type => SOCK_STREAM,
766 Timeout => 5,
767 );
768 if(not defined $socket) {
769 return;
770 }
771 daemon_log("open_socket:", 7);
772 daemon_log("\t$PeerAddr", 7);
773 return $socket;
774 }
777 #=== FUNCTION ================================================================
778 # NAME: open_fifo
779 # PARAMETERS: $fifo_path
780 # RETURNS: 0: FIFO couldn"t be setup, 1: FIFO setup correctly
781 # DESCRIPTION: creates a FIFO at $fifo_path
782 #===============================================================================
783 sub open_fifo {
784 my ($fifo_path) = @_ ;
785 if( -p $fifo_path ) {
786 daemon_log("FIFO at $fifo_path already exists! Is being deleted!", 1);
787 unlink($fifo_path);
788 }
789 POSIX::mkfifo($fifo_path, 0666) or die "can't mkfifo $fifo_path: $!";
790 daemon_log( "FIFO started at $fifo_path", 1) ;
791 return 1;
792 }
795 #=== FUNCTION ================================================================
796 # NAME: read_from_socket
797 # PARAMETERS: socket fh -
798 # RETURNS: result string - readed characters from socket
799 # DESCRIPTION: reads data from socket in 16 byte steps
800 #===============================================================================
801 sub read_from_socket {
802 my ($socket) = @_;
803 my $result = "";
805 $socket->blocking(1);
806 $result = <$socket>;
808 $socket->blocking(0);
809 while ( my $char = <$socket> ) {
810 if (not defined $char) { last }
811 $result .= $char;
812 }
814 # my $len = 16;
815 # while($len == 16){
816 # my $char;
817 # $len = sysread($socket, $char, 16);
818 # if($len != 16) { last }
819 # $result .= $char;
820 # }
821 return $result;
822 }
825 #=== FUNCTION ================================================================
826 # NAME: create_xml_hash
827 # PARAMETERS: header - string - message header (required)
828 # source - string - where the message come from (required)
829 # target - string - where the message should go to (required)
830 # [header_value] - string - something usefull (optional)
831 # RETURNS: hash - hash - nomen est omen
832 # DESCRIPTION: creates a key-value hash, all values are stored in a array
833 #===============================================================================
834 sub create_xml_hash {
835 my ($header, $source, $target, $header_value) = @_;
836 my $hash = {
837 header => [$header],
838 source => [$source],
839 target => [$target],
840 $header => [$header_value],
841 };
842 #daemon_log("create_xml_hash:", 7),
843 #chomp(my $tmp = Dumper $hash);
844 #daemon_log("\t$tmp", 7);
845 return $hash
846 }
849 #=== FUNCTION ================================================================
850 # NAME: create_xml_string
851 # PARAMETERS: xml_hash - hash - hash from function create_xml_hash
852 # RETURNS: xml_string - string - xml string representation of the hash
853 # DESCRIPTION: transform the hash to a string using XML::Simple module
854 #===============================================================================
855 sub create_xml_string {
856 my ($xml_hash) = @_ ;
857 my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
858 $xml_string =~ s/[\n]+//g;
859 #daemon_log("create_xml_string:",7);
860 #daemon_log("$xml_string\n", 7);
861 return $xml_string;
862 }
865 #=== FUNCTION ================================================================
866 # NAME: add_content2xml_hash
867 # PARAMETERS: xml_ref - ref - reference to a hash from function create_xml_hash
868 # element - string - key for the hash
869 # content - string - value for the hash
870 # RETURNS: nothing
871 # DESCRIPTION: add key-value pair to xml_ref, if key alread exists, then append value to list
872 #===============================================================================
873 sub add_content2xml_hash {
874 my ($xml_ref, $element, $content) = @_;
875 if(not exists $$xml_ref{$element} ) {
876 $$xml_ref{$element} = [];
877 }
878 my $tmp = $$xml_ref{$element};
879 push(@$tmp, $content);
880 return;
881 }
884 #=== FUNCTION ================================================================
885 # NAME: get_content_from_xml_hash
886 # PARAMETERS: xml_ref - ref - reference of the xml hash
887 # element - string - key of the value you want
888 # RETURNS: value - string - if key is either header, target or source
889 # value - list - for all other keys in xml hash
890 # DESCRIPTION:
891 #===============================================================================
892 sub get_content_from_xml_hash {
893 my ($xml_ref, $element) = @_ ;
894 my $result = $xml_ref->{$element};
895 if( $element eq "header" || $element eq "target" || $element eq "source") {
896 return @$result[0];
897 }
898 return @$result;
899 }
902 #=== FUNCTION ================================================================
903 # NAME: encrypt_msg
904 # PARAMETERS: msg - string - message to encrypt
905 # my_cipher - ref - reference to a Crypt::Rijndael object
906 # RETURNS: crypted_msg - string - crypted message
907 # DESCRIPTION: crypts the incoming message with the Crypt::Rijndael module
908 #===============================================================================
909 sub encrypt_msg {
910 my ($msg, $my_cipher) = @_;
911 if(not defined $my_cipher) { print "no cipher object\n"; }
912 $msg = "\0"x(16-length($msg)%16).$msg;
913 my $crypted_msg = $my_cipher->encrypt($msg);
914 chomp($crypted_msg = &encode_base64($crypted_msg));
915 return $crypted_msg;
916 }
919 #=== FUNCTION ================================================================
920 # NAME: decrypt_msg
921 # PARAMETERS: crypted_msg - string - message to decrypt
922 # my_cipher - ref - reference to a Crypt::Rijndael object
923 # RETURNS: msg - string - decrypted message
924 # DESCRIPTION: decrypts the incoming message with the Crypt::Rijndael module
925 #===============================================================================
926 sub decrypt_msg {
927 my ($crypted_msg, $my_cipher) = @_ ;
928 $crypted_msg = &decode_base64($crypted_msg);
929 my $msg = $my_cipher->decrypt($crypted_msg);
930 $msg =~ s/\0*//g;
931 return $msg;
932 }
935 #=== FUNCTION ================================================================
936 # NAME: create_ciphering
937 # PARAMETERS: passwd - string - used to create ciphering
938 # RETURNS: cipher - object
939 # DESCRIPTION: creates a Crypt::Rijndael::MODE_CBC object with passwd as key
940 #===============================================================================
941 sub create_ciphering {
942 my ($passwd) = @_;
943 $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
944 my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
946 #daemon_log("iv: $iv", 7);
947 #daemon_log("key: $passwd", 7);
948 my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
949 $my_cipher->set_iv($iv);
950 return $my_cipher;
951 }
954 #=== FUNCTION ================================================================
955 # NAME: send_msg_hash2address
956 # PARAMETERS: msg_hash - hash - xml_hash created with function create_xml_hash
957 # PeerAddr string - socket address to send msg
958 # PeerPort string - socket port, if not included in socket address
959 # RETURNS: nothing
960 # DESCRIPTION: ????
961 #===============================================================================
962 sub send_msg_hash2address {
963 my ($msg_hash, $address, $passwd) = @_ ;
965 # fetch header for logging
966 my $header = &get_content_from_xml_hash($msg_hash, "header");
968 # generate xml string
969 my $msg_xml = &create_xml_string($msg_hash);
971 # fetch the appropriated passwd from hash
972 if(not defined $passwd) {
973 if(exists $known_daemons->{$address}) {
974 $passwd = $known_daemons->{$address}->{passwd};
975 } elsif(exists $known_clients->{$address}) {
976 $passwd = $known_clients->{$address}->{passwd};
978 } else {
979 daemon_log("$address not known, neither as server nor as client", 1);
980 return;
981 }
982 }
984 # create ciphering object
985 my $act_cipher = &create_ciphering($passwd);
987 # encrypt xml msg
988 my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
990 # opensocket
991 my $socket = &open_socket($address);
992 if(not defined $socket){
993 daemon_log( "cannot send '$header'-msg to $address , server not reachable", 5);
995 if (exists $known_clients->{$address}) {
996 if ($known_clients->{$address}->{status} eq "down") {
997 # if status of not reachable client is already 'down', then delete client from known_clients
998 &clean_up_known_clients($address);
1000 } else {
1001 # update status to 'down'
1002 &update_known_clients(hostname=>$address, status=>"down");
1004 }
1005 }
1006 return;
1007 }
1009 # send xml msg
1010 print $socket $crypted_msg."\n";
1012 close $socket;
1014 daemon_log("send '$header'-msg to $address", 1);
1016 daemon_log("$msg_xml", 5);
1018 #daemon_log("crypted message:",7);
1019 #daemon_log("\t$crypted_msg", 7);
1021 # update status of client in known_clients with last send msg
1022 if(exists $known_daemons->{$address}) {
1023 #&update_known_daemons();
1024 } elsif(exists $known_clients->{$address}) {
1025 &update_known_clients(hostname=>$address, status=>$header);
1026 }
1028 return;
1029 }
1032 #=== FUNCTION ================================================================
1033 # NAME: send_msg_hash2bus
1034 # PARAMETERS: msg_hash - hash - xml_hash created with function create_xml_hash
1035 # RETURNS: nothing
1036 # DESCRIPTION: ????
1037 #===============================================================================
1038 sub send_msg_hash2bus {
1039 my($msg_hash) = @_;
1041 # fetch header for logging
1042 my $header = &get_content_from_xml_hash($msg_hash, "header");
1044 # generate xml string
1045 my $msg_xml = &create_xml_string($msg_hash);
1047 # encrypt xml msg
1048 my $crypted_msg = &encrypt_msg($msg_xml, $bus_cipher);
1050 # open socket
1051 my $socket = &open_socket($bus_address);
1052 if(not defined $socket){
1053 daemon_log( "cannot send '$header'-msg to $bus_address , bus not reachable", 5);
1054 return;
1055 }
1057 # send xml msg
1058 print $socket $crypted_msg."\n";
1060 close $socket;
1063 daemon_log("send '$header'-msg to bus", 1);
1064 daemon_log("$msg_xml", 5);
1065 #daemon_log("crypted msg:",7);
1066 #daemon_log("\t$crypted_msg", 7);
1068 return;
1069 }
1077 ##=== FUNCTION ================================================================
1078 ## NAME: new_passwd
1079 ## PARAMETERS: msg_hash - ref - hash from function create_xml_hash
1080 ## RETURNS: nothing
1081 ## DESCRIPTION: process this incoming message
1082 ##===============================================================================
1083 #sub new_passwd {
1084 # my ($msg_hash) = @_;
1085 #
1086 # my $source = &get_content_from_xml_hash($msg_hash, "source");
1087 # my $passwd = (&get_content_from_xml_hash($msg_hash, "new_passwd"))[0];
1088 #
1089 # if (exists $known_daemons->{$source}) {
1090 # &add_content2known_daemons(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
1091 # $bus_cipher = &create_ciphering($passwd);
1092 # my $hash = &create_xml_hash("confirm_new_passwd", "$server_ip:$server_port", "$source");
1093 # &send_msg_hash2address($hash, $source);
1094 #
1095 # } elsif (exists $known_clients->{$source}) {
1096 # &add_content2known_clients(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
1097 #
1098 # } else {
1099 # daemon_log("ERROR: $source not known, neither in known_daemons nor in known_clients", 1)
1100 # }
1101 #
1102 # return;
1103 #}
1106 ##=== FUNCTION ================================================================
1107 ## NAME: make ping
1108 ## PARAMETERS: address - string - address which should be pinged
1109 ## RETURNS: nothing
1110 ## DESCRIPTION: send ping message to address
1111 ##===============================================================================
1112 #sub make_ping {
1113 # my ($msg_hash) = @_;
1114 #
1115 # my $source = &get_content_from_xml_hash($msg_hash, "source");
1116 # my $target = &get_content_from_xml_hash($msg_hash, "target");
1117 #
1118 # print "make_ping:$source\n";
1119 # my $out_hash = &create_xml_hash("ping", $target, $source);
1120 # &send_msg_hash2address($out_hash, $source);
1121 # return;
1122 #}
1125 ##=== FUNCTION ================================================================
1126 ## NAME: got_ping
1127 ## PARAMETERS: msg_hash - hash - hash from function create_xml_hash
1128 ## RETURNS: nothing
1129 ## DESCRIPTION: process this incoming message
1130 ##===============================================================================
1131 #sub got_ping {
1132 # my ($msg_hash) = @_;
1133 #
1134 # my $source = &get_content_from_xml_hash($msg_hash, 'source');
1135 # my $target = &get_content_from_xml_hash($msg_hash, 'target');
1136 # my $header = &get_content_from_xml_hash($msg_hash, 'header');
1137 #
1138 # if(exists $known_daemons->{$source}) {
1139 # &add_content2known_daemons(hostname=>$source, status=>$header);
1140 # } else {
1141 # &add_content2known_clients(hostname=>$source, status=>$header);
1142 # }
1143 #
1144 # return;
1145 #}
1148 ##=== FUNCTION ================================================================
1149 ## NAME: here_i_am
1150 ## PARAMETERS: msg_hash - hash - hash from function create_xml_hash
1151 ## RETURNS: nothing
1152 ## DESCRIPTION: process this incoming message
1153 ##===============================================================================
1154 #sub here_i_am {
1155 # my ($msg_hash) = @_;
1156 #
1157 # my $source = &get_content_from_xml_hash($msg_hash, "source");
1158 # my $mac_address = (&get_content_from_xml_hash($msg_hash, "mac_address"))[0];
1159 # my $out_hash;
1160 #
1161 # # number of known clients
1162 # my $nu_clients = keys %$known_clients;
1163 #
1164 # # check wether client address or mac address is already known
1165 # if (exists $known_clients->{$source}) {
1166 # daemon_log("WARNING: $source is already known as a client", 1);
1167 # daemon_log("WARNING: values for $source are being overwritten", 1);
1168 # $nu_clients --;
1169 # }
1170 #
1171 # # number of actual activ clients
1172 # my $act_nu_clients = $nu_clients;
1173 #
1174 # daemon_log("number of actual activ clients: $act_nu_clients", 5);
1175 # daemon_log("number of maximal allowed clients: $max_clients", 5);
1176 #
1177 # if($max_clients <= $act_nu_clients) {
1178 # my $out_hash = &create_xml_hash("denied", $server_address, $source);
1179 # &add_content2xml_hash($out_hash, "denied", "I_cannot_take_any_more_clients!");
1180 # my $passwd = (&get_content_from_xml_hash($msg_hash, "new_passwd"))[0];
1181 # &send_msg_hash2address($out_hash, $source, $passwd);
1182 # return;
1183 # }
1184 #
1185 # # new client accepted
1186 # my $new_passwd = (&get_content_from_xml_hash($msg_hash, "new_passwd"))[0];
1187 #
1188 # # create known_daemons entry
1189 # my $events = (&get_content_from_xml_hash($msg_hash, "events"))[0];
1190 # &create_known_client($source);
1191 # &add_content2known_clients(hostname=>$source, events=>$events, mac_address=>$mac_address,
1192 # status=>"registered", passwd=>$new_passwd);
1193 #
1194 # # return acknowledgement to client
1195 # $out_hash = &create_xml_hash("registered", $server_address, $source);
1196 # &send_msg_hash2address($out_hash, $source);
1197 #
1198 # # notify registered client to bus
1199 # $out_hash = &create_xml_hash("new_client", $server_address, $bus_address, $source);
1200 # &send_msg_hash2bus($out_hash);
1201 #
1202 # # give the new client his ldap config
1203 # &new_ldap_config($source);
1204 #
1205 # return;
1206 #}
1209 #=== FUNCTION ================================================================
1210 # NAME: who_has
1211 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
1212 # RETURNS: nothing
1213 # DESCRIPTION: process this incoming message
1214 #===============================================================================
1215 #sub who_has {
1216 # my ($msg_hash) = @_ ;
1217 #
1218 # # what is your search pattern
1219 # my $search_pattern = (&get_content_from_xml_hash($msg_hash, "who_has"))[0];
1220 # my $search_element = (&get_content_from_xml_hash($msg_hash, $search_pattern))[0];
1221 # daemon_log("who_has-msg looking for $search_pattern $search_element", 7);
1222 #
1223 # # scanning known_clients for search_pattern
1224 # my @host_addresses = keys %$known_clients;
1225 # my $known_clients_entries = length @host_addresses;
1226 # my $host_address;
1227 # foreach my $host (@host_addresses) {
1228 # my $client_element = $known_clients->{$host}->{$search_pattern};
1229 # if ($search_element eq $client_element) {
1230 # $host_address = $host;
1231 # last;
1232 # }
1233 # }
1234 #
1235 # # search was successful
1236 # if (defined $host_address) {
1237 # my $source = @{$msg_hash->{source}}[0];
1238 # my $out_msg = &create_xml_hash("who_has_i_do", $server_address, $source, "mac_address");
1239 # &add_content2xml_hash($out_msg, "mac_address", $search_element);
1240 # &send_msg_hash2address($out_msg, $bus_address);
1241 # }
1242 # return;
1243 #}
1246 #sub who_has_i_do {
1247 # my ($msg_hash) = @_ ;
1248 # my $header = &get_content_from_xml_hash($msg_hash, "header");
1249 # my $source = &get_content_from_xml_hash($msg_hash, "source");
1250 # my $search_param = (&get_content_from_xml_hash($msg_hash, $header))[0];
1251 # my $search_value = (&get_content_from_xml_hash($msg_hash, $search_param))[0];
1252 # print "\ngot msg $header:\nserver $source has client with $search_param $search_value\n";
1253 #}
1256 #=== FUNCTION ================================================================
1257 # NAME: update_status
1258 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
1259 # RETURNS: nothing
1260 # DESCRIPTION: process this incoming message
1261 #===============================================================================
1262 #sub update_status {
1263 # my ($msg_hash) = @_;
1264 # my $header = &get_content_from_xml_hash($msg_hash, "header");
1265 # my $source = &get_content_from_xml_hash($msg_hash, "source");
1266 # my $new_status = (&get_content_from_xml_hash($msg_hash, "update_status"))[0];
1267 #
1268 # # find the source
1269 # my $act_known_hash;
1270 # if (exists $known_daemons->{$source}) {
1271 #
1272 # &add_content2known_daemons(hostname=>$source, status=>$new_status);
1273 # } elsif (exists $known_clients->{$source}) {
1274 # &update_known_clients(hostname=>$source, status=>$new_status);
1275 # #&add_content2known_clients(hostname=>$source, status=>$new_status);
1276 # } else {
1277 # daemon_log("ERROR: got $header-msg, but cannot find $source in my hashes, unable to update status", 1);
1278 # return;
1279 # }
1280 #
1281 # return;
1282 #}
1285 ##=== FUNCTION ================================================================
1286 ## NAME: new_ldap_config
1287 ## PARAMETERS: address - string - ip address and port of a host
1288 ## RETURNS: nothing
1289 ## DESCRIPTION: send to address the ldap configuration found for dn gotoLdapServer
1290 ##===============================================================================
1291 #sub new_ldap_config {
1292 # my ($address) = @_ ;
1293 #
1294 # if (not exists $known_clients->{$address}) {
1295 # daemon_log("ERROR: $address does not exist in known_clients, cannot send him his ldap config", 1);
1296 # return;
1297 # }
1298 #
1299 # my $mac_address = $known_clients->{$address}->{"mac_address"};
1300 # if (not defined $mac_address) {
1301 # daemon_log("ERROR: no mac address found for client $address", 1);
1302 # return;
1303 # }
1304 #
1305 # # fetch dn
1306 # my $goHard_cmd = "ldapsearch -x '(&(objectClass=goHard)(macAddress=00:11:22:33:44:57))' dn gotoLdapServer";
1307 # my $dn;
1308 # my @gotoLdapServer;
1309 # open (PIPE, "$goHard_cmd 2>&1 |");
1310 # while(<PIPE>) {
1311 # chomp $_;
1312 # # If it's a comment, goto next
1313 # if ($_ =~ m/^[#]/) { next;}
1314 # if ($_ =~ m/^dn: ([\S]+?)$/) {
1315 # $dn = $1;
1316 # } elsif ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
1317 # push(@gotoLdapServer, $1);
1318 # }
1319 # }
1320 # close(PIPE);
1321 #
1322 # # no dn found
1323 # if (not defined $dn) {
1324 # daemon_log("ERROR: no dn arose from command: $goHard_cmd", 1);
1325 # return;
1326 # }
1327 #
1328 # # no gotoLdapServer found
1329 # my $gosaGroupOfNames_cmd = "ldapsearch -x '(&(objectClass=gosaGroupOfNames)(member=$dn))' gotoLdapServer";
1330 # if (@gotoLdapServer == 0) {
1331 # open (PIPE, "$gosaGroupOfNames_cmd 2>&1 |");
1332 # while(<PIPE>) {
1333 # chomp $_;
1334 # if ($_ =~ m/^[#]/) { next; }
1335 # if ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
1336 # push(@gotoLdapServer, $1);
1337 # }
1338 # }
1339 # close(PIPE);
1340 # }
1341 #
1342 # # still no gotoLdapServer found
1343 # if (@gotoLdapServer == 0) {
1344 # daemon_log("ERROR: cannot find gotoLdapServer entry in command: $gosaGroupOfNames_cmd", 1);
1345 # return;
1346 # }
1347 #
1348 # # sort @gotoLdapServer and then split of ranking
1349 # my @sorted_gotoLdapServer = sort(@gotoLdapServer);
1350 # @gotoLdapServer = reverse(@sorted_gotoLdapServer);
1351 # foreach (@gotoLdapServer) {
1352 # $_ =~ s/^\d://;
1353 # }
1354 #
1355 # my $t = join(" ", @gotoLdapServer);
1356 #
1357 # my $out_hash = &create_xml_hash("new_ldap_config", $server_address, $address);
1358 # map(&add_content2xml_hash($out_hash, "new_ldap_config", $_), @gotoLdapServer);
1359 # &send_msg_hash2address($out_hash, $address);
1360 #
1361 # return;
1362 #}
1365 ##=== FUNCTION ================================================================
1366 ## NAME: execute_actions
1367 ## PARAMETERS: msg_hash - hash - hash from function create_xml_hash
1368 ## RETURNS: nothing
1369 ## DESCRIPTION: invokes the script specified in msg_hash which is located under
1370 ## /etc/gosad/actions
1371 ##===============================================================================
1372 #sub execute_actions {
1373 # my ($msg_hash) = @_ ;
1374 # my $configdir= '/etc/gosad/actions/';
1375 # my $result;
1376 #
1377 # my $header = &get_content_from_xml_hash($msg_hash, 'header');
1378 # my $source = &get_content_from_xml_hash($msg_hash, 'source');
1379 # my $target = &get_content_from_xml_hash($msg_hash, 'target');
1380 #
1381 #
1382 # if((not defined $source)
1383 # && (not defined $target)
1384 # && (not defined $header)) {
1385 # daemon_log("ERROR: Entries missing in XML msg for gosad actions under /etc/gosad/actions");
1386 # } else {
1387 # my $parameters="";
1388 # my @params = &get_content_from_xml_hash($msg_hash, $header);
1389 # my $params = join(", ", @params);
1390 # daemon_log("execute_actions: got parameters: $params", 5);
1391 #
1392 # if (@params) {
1393 # foreach my $param (@params) {
1394 # my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
1395 # daemon_log("execute_actions: parameter -> value: $param -> $param_value", 7);
1396 # $parameters.= " ".$param_value;
1397 # }
1398 # }
1399 #
1400 # my $cmd= $configdir.$header."$parameters";
1401 # daemon_log("execute_actions: executing cmd: $cmd", 7);
1402 # $result= "";
1403 # open(PIPE, "$cmd 2>&1 |");
1404 # while(<PIPE>) {
1405 # $result.=$_;
1406 # }
1407 # close(PIPE);
1408 # }
1409 #
1410 # # process the event result
1411 #
1412 #
1413 # return;
1414 #}
1417 #=== FUNCTION ================================================================
1418 # NAME: print_known_daemons
1419 # PARAMETERS: nothing
1420 # RETURNS: nothing
1421 # DESCRIPTION: nomen est omen
1422 #===============================================================================
1423 sub print_known_daemons {
1424 my ($tmp) = @_ ;
1425 print "####################################\n";
1426 print "# status of known_daemons\n";
1427 $shmda->shlock(LOCK_EX);
1428 my @hosts = keys %$known_daemons;
1429 foreach my $host (@hosts) {
1430 my $status = $known_daemons->{$host}->{status} ;
1431 my $passwd = $known_daemons->{$host}->{passwd};
1432 my $timestamp = $known_daemons->{$host}->{timestamp};
1433 print "$host\n";
1434 print "\tstatus: $status\n";
1435 print "\tpasswd: $passwd\n";
1436 print "\ttimestamp: $timestamp\n";
1437 }
1438 $shmda->shunlock(LOCK_EX);
1439 print "####################################\n";
1440 return;
1441 }
1444 #=== FUNCTION ================================================================
1445 # NAME: create_known_daemon
1446 # PARAMETERS: hostname - string - key for the hash known_daemons
1447 # RETURNS: nothing
1448 # DESCRIPTION: creates a dummy entry for hostname in known_daemons
1449 #===============================================================================
1450 sub create_known_daemon {
1451 my ($hostname) = @_;
1452 $shmda->shlock(LOCK_EX);
1453 $known_daemons->{$hostname} = {};
1454 $known_daemons->{$hostname}->{status} = "none";
1455 $known_daemons->{$hostname}->{passwd} = "none";
1456 $known_daemons->{$hostname}->{timestamp} = "none";
1457 $shmda->shunlock(LOCK_EX);
1458 return;
1459 }
1462 #=== FUNCTION ================================================================
1463 # NAME: add_content2known_daemons
1464 # PARAMETERS: hostname - string - ip address and port of host (required)
1465 # status - string - (optional)
1466 # passwd - string - (optional)
1467 # mac_address - string - mac address of host (optional)
1468 # RETURNS: nothing
1469 # DESCRIPTION: nome est omen and updates each time the timestamp of hostname
1470 #===============================================================================
1471 sub add_content2known_daemons {
1472 my $arg = {
1473 hostname => undef, status => undef, passwd => undef,
1474 mac_address => undef, events => undef,
1475 @_ };
1476 my $hostname = $arg->{hostname};
1477 my $status = $arg->{status};
1478 my $passwd = $arg->{passwd};
1479 my $mac_address = $arg->{mac_address};
1480 my $events = $arg->{events};
1482 if (not defined $hostname) {
1483 daemon_log("ERROR: function add_content2known_daemons is not invoked with requiered parameter 'hostname'", 1);
1484 return;
1485 }
1487 my ($seconds, $minutes, $hours, $monthday, $month,
1488 $year, $weekday, $yearday, $sommertime) = localtime(time);
1489 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1490 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1491 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1492 $month+=1;
1493 $month = $month < 10 ? $month = "0".$month : $month;
1494 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1495 $year+=1900;
1496 my $t = "$year$month$monthday$hours$minutes$seconds";
1498 $shmda->shlock(LOCK_EX);
1499 if (defined $status) {
1500 $known_daemons->{$hostname}->{status} = $status;
1501 }
1502 if (defined $passwd) {
1503 $known_daemons->{$hostname}->{passwd} = $passwd;
1504 }
1505 if (defined $mac_address) {
1506 $known_daemons->{$hostname}->{mac_address} = $mac_address;
1507 }
1508 if (defined $events) {
1509 $known_daemons->{$hostname}->{events} = $events;
1510 }
1511 $known_daemons->{$hostname}->{timestamp} = $t;
1512 $shmda->shlock(LOCK_EX);
1513 return;
1514 }
1517 #=== FUNCTION ================================================================
1518 # NAME: update_known_daemons
1519 # PARAMETERS: hostname - string - ip address and port of host (required)
1520 # status - string - (optional)
1521 # passwd - string - (optional)
1522 # client - string - ip address and port of client (optional)
1523 # RETURNS: nothing
1524 # DESCRIPTION: nome est omen and updates each time the timestamp of hostname
1525 #===============================================================================
1526 sub update_known_daemons {
1527 my $arg = {
1528 hostname => undef, status => undef, passwd => undef,
1529 @_ };
1530 my $hostname = $arg->{hostname};
1531 my $status = $arg->{status};
1532 my $passwd = $arg->{passwd};
1534 if (not defined $hostname) {
1535 daemon_log("ERROR: function add_content2known_daemons is not invoked with requiered parameter 'hostname'", 1);
1536 return;
1537 }
1539 my ($seconds, $minutes, $hours, $monthday, $month,
1540 $year, $weekday, $yearday, $sommertime) = localtime(time);
1541 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1542 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1543 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1544 $month+=1;
1545 $month = $month < 10 ? $month = "0".$month : $month;
1546 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1547 $year+=1900;
1548 my $t = "$year$month$monthday$hours$minutes$seconds";
1550 $shmda->shlock(LOCK_EX);
1551 if (defined $status) {
1552 $known_daemons->{$hostname}->{status} = $status;
1553 }
1554 if (defined $passwd) {
1555 $known_daemons->{$hostname}->{passwd} = $passwd;
1556 }
1557 $known_daemons->{$hostname}->{timestamp} = $t;
1558 $shmda->shunlock(LOCK_EX);
1559 return;
1560 }
1563 #=== FUNCTION ================================================================
1564 # NAME: print_known_clients
1565 # PARAMETERS: nothing
1566 # RETURNS: nothing
1567 # DESCRIPTION: nomen est omen
1568 #===============================================================================
1569 sub print_known_clients {
1571 print "####################################\n";
1572 print "# status of known_clients\n";
1573 $shmcl->shlock(LOCK_EX);
1574 my @hosts = keys %$known_clients;
1575 if (@hosts) {
1576 foreach my $host (@hosts) {
1577 my $status = $known_clients->{$host}->{status} ;
1578 my $passwd = $known_clients->{$host}->{passwd};
1579 my $timestamp = $known_clients->{$host}->{timestamp};
1580 my $mac_address = $known_clients->{$host}->{mac_address};
1581 my $events = $known_clients->{$host}->{events};
1582 print "$host\n";
1583 print "\tstatus: $status\n";
1584 print "\tpasswd: $passwd\n";
1585 print "\ttimestamp: $timestamp\n";
1586 print "\tmac_address: $mac_address\n";
1587 print "\tevents: $events\n";
1588 }
1589 }
1590 $shmcl->shunlock(LOCK_EX);
1591 print "####################################\n";
1592 return;
1593 }
1599 #=== FUNCTION ================================================================
1600 # NAME: create_known_client
1601 # PARAMETERS: hostname - string - key for the hash known_clients
1602 # RETURNS: nothing
1603 # DESCRIPTION: creates a dummy entry for hostname in known_clients
1604 #===============================================================================
1605 sub create_known_client {
1606 my ($hostname) = @_;
1607 $shmcl->shlock(LOCK_EX);
1608 $known_clients->{$hostname} = {};
1609 $known_clients->{$hostname}->{status} = "none";
1610 $known_clients->{$hostname}->{passwd} = "none";
1611 $known_clients->{$hostname}->{timestamp} = "none";
1612 $known_clients->{$hostname}->{mac_address} = "none";
1613 $known_clients->{$hostname}->{events} = "none";
1614 $shmcl->shunlock(LOCK_EX);
1615 return;
1616 }
1621 #=== FUNCTION ================================================================
1622 # NAME: add_content2known_clients
1623 # PARAMETERS: hostname - string - ip address and port of host (required)
1624 # status - string - (optional)
1625 # passwd - string - (optional)
1626 # mac_address - string - (optional)
1627 # events - string - event of client, executable skripts under /etc/gosac/events
1628 # RETURNS: nothing
1629 # DESCRIPTION: nome est omen and updates each time the timestamp of hostname
1630 #===============================================================================
1631 sub add_content2known_clients {
1632 my $arg = {
1633 hostname => undef, status => undef, passwd => undef,
1634 mac_address => undef, events => undef,
1635 @_ };
1636 my $hostname = $arg->{hostname};
1637 my $status = $arg->{status};
1638 my $passwd = $arg->{passwd};
1639 my $mac_address = $arg->{mac_address};
1640 my $events = $arg->{events};
1642 if (not defined $hostname) {
1643 daemon_log("ERROR: function add_content2known_clients is not invoked with requiered parameter 'hostname'", 1);
1644 return;
1645 }
1647 my ($seconds, $minutes, $hours, $monthday, $month,
1648 $year, $weekday, $yearday, $sommertime) = localtime(time);
1649 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1650 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1651 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1652 $month+=1;
1653 $month = $month < 10 ? $month = "0".$month : $month;
1654 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1655 $year+=1900;
1656 my $t = "$year$month$monthday$hours$minutes$seconds";
1658 $shmcl->shlock(LOCK_EX);
1659 if (defined $status) {
1660 $known_clients->{$hostname}->{status} = $status;
1661 }
1662 if (defined $passwd) {
1663 $known_clients->{$hostname}->{passwd} = $passwd;
1664 }
1665 if (defined $mac_address) {
1666 $known_clients->{$hostname}->{mac_address} = $mac_address;
1667 }
1668 if (defined $events) {
1669 $known_clients->{$hostname}->{events} = $events;
1670 }
1671 $known_clients->{$hostname}->{timestamp} = $t;
1672 $shmcl->shlock(LOCK_EX);
1673 return;
1674 }
1677 #=== FUNCTION ================================================================
1678 # NAME:
1679 # PARAMETERS:
1680 # RETURNS:
1681 # DESCRIPTION:
1682 #===============================================================================
1683 sub clean_up_known_clients {
1684 my ($address) = @_ ;
1686 if (not exists $known_clients->{$address}) {
1687 daemon_log("cannot prune known_clients from $address, client not known", 5);
1688 return;
1689 }
1691 delete $known_clients->{$address};
1693 # send bus a msg that address was deleted from known_clients
1694 my $out_hash = &create_xml_hash('delete_client', $server_address, $bus_address, $address);
1695 &send_msg_hash2bus($out_hash);
1697 daemon_log("client $address deleted from known_clients because of multiple down time", 3);
1698 return;
1699 }
1702 #=== FUNCTION ================================================================
1703 # NAME: update_known_clients
1704 # PARAMETERS: hostname - string - ip address and port of host (required)
1705 # status - string - (optional)
1706 # passwd - string - (optional)
1707 # client - string - ip address and port of client (optional)
1708 # RETURNS: nothing
1709 # DESCRIPTION: nome est omen and updates each time the timestamp of hostname
1710 #===============================================================================
1711 sub update_known_clients {
1712 my $arg = {
1713 hostname => undef, status => undef, passwd => undef,
1714 mac_address => undef, events => undef,
1715 @_ };
1716 my $hostname = $arg->{hostname};
1717 my $status = $arg->{status};
1718 my $passwd = $arg->{passwd};
1719 my $mac_address = $arg->{mac_address};
1720 my $events = $arg->{events};
1722 if (not defined $hostname) {
1723 daemon_log("ERROR: function add_content2known_daemons is not invoked with requiered parameter 'hostname'", 1);
1724 return;
1725 }
1727 my ($seconds, $minutes, $hours, $monthday, $month,
1728 $year, $weekday, $yearday, $sommertime) = localtime(time);
1729 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
1730 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
1731 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
1732 $month+=1;
1733 $month = $month < 10 ? $month = "0".$month : $month;
1734 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
1735 $year+=1900;
1736 my $t = "$year$month$monthday$hours$minutes$seconds";
1738 $shmcl->shlock(LOCK_EX);
1739 if (defined $status) {
1740 $known_clients->{$hostname}->{status} = $status;
1741 }
1742 if (defined $passwd) {
1743 $known_clients->{$hostname}->{passwd} = $passwd;
1744 }
1745 if (defined $mac_address) {
1746 $known_clients->{$hostname}->{mac_address} = $mac_address;
1747 }
1748 if (defined $events) {
1749 $known_clients->{$hostname}->{events} = $events;
1750 }
1751 $known_clients->{$hostname}->{timestamp} = $t;
1752 $shmcl->shunlock(LOCK_EX);
1753 return;
1754 }
1762 #==== MAIN = main ==============================================================
1764 # parse commandline options
1765 Getopt::Long::Configure( "bundling" );
1766 GetOptions("h|help" => \&usage,
1767 "c|config=s" => \$cfg_file,
1768 "f|foreground" => \$foreground,
1769 "v|verbose+" => \$verbose,
1770 "no-bus+" => \$no_bus,
1771 "no-arp+" => \$no_arp,
1772 );
1774 # read and set config parameters
1775 &check_cmdline_param ;
1776 &read_configfile;
1777 &check_pid;
1778 &import_modules;
1780 $SIG{CHLD} = 'IGNORE';
1782 # restart daemon log file
1783 if(-e $log_file ) { unlink $log_file }
1784 daemon_log(" ", 1);
1785 daemon_log("gosad started!", 1);
1787 # Just fork, if we"re not in foreground mode
1788 if( ! $foreground ) { $pid = fork(); }
1789 else { $pid = $$; }
1791 # Do something useful - put our PID into the pid_file
1792 if( 0 != $pid ) {
1793 open( LOCK_FILE, ">$pid_file" );
1794 print LOCK_FILE "$pid\n";
1795 close( LOCK_FILE );
1796 if( !$foreground ) { exit( 0 ) };
1797 }
1799 # detect own ip and mac address
1800 ($server_ip, $server_mac_address) = &get_ip_and_mac();
1801 if (not defined $server_ip) {
1802 die "EXIT: ip address of $0 could not be detected";
1803 }
1804 daemon_log("server ip address detected: $server_ip", 1);
1805 daemon_log("server mac address detected: $server_mac_address", 1);
1807 # setup xml parser
1808 $xml = new XML::Simple();
1810 # create cipher object
1811 $bus_cipher = &create_ciphering($bus_passwd);
1812 $bus_address = "$bus_ip:$bus_port";
1814 # create reading and writing vectors
1815 my $rbits = my $wbits = my $ebits = "";
1817 # open server socket
1818 $server_address = "$server_ip:$server_port";
1819 if($server_activ eq "on"){
1820 daemon_log(" ", 1);
1821 $server = IO::Socket::INET->new(LocalPort => $server_port,
1822 Type => SOCK_STREAM,
1823 Reuse => 1,
1824 Listen => 20,
1825 );
1826 if(not defined $server){
1827 daemon_log("cannot be a tcp server at $server_port : $@");
1828 } else {
1829 daemon_log("start server:", 1);
1830 daemon_log("\t$server_ip:$server_port",1) ;
1831 vec($rbits, fileno $server, 1) = 1;
1832 vec($wbits, fileno $server, 1) = 1;
1833 }
1834 }
1836 # register at bus
1837 if ($no_bus > 0) {
1838 $bus_activ = "off"
1839 }
1840 if($bus_activ eq "on") {
1841 daemon_log(" ", 1);
1842 ®ister_at_bus();
1843 }
1846 daemon_log(" ", 1);
1848 # start arp fifo
1849 if ($no_arp > 0) {
1850 $arp_activ = "off";
1851 }
1852 my $my_fifo;
1853 if($arp_activ eq "on") {
1854 $my_fifo = &open_fifo($arp_fifo_path);
1855 if($my_fifo == 0) { die "fifo file disappeared\n" }
1856 sysopen($arp_fifo, $arp_fifo_path, O_RDWR) or die "can't read from $arp_fifo: $!" ;
1858 vec($rbits, fileno $arp_fifo, 1) = 1;
1859 }
1861 $gosa_address = "$gosa_ip:$gosa_port";
1862 # start gosa inferface fifos
1863 if ($gosa_activ eq "on") {
1864 daemon_log(" ",1);
1865 $gosa_server = IO::Socket::INET->new(LocalPort => $gosa_port,
1866 Type => SOCK_STREAM,
1867 Reuse => 1,
1868 Listen => 1,
1869 );
1870 if (not defined $gosa_server) {
1871 daemon_log("cannot start tcp server at $gosa_port for communication to gosa: $@", 1);
1872 } else {
1873 daemon_log("start server at for communication to gosa", 1);
1874 daemon_log("\t$server_ip:$gosa_port");
1875 vec($rbits, fileno $gosa_server, 1) = 1;
1877 }
1880 #&open_fifo($gosa_fifo_in);
1881 #sysopen(GOSA_FIFO_IN, $gosa_fifo_in, O_RDWR) or die "can't read from GOSA_FIFO_IN: $!" ;
1882 #vec($rbits, fileno GOSA_FIFO_IN, 1) = 1;
1884 #&open_fifo($gosa_fifo_out);
1885 #sysopen(GOSA_FIFO_OUT, $gosa_fifo_out, O_RDWR) or die "can't read from GOSA_FIFO_IN: $!" ;
1887 }
1890 ###################################
1891 #everything ready, okay, lets start
1892 ###################################
1893 while(1) {
1895 # add all handles from the childs
1896 while ( my ($pid, $child_hash) = each %busy_child ) {
1898 # check whether process still exists
1899 my $exitus_pid = waitpid($pid, WNOHANG);
1900 if($exitus_pid != 0) {
1901 delete $busy_child{$pid};
1902 next;
1903 }
1905 # add child fhd to the listener
1906 my $fhd = $$child_hash{'pipe_rd'};
1907 vec($rbits, fileno $fhd, 1) = 1;
1908 }
1910 my ($rout, $wout);
1911 my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
1913 # error handling
1914 if($nf < 0 ) {
1915 }
1917 # something is coming in
1918 if($server_activ eq "on" && vec($rout, fileno $server, 1)) {
1919 daemon_log(" ", 1);
1920 my $client = $server->accept();
1921 my $other_end = getpeername($client);
1922 if(not defined $other_end) {
1923 daemon_log("client cannot be identified: $!");
1924 } else {
1925 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1926 my $actual_ip = inet_ntoa($iaddr);
1927 daemon_log("accept client at daemon socket from $actual_ip", 5);
1928 my $in_msg = &read_from_socket($client);
1929 if(defined $in_msg){
1930 chomp($in_msg);
1931 &activating_child($in_msg, $actual_ip);
1932 } else {
1933 daemon_log("cannot read from $actual_ip", 5);
1934 }
1935 }
1936 close($client);
1937 }
1939 if($arp_activ eq "on" && vec($rout, fileno $arp_fifo, 1)) {
1940 my $in_msg = <$arp_fifo>;
1941 chomp($in_msg);
1942 print "arp_activ: msg: $in_msg\n";
1943 my $act_passwd = $known_daemons->{$bus_address}->{passwd};
1944 print "arp_activ: arp_passwd: $act_passwd\n";
1946 my $in_msg_hash = $xml->XMLin($in_msg, ForceArray=>1);
1948 my $target = &get_content_from_xml_hash($in_msg_hash, 'target');
1950 if ($target eq $server_address) {
1951 print "arp_activ: forward to server\n";
1952 my $arp_cipher = &create_ciphering($act_passwd);
1953 my $crypted_msg = &encrypt_msg($in_msg, $arp_cipher);
1954 &activating_child($crypted_msg, $server_ip);
1955 } else {
1956 print "arp_activ: send to bus\n";
1957 &send_msg_hash2address($in_msg_hash, $bus_address);
1958 }
1959 print "\n";
1960 }
1962 if($gosa_activ eq "on" && vec($rout, fileno $gosa_server, 1)) {
1963 daemon_log(" ", 1);
1964 my $client = $gosa_server->accept();
1965 my $other_end = getpeername($client);
1966 if(not defined $other_end) {
1967 daemon_log("client cannot be identified: $!");
1968 } else {
1969 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1970 my $actual_ip = inet_ntoa($iaddr);
1971 daemon_log("accept client at gosa socket from $actual_ip", 5);
1972 my $in_msg = <$client>;
1973 #my $in_msg = &read_from_socket($client);
1975 daemon_log(">>>>>>>>>>> frisch vom socket gelesen\n!$in_msg!\n",1);
1976 if(defined $in_msg){
1977 chomp($in_msg);
1978 &activating_child($in_msg, $actual_ip, $client);
1979 } else {
1980 daemon_log("cannot read from $actual_ip", 5);
1981 }
1982 }
1983 #close($client);
1984 }
1986 # check all processing childs whether they are finished ('done') or
1987 while ( my ($pid, $child_hash) = each %busy_child ) {
1988 my $fhd = $$child_hash{'pipe_rd'};
1990 if (vec($rout, fileno $fhd, 1) ) {
1991 daemon_log("process child $pid is ready to read", 5);
1993 $fhd->blocking(1);
1994 my $in_msg = <$fhd>;
1995 $fhd->blocking(0);
1996 my $part_in_msg;
1997 while ($part_in_msg = <$fhd>) {
1998 if (not defined $part_in_msg) {
1999 last;
2000 }
2001 $in_msg .= $part_in_msg;
2002 }
2003 chomp($in_msg);
2005 daemon_log("process child read: $in_msg", 5);
2006 if (not defined $in_msg) {
2007 next;
2008 } elsif ($in_msg =~ "done") {
2009 delete $busy_child{$pid};
2010 $free_child{$pid} = $child_hash;
2012 } else {
2013 my $act_client = $busy_child{$pid}{client_ref};
2014 print $act_client $in_msg."\n";
2015 my $act_pipe = $busy_child{$pid}{pipe_rd};
2016 sleep(10);
2017 close ($act_client);
2018 delete $busy_child{$pid};
2019 $free_child{$pid} = $child_hash;
2021 }
2022 }
2023 }
2026 }