Code

e273e9e0c04ef50dc78e664131fba949c20a25f2
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
24 # TODO
25 #
26 # max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
28 use strict;
29 use warnings;
30 use Getopt::Long;
31 use Config::IniFiles;
32 use POSIX;
34 use Fcntl;
35 use IO::Socket::INET;
36 use IO::Handle;
37 use IO::Select;
38 use Symbol qw(qualify_to_ref);
39 use Crypt::Rijndael;
40 use MIME::Base64;
41 use Digest::MD5  qw(md5 md5_hex md5_base64);
42 use XML::Simple;
43 use Data::Dumper;
44 use Sys::Syslog qw( :DEFAULT setlogsock);
45 use Cwd;
46 use File::Spec;
47 use File::Basename;
48 use File::Find;
49 use File::Copy;
50 use File::Path;
51 use GOSA::DBsqlite;
52 use GOSA::GosaSupportDaemon;
53 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
54 use Net::LDAP;
55 use Net::LDAP::Util qw(:escape);
56 use Time::HiRes qw( usleep);
57 use DateTime;
59 my $modules_path = "/usr/lib/gosa-si/modules";
60 use lib "/usr/lib/gosa-si/modules";
62 # revision number of server and program name
63 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
64 my $server_headURL;
65 my $server_revision;
66 my $server_status;
67 our $prg= basename($0);
69 our $global_kernel;
70 my ($foreground, $ping_timeout);
71 my ($server);
72 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
73 my ($messaging_db_loop_delay);
74 my ($known_modules);
75 my ($procid, $pid);
76 my ($arp_fifo);
77 my ($xml);
78 my $sources_list;
79 my $max_clients;
80 my %repo_files=();
81 my $repo_path;
82 my %repo_dirs=();
83 # variables declared in config file are always set to 'our'
84 our (%cfg_defaults, $log_file, $pid_file, 
85     $server_ip, $server_port, $ClientPackages_key, 
86     $arp_activ, $gosa_unit_tag,
87     $GosaPackages_key, $gosa_timeout,
88     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
89     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
90     $arp_enabled, $arp_interface,
91 );
93 # additional variable which should be globaly accessable
94 our $server_address;
95 our $server_mac_address;
96 our $gosa_address;
97 our $no_arp;
98 our $verbose;
99 our $forground;
100 our $cfg_file;
101 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
103 # specifies the verbosity of the daemon_log
104 $verbose = 0 ;
106 # if foreground is not null, script will be not forked to background
107 $foreground = 0 ;
109 # specifies the timeout seconds while checking the online status of a registrating client
110 $ping_timeout = 5;
112 $no_arp = 0;
113 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
114 my @packages_list_statements;
115 my $watch_for_new_jobs_in_progress = 0;
117 # holds all incoming decrypted messages
118 our $incoming_db;
119 our $incoming_tn = 'incoming';
120 my $incoming_file_name;
121 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
122         "timestamp DEFAULT 'none'", 
123         "headertag DEFAULT 'none'",
124                 "targettag DEFAULT 'none'",
125         "xmlmessage DEFAULT 'none'",
126         "module DEFAULT 'none'",
127         "sessionid DEFAULT '0'",
128         );
130 # holds all gosa jobs
131 our $job_db;
132 our $job_queue_tn = 'jobs';
133 my $job_queue_file_name;
134 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
135                 "timestamp DEFAULT 'none'", 
136                 "status DEFAULT 'none'", 
137                 "result DEFAULT 'none'", 
138                 "progress DEFAULT 'none'", 
139         "headertag DEFAULT 'none'", 
140                 "targettag DEFAULT 'none'", 
141                 "xmlmessage DEFAULT 'none'", 
142                 "macaddress DEFAULT 'none'",
143                 "plainname DEFAULT 'none'",
144         "siserver DEFAULT 'none'",
145         "modified DEFAULT '0'",
146                 );
148 # holds all other gosa-si-server
149 our $known_server_db;
150 our $known_server_tn = "known_server";
151 my $known_server_file_name;
152 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
154 # holds all registrated clients
155 our $known_clients_db;
156 our $known_clients_tn = "known_clients";
157 my $known_clients_file_name;
158 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
160 # holds all registered clients at a foreign server
161 our $foreign_clients_db;
162 our $foreign_clients_tn = "foreign_clients"; 
163 my $foreign_clients_file_name;
164 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
166 # holds all logged in user at each client 
167 our $login_users_db;
168 our $login_users_tn = "login_users";
169 my $login_users_file_name;
170 my @login_users_col_names = ("client", "user", "timestamp");
172 # holds all fai server, the debian release and tag
173 our $fai_server_db;
174 our $fai_server_tn = "fai_server"; 
175 my $fai_server_file_name;
176 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
178 our $fai_release_db;
179 our $fai_release_tn = "fai_release"; 
180 my $fai_release_file_name;
181 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
183 # holds all packages available from different repositories
184 our $packages_list_db;
185 our $packages_list_tn = "packages_list";
186 my $packages_list_file_name;
187 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
188 my $outdir = "/tmp/packages_list_db";
189 my $arch = "i386"; 
191 # holds all messages which should be delivered to a user
192 our $messaging_db;
193 our $messaging_tn = "messaging"; 
194 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
195         "flag", "direction", "delivery_time", "message", "timestamp" );
196 my $messaging_file_name;
198 # path to directory to store client install log files
199 our $client_fai_log_dir = "/var/log/fai"; 
201 # queue which stores taskes until one of the $max_children children are ready to process the task
202 my @tasks = qw();
203 my @msgs_to_decrypt = qw();
204 my $max_children = 2;
207 # loop delay for job queue to look for opsi jobs
208 my $job_queue_opsi_delay = 2;
211 %cfg_defaults = (
212 "general" => {
213     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
214     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
215     },
216 "server" => {
217     "ip" => [\$server_ip, "0.0.0.0"],
218     "port" => [\$server_port, "20081"],
219     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
220     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
221     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
222     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
223     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
224     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
225     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
226     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
227     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
228     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
229     "repo-path"            => [\$repo_path, '/srv/www/repository'],
230     "ldap-uri"             => [\$ldap_uri, ""],
231     "ldap-base"            => [\$ldap_base, ""],
232     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
233     "ldap-admin-password"  => [\$ldap_admin_password, ""],
234     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
235     "max-clients"          => [\$max_clients, 10],
236     "wol-password"           => [\$wake_on_lan_passwd, ""],
237     },
238 "GOsaPackages" => {
239     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
240     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
241     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
242     "key" => [\$GosaPackages_key, "none"],
243     },
244 "ClientPackages" => {
245     "key" => [\$ClientPackages_key, "none"],
246     },
247 "ServerPackages"=> {
248     "address"      => [\$foreign_server_string, ""],
249     "domain"  => [\$server_domain, ""],
250     "key"     => [\$ServerPackages_key, "none"],
251     "key-lifetime" => [\$foreign_servers_register_delay, 120],
252     "job-synchronization-enabled" => [\$job_synchronization, "true"],
253     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
254     },
255 "ArpHandler" => {
256     "enabled"   => [\$arp_enabled, "true"],
257     "interface" => [\$arp_interface, "all"],
258         },
260 );
263 #===  FUNCTION  ================================================================
264 #         NAME:  usage
265 #   PARAMETERS:  nothing
266 #      RETURNS:  nothing
267 #  DESCRIPTION:  print out usage text to STDERR
268 #===============================================================================
269 sub usage {
270     print STDERR << "EOF" ;
271 usage: $prg [-hvf] [-c config]
273            -h        : this (help) message
274            -c <file> : config file
275            -f        : foreground, process will not be forked to background
276            -v        : be verbose (multiple to increase verbosity)
277            -no-arp   : starts $prg without connection to arp module
278  
279 EOF
280     print "\n" ;
284 #===  FUNCTION  ================================================================
285 #         NAME:  logging
286 #   PARAMETERS:  level - string - default 'info'
287 #                msg - string -
288 #                facility - string - default 'LOG_DAEMON'
289 #      RETURNS:  nothing
290 #  DESCRIPTION:  function for logging
291 #===============================================================================
292 sub daemon_log {
293     # log into log_file
294     my( $msg, $level ) = @_;
295     if(not defined $msg) { return }
296     if(not defined $level) { $level = 1 }
297     if(defined $log_file){
298         open(LOG_HANDLE, ">>$log_file");
299         chmod 0600, $log_file;
300         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
301             print STDERR "cannot open $log_file: $!";
302             return 
303         }
304         chomp($msg);
305         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
306         if($level <= $verbose){
307             my ($seconds, $minutes, $hours, $monthday, $month,
308                     $year, $weekday, $yearday, $sommertime) = localtime(time);
309             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
310             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
311             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
312             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
313             $month = $monthnames[$month];
314             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
315             $year+=1900;
316             my $name = $prg;
318             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
319             print LOG_HANDLE $log_msg;
320             if( $foreground ) { 
321                 print STDERR $log_msg;
322             }
323         }
324         close( LOG_HANDLE );
325     }
329 #===  FUNCTION  ================================================================
330 #         NAME:  check_cmdline_param
331 #   PARAMETERS:  nothing
332 #      RETURNS:  nothing
333 #  DESCRIPTION:  validates commandline parameter
334 #===============================================================================
335 sub check_cmdline_param () {
336     my $err_config;
337     my $err_counter = 0;
338         if(not defined($cfg_file)) {
339                 $cfg_file = "/etc/gosa-si/server.conf";
340                 if(! -r $cfg_file) {
341                         $err_config = "please specify a config file";
342                         $err_counter += 1;
343                 }
344     }
345     if( $err_counter > 0 ) {
346         &usage( "", 1 );
347         if( defined( $err_config)) { print STDERR "$err_config\n"}
348         print STDERR "\n";
349         exit( -1 );
350     }
354 #===  FUNCTION  ================================================================
355 #         NAME:  check_pid
356 #   PARAMETERS:  nothing
357 #      RETURNS:  nothing
358 #  DESCRIPTION:  handels pid processing
359 #===============================================================================
360 sub check_pid {
361     $pid = -1;
362     # Check, if we are already running
363     if( open(LOCK_FILE, "<$pid_file") ) {
364         $pid = <LOCK_FILE>;
365         if( defined $pid ) {
366             chomp( $pid );
367             if( -f "/proc/$pid/stat" ) {
368                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
369                 if( $stat ) {
370                                         daemon_log("ERROR: Already running",1);
371                     close( LOCK_FILE );
372                     exit -1;
373                 }
374             }
375         }
376         close( LOCK_FILE );
377         unlink( $pid_file );
378     }
380     # create a syslog msg if it is not to possible to open PID file
381     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
382         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
383         if (open(LOCK_FILE, '<', $pid_file)
384                 && ($pid = <LOCK_FILE>))
385         {
386             chomp($pid);
387             $msg .= "(PID $pid)\n";
388         } else {
389             $msg .= "(unable to read PID)\n";
390         }
391         if( ! ($foreground) ) {
392             openlog( $0, "cons,pid", "daemon" );
393             syslog( "warning", $msg );
394             closelog();
395         }
396         else {
397             print( STDERR " $msg " );
398         }
399         exit( -1 );
400     }
403 #===  FUNCTION  ================================================================
404 #         NAME:  import_modules
405 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
406 #                are stored
407 #      RETURNS:  nothing
408 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
409 #                state is on is imported by "require 'file';"
410 #===============================================================================
411 sub import_modules {
412     daemon_log(" ", 1);
414     if (not -e $modules_path) {
415         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
416     }
418     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
419     while (defined (my $file = readdir (DIR))) {
420         if (not $file =~ /(\S*?).pm$/) {
421             next;
422         }
423                 my $mod_name = $1;
425         # ArpHandler switch
426         if( $file =~ /ArpHandler.pm/ ) {
427             if( $arp_enabled eq "false" ) { next; }
428         }
429         
430         eval { require $file; };
431         if ($@) {
432             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
433             daemon_log("$@", 5);
434                 } else {
435                         my $info = eval($mod_name.'::get_module_info()');
436                         # Only load module if get_module_info() returns a non-null object
437                         if( $info ) {
438                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
439                                 $known_modules->{$mod_name} = $info;
440                                 daemon_log("0 INFO: module $mod_name loaded", 5);
441                         }
442                 }
443     }   
444     close (DIR);
447 #===  FUNCTION  ================================================================
448 #         NAME:  password_check
449 #   PARAMETERS:  nothing
450 #      RETURNS:  nothing
451 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
452 #                the same password
453 #===============================================================================
454 sub password_check {
455     my $passwd_hash = {};
456     while (my ($mod_name, $mod_info) = each %$known_modules) {
457         my $mod_passwd = @$mod_info[1];
458         if (not defined $mod_passwd) { next; }
459         if (not exists $passwd_hash->{$mod_passwd}) {
460             $passwd_hash->{$mod_passwd} = $mod_name;
462         # escalates critical error
463         } else {
464             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
465             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
466             exit( -1 );
467         }
468     }
473 #===  FUNCTION  ================================================================
474 #         NAME:  sig_int_handler
475 #   PARAMETERS:  signal - string - signal arose from system
476 #      RETURNS:  nothing
477 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
478 #===============================================================================
479 sub sig_int_handler {
480     my ($signal) = @_;
482 #       if (defined($ldap_handle)) {
483 #               $ldap_handle->disconnect;
484 #       }
485     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
486     
488     daemon_log("shutting down gosa-si-server", 1);
489     system("kill `ps -C gosa-si-server -o pid=`");
491 $SIG{INT} = \&sig_int_handler;
494 sub check_key_and_xml_validity {
495     my ($crypted_msg, $module_key, $session_id) = @_;
496     my $msg;
497     my $msg_hash;
498     my $error_string;
499     eval{
500         $msg = &decrypt_msg($crypted_msg, $module_key);
502         if ($msg =~ /<xml>/i){
503             $msg =~ s/\s+/ /g;  # just for better daemon_log
504             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
505             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
507             ##############
508             # check header
509             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
510             my $header_l = $msg_hash->{'header'};
511             if( 1 > @{$header_l} ) { die 'empty header tag'; }
512             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
513             my $header = @{$header_l}[0];
514             if( 0 == length $header) { die 'empty string in header tag'; }
516             ##############
517             # check source
518             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
519             my $source_l = $msg_hash->{'source'};
520             if( 1 > @{$source_l} ) { die 'empty source tag'; }
521             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
522             my $source = @{$source_l}[0];
523             if( 0 == length $source) { die 'source error'; }
525             ##############
526             # check target
527             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
528             my $target_l = $msg_hash->{'target'};
529             if( 1 > @{$target_l} ) { die 'empty target tag'; }
530         }
531     };
532     if($@) {
533         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
534         $msg = undef;
535         $msg_hash = undef;
536     }
538     return ($msg, $msg_hash);
542 sub check_outgoing_xml_validity {
543     my ($msg, $session_id) = @_;
545     my $msg_hash;
546     eval{
547         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
549         ##############
550         # check header
551         my $header_l = $msg_hash->{'header'};
552         if( 1 != @{$header_l} ) {
553             die 'no or more than one headers specified';
554         }
555         my $header = @{$header_l}[0];
556         if( 0 == length $header) {
557             die 'header has length 0';
558         }
560         ##############
561         # check source
562         my $source_l = $msg_hash->{'source'};
563         if( 1 != @{$source_l} ) {
564             die 'no or more than 1 sources specified';
565         }
566         my $source = @{$source_l}[0];
567         if( 0 == length $source) {
568             die 'source has length 0';
569         }
570         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
571                 $source =~ /^GOSA$/i ) {
572             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
573         }
574         
575         ##############
576         # check target  
577         my $target_l = $msg_hash->{'target'};
578         if( 0 == @{$target_l} ) {
579             die "no targets specified";
580         }
581         foreach my $target (@$target_l) {
582             if( 0 == length $target) {
583                 die "target has length 0";
584             }
585             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
586                     $target =~ /^GOSA$/i ||
587                     $target =~ /^\*$/ ||
588                     $target =~ /KNOWN_SERVER/i ||
589                     $target =~ /JOBDB/i ||
590                     $target =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i ){
591                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
592             }
593         }
594     };
595     if($@) {
596         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
597         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
598         $msg_hash = undef;
599     }
601     return ($msg_hash);
605 sub input_from_known_server {
606     my ($input, $remote_ip, $session_id) = @_ ;  
607     my ($msg, $msg_hash, $module);
609     my $sql_statement= "SELECT * FROM known_server";
610     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
612     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
613         my $host_name = $hit->{hostname};
614         if( not $host_name =~ "^$remote_ip") {
615             next;
616         }
617         my $host_key = $hit->{hostkey};
618         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
619         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
621         # check if module can open msg envelope with module key
622         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
623         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
624             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
625             daemon_log("$@", 8);
626             next;
627         }
628         else {
629             $msg = $tmp_msg;
630             $msg_hash = $tmp_msg_hash;
631             $module = "ServerPackages";
632             last;
633         }
634     }
636     if( (!$msg) || (!$msg_hash) || (!$module) ) {
637         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
638     }
639   
640     return ($msg, $msg_hash, $module);
644 sub input_from_known_client {
645     my ($input, $remote_ip, $session_id) = @_ ;  
646     my ($msg, $msg_hash, $module);
648     my $sql_statement= "SELECT * FROM known_clients";
649     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
650     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
651         my $host_name = $hit->{hostname};
652         if( not $host_name =~ /^$remote_ip:\d*$/) {
653                 next;
654                 }
655         my $host_key = $hit->{hostkey};
656         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
657         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
659         # check if module can open msg envelope with module key
660         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
662         if( (!$msg) || (!$msg_hash) ) {
663             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
664             &daemon_log("$@", 8);
665             next;
666         }
667         else {
668             $module = "ClientPackages";
669             last;
670         }
671     }
673     if( (!$msg) || (!$msg_hash) || (!$module) ) {
674         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
675     }
677     return ($msg, $msg_hash, $module);
681 sub input_from_unknown_host {
682     no strict "refs";
683     my ($input, $session_id) = @_ ;
684     my ($msg, $msg_hash, $module);
685     my $error_string;
686     
687         my %act_modules = %$known_modules;
688         
689     while( my ($mod, $info) = each(%act_modules)) {
691         # check a key exists for this module
692         my $module_key = ${$mod."_key"};
693         if( not defined $module_key ) {
694             if( $mod eq 'ArpHandler' ) {
695                 next;
696             }
697             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
698             next;
699         }
700         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
702         # check if module can open msg envelope with module key
703         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
704         if( (not defined $msg) || (not defined $msg_hash) ) {
705             next;
706         }
707         else {
708             $module = $mod;
709             last;
710         }
711     }
713     if( (!$msg) || (!$msg_hash) || (!$module)) {
714         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
715     }
717     return ($msg, $msg_hash, $module);
721 sub create_ciphering {
722     my ($passwd) = @_;
723         if((!defined($passwd)) || length($passwd)==0) {
724                 $passwd = "";
725         }
726     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
727     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
728     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
729     $my_cipher->set_iv($iv);
730     return $my_cipher;
734 sub encrypt_msg {
735     my ($msg, $key) = @_;
736     my $my_cipher = &create_ciphering($key);
737     my $len;
738     {
739             use bytes;
740             $len= 16-length($msg)%16;
741     }
742     $msg = "\0"x($len).$msg;
743     $msg = $my_cipher->encrypt($msg);
744     chomp($msg = &encode_base64($msg));
745     # there are no newlines allowed inside msg
746     $msg=~ s/\n//g;
747     return $msg;
751 sub decrypt_msg {
753     my ($msg, $key) = @_ ;
754     $msg = &decode_base64($msg);
755     my $my_cipher = &create_ciphering($key);
756     $msg = $my_cipher->decrypt($msg); 
757     $msg =~ s/\0*//g;
758     return $msg;
762 sub get_encrypt_key {
763     my ($target) = @_ ;
764     my $encrypt_key;
765     my $error = 0;
767     # target can be in known_server
768     if( not defined $encrypt_key ) {
769         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
770         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
771         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
772             my $host_name = $hit->{hostname};
773             if( $host_name ne $target ) {
774                 next;
775             }
776             $encrypt_key = $hit->{hostkey};
777             last;
778         }
779     }
781     # target can be in known_client
782     if( not defined $encrypt_key ) {
783         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
784         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
785         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
786             my $host_name = $hit->{hostname};
787             if( $host_name ne $target ) {
788                 next;
789             }
790             $encrypt_key = $hit->{hostkey};
791             last;
792         }
793     }
795     return $encrypt_key;
799 #===  FUNCTION  ================================================================
800 #         NAME:  open_socket
801 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
802 #                [PeerPort] string necessary if port not appended by PeerAddr
803 #      RETURNS:  socket IO::Socket::INET
804 #  DESCRIPTION:  open a socket to PeerAddr
805 #===============================================================================
806 sub open_socket {
807     my ($PeerAddr, $PeerPort) = @_ ;
808     if(defined($PeerPort)){
809         $PeerAddr = $PeerAddr.":".$PeerPort;
810     }
811     my $socket;
812     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
813             Porto => "tcp",
814             Type => SOCK_STREAM,
815             Timeout => 5,
816             );
817     if(not defined $socket) {
818         return;
819     }
820 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
821     return $socket;
825 sub get_local_ip_for_remote_ip {
826         my $remote_ip= shift;
827         my $result="0.0.0.0";
829         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
830                 if($remote_ip eq "127.0.0.1") {
831                         $result = "127.0.0.1";
832                 } else {
833                         my $PROC_NET_ROUTE= ('/proc/net/route');
835                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
836                                 or die "Could not open $PROC_NET_ROUTE";
838                         my @ifs = <PROC_NET_ROUTE>;
840                         close(PROC_NET_ROUTE);
842                         # Eat header line
843                         shift @ifs;
844                         chomp @ifs;
845                         foreach my $line(@ifs) {
846                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
847                                 my $destination;
848                                 my $mask;
849                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
850                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
851                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
852                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
853                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
854                                         # destination matches route, save mac and exit
855                                         $result= &get_ip($Iface);
856                                         last;
857                                 }
858                         }
859                 }
860         } else {
861                 daemon_log("0 WARNING: get_local_ip_for_remote_ip() was called with a non-ip parameter: '$remote_ip'", 1);
862         }
863         return $result;
867 sub send_msg_to_target {
868     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
869     my $error = 0;
870     my $header;
871     my $timestamp = &get_time();
872     my $new_status;
873     my $act_status;
874     my ($sql_statement, $res);
875   
876     if( $msg_header ) {
877         $header = "'$msg_header'-";
878     } else {
879         $header = "";
880     }
882         # Patch the source ip
883         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
884                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
885                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
886         }
888     # encrypt xml msg
889     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
891     # opensocket
892     my $socket = &open_socket($address);
893     if( !$socket ) {
894         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
895         $error++;
896     }
897     
898     if( $error == 0 ) {
899         # send xml msg
900         print $socket $crypted_msg."\n";
902         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
903         daemon_log("$session_id DEBUG: message:\n$msg", 9);
904         
905     }
907     # close socket in any case
908     if( $socket ) {
909         close $socket;
910     }
912     if( $error > 0 ) { $new_status = "down"; }
913     else { $new_status = $msg_header; }
916     # known_clients
917     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
918     $res = $known_clients_db->select_dbentry($sql_statement);
919     if( keys(%$res) == 1) {
920         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
921         if ($act_status eq "down" && $new_status eq "down") {
922             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
923             $res = $known_clients_db->del_dbentry($sql_statement);
924             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
925         } else { 
926             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
927             $res = $known_clients_db->update_dbentry($sql_statement);
928             if($new_status eq "down"){
929                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
930             } else {
931                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
932             }
933         }
934     }
936     # known_server
937     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
938     $res = $known_server_db->select_dbentry($sql_statement);
939     if( keys(%$res) == 1) {
940         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
941         if ($act_status eq "down" && $new_status eq "down") {
942             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
943             $res = $known_server_db->del_dbentry($sql_statement);
944             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
945         } 
946         else { 
947             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
948             $res = $known_server_db->update_dbentry($sql_statement);
949             if($new_status eq "down"){
950                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
951             } else {
952                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
953             }
954         }
955     }
956     return $error; 
960 sub update_jobdb_status_for_send_msgs {
961     my ($answer, $error) = @_;
962     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
963         my $jobdb_id = $1;
964             
965         # sending msg faild
966         if( $error ) {
967             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
968                 my $sql_statement = "UPDATE $job_queue_tn ".
969                     "SET status='error', result='can not deliver msg, please consult log file' ".
970                     "WHERE id=$jobdb_id";
971                 my $res = $job_db->update_dbentry($sql_statement);
972             }
974         # sending msg was successful
975         } else {
976             my $sql_statement = "UPDATE $job_queue_tn ".
977                 "SET status='done' ".
978                 "WHERE id=$jobdb_id AND status='processed'";
979             my $res = $job_db->update_dbentry($sql_statement);
980         }
981     }
985 sub sig_handler {
986         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
987         daemon_log("0 INFO got signal '$signal'", 1); 
988         $kernel->sig_handled();
989         return;
993 sub msg_to_decrypt {
994     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
995     my $session_id = $session->ID;
996     my ($msg, $msg_hash, $module);
997     my $error = 0;
999     # hole neue msg aus @msgs_to_decrypt
1000     my $next_msg = shift @msgs_to_decrypt;
1001     
1002     # entschlüssle sie
1004     # msg is from a new client or gosa
1005     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1006     # msg is from a gosa-si-server
1007     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1008         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1009     }
1010     # msg is from a gosa-si-client
1011     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1012         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1013     }
1014     # an error occurred
1015     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1016         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1017         # could not understand a msg from its server the client cause a re-registering process
1018         daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1019                 "' to cause a re-registering of the client if necessary", 3);
1020         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1021         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1022         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1023             my $host_name = $hit->{'hostname'};
1024             my $host_key = $hit->{'hostkey'};
1025             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1026             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1027             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1028         }
1029         $error++;
1030     }
1033     my $header;
1034     my $target;
1035     my $source;
1036     my $done = 0;
1037     my $sql;
1038     my $res;
1040     # check whether this message should be processed here
1041     if ($error == 0) {
1042         $header = @{$msg_hash->{'header'}}[0];
1043         $target = @{$msg_hash->{'target'}}[0];
1044         $source = @{$msg_hash->{'source'}}[0];
1045                 my $not_found_in_known_clients_db = 0;
1046                 my $not_found_in_known_server_db = 0;
1047                 my $not_found_in_foreign_clients_db = 0;
1048         my $local_address;
1049         my ($target_ip, $target_port) = split(':', $target);
1050                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1051                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1052                 } else {
1053             $local_address = $server_address;
1054         }
1056         # target and source is equal to GOSA -> process here
1057         if (not $done) {
1058             if ($target eq "GOSA" && $source eq "GOSA") {
1059                 $done = 1;                    
1060             }
1061         }
1063         # target is own address without forward_to_gosa-tag -> process here
1064         if (not $done) {
1065             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1066                 $done = 1;
1067                 if ($source eq "GOSA") {
1068                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1069                 }
1070                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1071             }
1072         }
1074         # target is a client address in known_clients -> process here
1075                 if (not $done) {
1076                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1077                                 $res = $known_clients_db->select_dbentry($sql);
1078                                 if (keys(%$res) > 0) {
1079                                                 $done = 1; 
1080                                                 my $hostname = $res->{1}->{'hostname'};
1081                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1082                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1083                         my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1084                         if ($source eq "GOSA") {
1085                             $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1086                         }
1088                                 } else {
1089                                                 $not_found_in_known_clients_db = 1;
1090                                 }
1091                 }
1092         
1093         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1094         if (not $done) {
1095             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1096             my $gosa_at;
1097             my $gosa_session_id;
1098             if (($target eq $local_address) && (defined $forward_to_gosa)){
1099                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1100                 if ($gosa_at ne $local_address) {
1101                     $done = 1;
1102                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1103                 }
1104             }
1105         }
1107         # if message should be processed here -> add message to incoming_db
1108                 if ($done) {
1109                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1110                                 # so gosa-si-server knows how to process this kind of messages
1111                                 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1112                                                 $module = "GosaPackages";
1113                                 }
1115                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1116                                                                 primkey=>[],
1117                                                                 headertag=>$header,
1118                                                                 targettag=>$target,
1119                                                                 xmlmessage=>&encode_base64($msg),
1120                                                                 timestamp=>&get_time,
1121                                                                 module=>$module,
1122                                                                 sessionid=>$session_id,
1123                                                                 } );
1124                 }
1126         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1127         if (not $done) {
1128             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1129             my $gosa_at;
1130             my $gosa_session_id;
1131             if (($target eq $local_address) && (defined $forward_to_gosa)){
1132                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1133                 if ($gosa_at eq $local_address) {
1134                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1135                     if( defined $session_reference ) {
1136                         $heap = $session_reference->get_heap();
1137                     }
1138                     if(exists $heap->{'client'}) {
1139                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1140                         $heap->{'client'}->put($msg);
1141                         &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1142                     }
1143                     $done = 1;
1144                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1145                 }
1146             }
1148         }
1150         # target is a client address in foreign_clients -> forward to registration server
1151         if (not $done) {
1152             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1153             $res = $foreign_clients_db->select_dbentry($sql);
1154             if (keys(%$res) > 0) {
1155                     my $hostname = $res->{1}->{'hostname'};
1156                     my ($host_ip, $host_port) = split(/:/, $hostname);
1157                     my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1158                 my $regserver = $res->{1}->{'regserver'};
1159                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1160                 my $res = $known_server_db->select_dbentry($sql);
1161                 if (keys(%$res) > 0) {
1162                     my $regserver_key = $res->{1}->{'hostkey'};
1163                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1164                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1165                     if ($source eq "GOSA") {
1166                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1167                     }
1168                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1169                 }
1170                 $done = 1;
1171                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1172             } else {
1173                                 $not_found_in_foreign_clients_db = 1;
1174                         }
1175         }
1177         # target is a server address -> forward to server
1178         if (not $done) {
1179             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1180             $res = $known_server_db->select_dbentry($sql);
1181             if (keys(%$res) > 0) {
1182                 my $hostkey = $res->{1}->{'hostkey'};
1184                 if ($source eq "GOSA") {
1185                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1186                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1188                 }
1190                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1191                 $done = 1;
1192                 #print STDERR "target is a server address -> forward to server\n";
1193             } else {
1194                                 $not_found_in_known_server_db = 1;
1195                         }
1196         }
1198                 
1199                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1200                 if ( $not_found_in_foreign_clients_db 
1201                                                 && $not_found_in_known_server_db
1202                                                 && $not_found_in_known_clients_db) {
1203                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1204                                                                 primkey=>[],
1205                                                                 headertag=>$header,
1206                                                                 targettag=>$target,
1207                                                                 xmlmessage=>&encode_base64($msg),
1208                                                                 timestamp=>&get_time,
1209                                                                 module=>$module,
1210                                                                 sessionid=>$session_id,
1211                                                                 } );
1212                                 $done = 1;
1213                 }
1216         if (not $done) {
1217             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1218             if ($source eq "GOSA") {
1219                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1220                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1222                 my $session_reference = $kernel->ID_id_to_session($session_id);
1223                 if( defined $session_reference ) {
1224                     $heap = $session_reference->get_heap();
1225                 }
1226                 if(exists $heap->{'client'}) {
1227                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1228                     $heap->{'client'}->put($error_msg);
1229                 }
1230             }
1231         }
1233     }
1235     return;
1239 sub next_task {
1240     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1241     my $running_task = POE::Wheel::Run->new(
1242             Program => sub { process_task($session, $heap, $task) },
1243             StdioFilter => POE::Filter::Reference->new(),
1244             StdoutEvent  => "task_result",
1245             StderrEvent  => "task_debug",
1246             CloseEvent   => "task_done",
1247             );
1248     $heap->{task}->{ $running_task->ID } = $running_task;
1251 sub handle_task_result {
1252     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1253     my $client_answer = $result->{'answer'};
1254     if( $client_answer =~ s/session_id=(\d+)$// ) {
1255         my $session_id = $1;
1256         if( defined $session_id ) {
1257             my $session_reference = $kernel->ID_id_to_session($session_id);
1258             if( defined $session_reference ) {
1259                 $heap = $session_reference->get_heap();
1260             }
1261         }
1263         if(exists $heap->{'client'}) {
1264             $heap->{'client'}->put($client_answer);
1265         }
1266     }
1267     $kernel->sig(CHLD => "child_reap");
1270 sub handle_task_debug {
1271     my $result = $_[ARG0];
1272     print STDERR "$result\n";
1275 sub handle_task_done {
1276     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1277     delete $heap->{task}->{$task_id};
1280 sub process_task {
1281     no strict "refs";
1282     #CHECK: Not @_[...]?
1283     my ($session, $heap, $task) = @_;
1284     my $error = 0;
1285     my $answer_l;
1286     my ($answer_header, @answer_target_l, $answer_source);
1287     my $client_answer = "";
1289     # prepare all variables needed to process message
1290     #my $msg = $task->{'xmlmessage'};
1291     my $msg = &decode_base64($task->{'xmlmessage'});
1292     my $incoming_id = $task->{'id'};
1293     my $module = $task->{'module'};
1294     my $header =  $task->{'headertag'};
1295     my $session_id = $task->{'sessionid'};
1296     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1297     my $source = @{$msg_hash->{'source'}}[0];
1298     
1299     # set timestamp of incoming client uptodate, so client will not 
1300     # be deleted from known_clients because of expiration
1301     my $act_time = &get_time();
1302     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1303     my $res = $known_clients_db->exec_statement($sql);
1305     ######################
1306     # process incoming msg
1307     if( $error == 0) {
1308         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1309         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1310         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1312         if ( 0 < @{$answer_l} ) {
1313             my $answer_str = join("\n", @{$answer_l});
1314             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1315                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1316             }
1317             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1318         } else {
1319             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1320         }
1322     }
1323     if( !$answer_l ) { $error++ };
1325     ########
1326     # answer
1327     if( $error == 0 ) {
1329         foreach my $answer ( @{$answer_l} ) {
1330             # check outgoing msg to xml validity
1331             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1332             if( not defined $answer_hash ) { next; }
1333             
1334             $answer_header = @{$answer_hash->{'header'}}[0];
1335             @answer_target_l = @{$answer_hash->{'target'}};
1336             $answer_source = @{$answer_hash->{'source'}}[0];
1338             # deliver msg to all targets 
1339             foreach my $answer_target ( @answer_target_l ) {
1341                 # targets of msg are all gosa-si-clients in known_clients_db
1342                 if( $answer_target eq "*" ) {
1343                     # answer is for all clients
1344                     my $sql_statement= "SELECT * FROM known_clients";
1345                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1346                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1347                         my $host_name = $hit->{hostname};
1348                         my $host_key = $hit->{hostkey};
1349                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1350                         &update_jobdb_status_for_send_msgs($answer, $error);
1351                     }
1352                 }
1354                 # targets of msg are all gosa-si-server in known_server_db
1355                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1356                     # answer is for all server in known_server
1357                     my $sql_statement= "SELECT * FROM $known_server_tn";
1358                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1359                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1360                         my $host_name = $hit->{hostname};
1361                         my $host_key = $hit->{hostkey};
1362                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1363                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1364                         &update_jobdb_status_for_send_msgs($answer, $error);
1365                     }
1366                 }
1368                 # target of msg is GOsa
1369                                 elsif( $answer_target eq "GOSA" ) {
1370                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1371                                         my $add_on = "";
1372                     if( defined $session_id ) {
1373                         $add_on = ".session_id=$session_id";
1374                     }
1375                     # answer is for GOSA and has to returned to connected client
1376                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1377                     $client_answer = $gosa_answer.$add_on;
1378                 }
1380                 # target of msg is job queue at this host
1381                 elsif( $answer_target eq "JOBDB") {
1382                     $answer =~ /<header>(\S+)<\/header>/;   
1383                     my $header;
1384                     if( defined $1 ) { $header = $1; }
1385                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1386                     &update_jobdb_status_for_send_msgs($answer, $error);
1387                 }
1389                 # target of msg is a mac address
1390                 elsif( $answer_target =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i ) {
1391                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1392                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1393                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1394                     my $found_ip_flag = 0;
1395                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1396                         my $host_name = $hit->{hostname};
1397                         my $host_key = $hit->{hostkey};
1398                         $answer =~ s/$answer_target/$host_name/g;
1399                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1400                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1401                         &update_jobdb_status_for_send_msgs($answer, $error);
1402                         $found_ip_flag++ ;
1403                     }   
1404                     if( $found_ip_flag == 0) {
1405                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1406                     }
1408                 #  answer is for one specific host   
1409                 } else {
1410                     # get encrypt_key
1411                     my $encrypt_key = &get_encrypt_key($answer_target);
1412                     if( not defined $encrypt_key ) {
1413                         # unknown target
1414                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1415                         next;
1416                     }
1417                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1418                     &update_jobdb_status_for_send_msgs($answer, $error);
1419                 }
1420             }
1421         }
1422     }
1424     my $filter = POE::Filter::Reference->new();
1425     my %result = ( 
1426             status => "seems ok to me",
1427             answer => $client_answer,
1428             );
1430     my $output = $filter->put( [ \%result ] );
1431     print @$output;
1436 sub session_start {
1437     my ($kernel) = $_[KERNEL];
1438     $global_kernel = $kernel;
1439     $kernel->yield('register_at_foreign_servers');
1440         $kernel->yield('create_fai_server_db', $fai_server_tn );
1441         $kernel->yield('create_fai_release_db', $fai_release_tn );
1442     $kernel->yield('watch_for_next_tasks');
1443         $kernel->sig(USR1 => "sig_handler");
1444         $kernel->sig(USR2 => "recreate_packages_db");
1445         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1446         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1447     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1448     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1449         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1450     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1451         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1452     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1458 sub watch_for_done_jobs {
1459     #CHECK: $heap for what?
1460     my ($kernel,$heap) = @_[KERNEL, HEAP];
1462     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1463         my $res = $job_db->select_dbentry( $sql_statement );
1465     while( my ($id, $hit) = each %{$res} ) {
1466         my $jobdb_id = $hit->{id};
1467         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1468         my $res = $job_db->del_dbentry($sql_statement); 
1469     }
1471     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1475 sub watch_for_opsi_jobs {
1476     my ($kernel) = @_[KERNEL];
1478     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((headertag='opsi_install_client') AND (status='processing'))";
1479         my $res = $job_db->select_dbentry( $sql_statement );
1481     while (my ($id, $hit) = each %$res ) {
1483         # Ask OPSI for an update of the running jobs
1484         my $hostId = $hit->{'plainname'};
1485         print STDERR "\n\n$hostId\n";
1486 #        my $result= {};
1487 #        
1488 #        # For hosts, only return the products that are or get installed
1489 #        my $callobj;
1490 #        $callobj = {
1491 #            method  => 'getProductStates_hash',
1492 #            params  => [ $hostId ],
1493 #            id  => 1,
1494 #        };
1495 #        
1496 #        my $hres = $client->call($opsi_url, $callobj);
1497 #        if (check_res($hres)){
1498 #            my $htmp= $hres->result->{$hostId};
1499 #        
1500 #        # check state != not_installed or action == setup -> load and add
1501 #            my $products= 0;
1502 #            my $installed= 0;
1503 #            my $error= 0;
1504 #            foreach my $product (@{$htmp}){
1505 #        
1506 #                if ($product->{'installationStatus'} ne "not_installed" or
1507 #                        $product->{'actionRequest'} eq "setup"){
1508 #        
1509 #        # Increase number of products for this host
1510 #                    $products++;
1511 #        
1512 #                    if ($product->{'installationStatus'} eq "failed"){
1513 #                        $result->{$product->{'productId'}}= "error";
1514 #                        $error++;
1515 #                    }
1516 #                    if ($product->{'installationStatus'} eq "installed"){
1517 #                        $result->{$product->{'productId'}}= "installed";
1518 #                        $installed++;
1519 #                    }
1520 #                    if ($product->{'installationStatus'} eq "installing"){
1521 #                        $result->{$product->{'productId'}}= "installing";
1522 #                    }
1523 #                }
1524 #            }
1525 #        
1526 #        # Estimate "rough" progress
1527 #            $result->{'progress'}= int($installed * 100 / $products);
1528 #}
1530 #return $result;
1532     }
1534     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1538 # if a job got an update or was modified anyway, send to all other si-server an update message
1539 # of this jobs
1540 sub watch_for_modified_jobs {
1541     my ($kernel,$heap) = @_[KERNEL, HEAP];
1543     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))"; 
1544     my $res = $job_db->select_dbentry( $sql_statement );
1545     
1546     # if db contains no jobs which should be update, do nothing
1547     if (keys %$res != 0) {
1549         if ($job_synchronization  eq "true") {
1550             # make out of the db result a gosa-si message   
1551             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1552  
1553             # update all other SI-server
1554             &inform_all_other_si_server($update_msg);
1555         }
1557         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1558         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1559         $res = $job_db->update_dbentry($sql_statement);
1560     }
1562     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1566 sub watch_for_new_jobs {
1567         if($watch_for_new_jobs_in_progress == 0) {
1568                 $watch_for_new_jobs_in_progress = 1;
1569                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1571                 # check gosa job quaeue for jobs with executable timestamp
1572                 my $timestamp = &get_time();
1573                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1574                 my $res = $job_db->exec_statement( $sql_statement );
1576                 # Merge all new jobs that would do the same actions
1577                 my @drops;
1578                 my $hits;
1579                 foreach my $hit (reverse @{$res} ) {
1580                         my $macaddress= lc @{$hit}[8];
1581                         my $headertag= @{$hit}[5];
1582                         if(
1583                                 defined($hits->{$macaddress}) &&
1584                                 defined($hits->{$macaddress}->{$headertag}) &&
1585                                 defined($hits->{$macaddress}->{$headertag}[0])
1586                         ) {
1587                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1588                         }
1589                         $hits->{$macaddress}->{$headertag}= $hit;
1590                 }
1592                 # Delete new jobs with a matching job in state 'processing'
1593                 foreach my $macaddress (keys %{$hits}) {
1594                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1595                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1596                                 if(defined($jobdb_id)) {
1597                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1598                                         my $res = $job_db->exec_statement( $sql_statement );
1599                                         foreach my $hit (@{$res}) {
1600                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1601                                         }
1602                                 } else {
1603                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1604                                 }
1605                         }
1606                 }
1608                 # Commit deletion
1609                 $job_db->exec_statementlist(\@drops);
1611                 # Look for new jobs that could be executed
1612                 foreach my $macaddress (keys %{$hits}) {
1614                         # Look if there is an executing job
1615                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1616                         my $res = $job_db->exec_statement( $sql_statement );
1618                         # Skip new jobs for host if there is a processing job
1619                         if(defined($res) and defined @{$res}[0]) {
1620                                 next;
1621                         }
1623                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1624                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1625                                 if(defined($jobdb_id)) {
1626                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1628                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1629                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1630                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1632                                         # expect macaddress is unique!!!!!!
1633                                         my $target = $res_hash->{1}->{hostname};
1635                                         # change header
1636                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1638                                         # add sqlite_id
1639                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1641                                         $job_msg =~ /<header>(\S+)<\/header>/;
1642                                         my $header = $1 ;
1643                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1645                                         # update status in job queue to 'processing'
1646                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1647                                         my $res = $job_db->update_dbentry($sql_statement);
1648 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1650                                         # We don't want parallel processing
1651                                         last;
1652                                 }
1653                         }
1654                 }
1656                 $watch_for_new_jobs_in_progress = 0;
1657                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1658         }
1662 sub watch_for_new_messages {
1663     my ($kernel,$heap) = @_[KERNEL, HEAP];
1664     my @coll_user_msg;   # collection list of outgoing messages
1665     
1666     # check messaging_db for new incoming messages with executable timestamp
1667     my $timestamp = &get_time();
1668     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1669     my $res = $messaging_db->exec_statement( $sql_statement );
1670         foreach my $hit (@{$res}) {
1672         # create outgoing messages
1673         my $message_to = @{$hit}[3];
1674         # translate message_to to plain login name
1675         my @message_to_l = split(/,/, $message_to);  
1676                 my %receiver_h; 
1677                 foreach my $receiver (@message_to_l) {
1678                         if ($receiver =~ /^u_([\s\S]*)$/) {
1679                                 $receiver_h{$1} = 0;
1680                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1681                                 my $group_name = $1;
1682                                 # fetch all group members from ldap and add them to receiver hash
1683                                 my $ldap_handle = &get_ldap_handle();
1684                                 if (defined $ldap_handle) {
1685                                                 my $mesg = $ldap_handle->search(
1686                                                                                 base => $ldap_base,
1687                                                                                 scope => 'sub',
1688                                                                                 attrs => ['memberUid'],
1689                                                                                 filter => "cn=$group_name",
1690                                                                                 );
1691                                                 if ($mesg->count) {
1692                                                                 my @entries = $mesg->entries;
1693                                                                 foreach my $entry (@entries) {
1694                                                                                 my @receivers= $entry->get_value("memberUid");
1695                                                                                 foreach my $receiver (@receivers) { 
1696                                                                                                 $receiver_h{$1} = 0;
1697                                                                                 }
1698                                                                 }
1699                                                 } 
1700                                                 # translating errors ?
1701                                                 if ($mesg->code) {
1702                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1703                                                 }
1704                                 # ldap handle error ?           
1705                                 } else {
1706                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1707                                 }
1708                         } else {
1709                                 my $sbjct = &encode_base64(@{$hit}[1]);
1710                                 my $msg = &encode_base64(@{$hit}[7]);
1711                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1712                         }
1713                 }
1714                 my @receiver_l = keys(%receiver_h);
1716         my $message_id = @{$hit}[0];
1718         #add each outgoing msg to messaging_db
1719         my $receiver;
1720         foreach $receiver (@receiver_l) {
1721             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1722                 "VALUES ('".
1723                 $message_id."', '".    # id
1724                 @{$hit}[1]."', '".     # subject
1725                 @{$hit}[2]."', '".     # message_from
1726                 $receiver."', '".      # message_to
1727                 "none"."', '".         # flag
1728                 "out"."', '".          # direction
1729                 @{$hit}[6]."', '".     # delivery_time
1730                 @{$hit}[7]."', '".     # message
1731                 $timestamp."'".     # timestamp
1732                 ")";
1733             &daemon_log("M DEBUG: $sql_statement", 1);
1734             my $res = $messaging_db->exec_statement($sql_statement);
1735             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1736         }
1738         # set incoming message to flag d=deliverd
1739         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1740         &daemon_log("M DEBUG: $sql_statement", 7);
1741         $res = $messaging_db->update_dbentry($sql_statement);
1742         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1743     }
1745     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1746     return;
1749 sub watch_for_delivery_messages {
1750     my ($kernel, $heap) = @_[KERNEL, HEAP];
1752     # select outgoing messages
1753     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1754     #&daemon_log("0 DEBUG: $sql", 7);
1755     my $res = $messaging_db->exec_statement( $sql_statement );
1756     
1757     # build out msg for each    usr
1758     foreach my $hit (@{$res}) {
1759         my $receiver = @{$hit}[3];
1760         my $msg_id = @{$hit}[0];
1761         my $subject = @{$hit}[1];
1762         my $message = @{$hit}[7];
1764         # resolve usr -> host where usr is logged in
1765         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1766         #&daemon_log("0 DEBUG: $sql", 7);
1767         my $res = $login_users_db->exec_statement($sql);
1769         # reciver is logged in nowhere
1770         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1772                 my $send_succeed = 0;
1773                 foreach my $hit (@$res) {
1774                                 my $receiver_host = @$hit[0];
1775                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1777                                 # fetch key to encrypt msg propperly for usr/host
1778                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1779                                 &daemon_log("0 DEBUG: $sql", 7);
1780                                 my $res = $known_clients_db->exec_statement($sql);
1782                                 # host is already down
1783                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1785                                 # host is on
1786                                 my $receiver_key = @{@{$res}[0]}[2];
1787                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1788                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1789                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1790                                 if ($error == 0 ) {
1791                                         $send_succeed++ ;
1792                                 }
1793                 }
1795                 if ($send_succeed) {
1796                                 # set outgoing msg at db to deliverd
1797                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1798                                 &daemon_log("0 DEBUG: $sql", 7);
1799                                 my $res = $messaging_db->exec_statement($sql); 
1800                 }
1801         }
1803     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1804     return;
1808 sub watch_for_done_messages {
1809     my ($kernel,$heap) = @_[KERNEL, HEAP];
1811     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1812     #&daemon_log("0 DEBUG: $sql", 7);
1813     my $res = $messaging_db->exec_statement($sql); 
1815     foreach my $hit (@{$res}) {
1816         my $msg_id = @{$hit}[0];
1818         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1819         #&daemon_log("0 DEBUG: $sql", 7); 
1820         my $res = $messaging_db->exec_statement($sql);
1822         # not all usr msgs have been seen till now
1823         if ( ref(@$res[0]) eq "ARRAY") { next; }
1824         
1825         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1826         #&daemon_log("0 DEBUG: $sql", 7);
1827         $res = $messaging_db->exec_statement($sql);
1828     
1829     }
1831     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1832     return;
1836 sub watch_for_old_known_clients {
1837     my ($kernel,$heap) = @_[KERNEL, HEAP];
1839     my $sql_statement = "SELECT * FROM $known_clients_tn";
1840     my $res = $known_clients_db->select_dbentry( $sql_statement );
1842     my $act_time = int(&get_time());
1844     while ( my ($hit_num, $hit) = each %$res) {
1845         my $expired_timestamp = int($hit->{'timestamp'});
1846         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1847         my $dt = DateTime->new( year   => $1,
1848                 month  => $2,
1849                 day    => $3,
1850                 hour   => $4,
1851                 minute => $5,
1852                 second => $6,
1853                 );
1855         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1856         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1857         if ($act_time > $expired_timestamp) {
1858             my $hostname = $hit->{'hostname'};
1859             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1860             my $del_res = $known_clients_db->exec_statement($del_sql);
1862             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1863         }
1865     }
1867     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1871 sub watch_for_next_tasks {
1872     my ($kernel,$heap) = @_[KERNEL, HEAP];
1874     my $sql = "SELECT * FROM $incoming_tn";
1875     my $res = $incoming_db->select_dbentry($sql);
1877     while ( my ($hit_num, $hit) = each %$res) {
1878         my $headertag = $hit->{'headertag'};
1879         if ($headertag =~ /^answer_(\d+)/) {
1880             # do not start processing, this message is for a still running POE::Wheel
1881             next;
1882         }
1883         my $message_id = $hit->{'id'};
1884         $kernel->yield('next_task', $hit);
1886         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1887         my $res = $incoming_db->exec_statement($sql);
1888     }
1890     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1894 sub get_ldap_handle {
1895         my ($session_id) = @_;
1896         my $heap;
1897         my $ldap_handle;
1899         if (not defined $session_id ) { $session_id = 0 };
1900         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1902         if ($session_id == 0) {
1903                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1904                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1905                 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
1907         } else {
1908                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1909                 if( defined $session_reference ) {
1910                         $heap = $session_reference->get_heap();
1911                 }
1913                 if (not defined $heap) {
1914                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1915                         return;
1916                 }
1918                 # TODO: This "if" is nonsense, because it doesn't prove that the
1919                 #       used handle is still valid - or if we've to reconnect...
1920                 #if (not exists $heap->{ldap_handle}) {
1921                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1922                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
1923                         $heap->{ldap_handle} = $ldap_handle;
1924                 #}
1925         }
1926         return $ldap_handle;
1930 sub change_fai_state {
1931     my ($st, $targets, $session_id) = @_;
1932     $session_id = 0 if not defined $session_id;
1933     # Set FAI state to localboot
1934     my %mapActions= (
1935         reboot    => '',
1936         update    => 'softupdate',
1937         localboot => 'localboot',
1938         reinstall => 'install',
1939         rescan    => '',
1940         wake      => '',
1941         memcheck  => 'memcheck',
1942         sysinfo   => 'sysinfo',
1943         install   => 'install',
1944     );
1946     # Return if this is unknown
1947     if (!exists $mapActions{ $st }){
1948         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1949       return;
1950     }
1952     my $state= $mapActions{ $st };
1954     my $ldap_handle = &get_ldap_handle($session_id);
1955     if( defined($ldap_handle) ) {
1957       # Build search filter for hosts
1958         my $search= "(&(objectClass=GOhard)";
1959         foreach (@{$targets}){
1960             $search.= "(macAddress=$_)";
1961         }
1962         $search.= ")";
1964       # If there's any host inside of the search string, procress them
1965         if (!($search =~ /macAddress/)){
1966             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1967             return;
1968         }
1970       # Perform search for Unit Tag
1971       my $mesg = $ldap_handle->search(
1972           base   => $ldap_base,
1973           scope  => 'sub',
1974           attrs  => ['dn', 'FAIstate', 'objectClass'],
1975           filter => "$search"
1976           );
1978           if ($mesg->count) {
1979                   my @entries = $mesg->entries;
1980                   if (0 == @entries) {
1981                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
1982                   }
1984                   foreach my $entry (@entries) {
1985                           # Only modify entry if it is not set to '$state'
1986                           if ($entry->get_value("FAIstate") ne "$state"){
1987                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1988                                   my $result;
1989                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1990                                   if (exists $tmp{'FAIobject'}){
1991                                           if ($state eq ''){
1992                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1993                                                           delete => [ FAIstate => [] ] ]);
1994                                           } else {
1995                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1996                                                           replace => [ FAIstate => $state ] ]);
1997                                           }
1998                                   } elsif ($state ne ''){
1999                                           $result= $ldap_handle->modify($entry->dn, changes => [
2000                                                   add     => [ objectClass => 'FAIobject' ],
2001                                                   add     => [ FAIstate => $state ] ]);
2002                                   }
2004                                   # Errors?
2005                                   if ($result->code){
2006                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2007                                   }
2008                           } else {
2009                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2010                           }  
2011                   }
2012           } else {
2013                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2014           }
2016     # if no ldap handle defined
2017     } else {
2018         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2019     }
2021         return;
2025 sub change_goto_state {
2026     my ($st, $targets, $session_id) = @_;
2027     $session_id = 0  if not defined $session_id;
2029     # Switch on or off?
2030     my $state= $st eq 'active' ? 'active': 'locked';
2032     my $ldap_handle = &get_ldap_handle($session_id);
2033     if( defined($ldap_handle) ) {
2035       # Build search filter for hosts
2036       my $search= "(&(objectClass=GOhard)";
2037       foreach (@{$targets}){
2038         $search.= "(macAddress=$_)";
2039       }
2040       $search.= ")";
2042       # If there's any host inside of the search string, procress them
2043       if (!($search =~ /macAddress/)){
2044         return;
2045       }
2047       # Perform search for Unit Tag
2048       my $mesg = $ldap_handle->search(
2049           base   => $ldap_base,
2050           scope  => 'sub',
2051           attrs  => ['dn', 'gotoMode'],
2052           filter => "$search"
2053           );
2055       if ($mesg->count) {
2056         my @entries = $mesg->entries;
2057         foreach my $entry (@entries) {
2059           # Only modify entry if it is not set to '$state'
2060           if ($entry->get_value("gotoMode") ne $state){
2062             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2063             my $result;
2064             $result= $ldap_handle->modify($entry->dn, changes => [
2065                                                 replace => [ gotoMode => $state ] ]);
2067             # Errors?
2068             if ($result->code){
2069               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2070             }
2072           }
2073         }
2074       } else {
2075                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2076           }
2078     }
2082 sub run_recreate_packages_db {
2083     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2084     my $session_id = $session->ID;
2085         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2086         $kernel->yield('create_fai_release_db', $fai_release_tn);
2087         $kernel->yield('create_fai_server_db', $fai_server_tn);
2088         return;
2092 sub run_create_fai_server_db {
2093     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2094     my $session_id = $session->ID;
2095     my $task = POE::Wheel::Run->new(
2096             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2097             StdoutEvent  => "session_run_result",
2098             StderrEvent  => "session_run_debug",
2099             CloseEvent   => "session_run_done",
2100             );
2102     $heap->{task}->{ $task->ID } = $task;
2103     return;
2107 sub create_fai_server_db {
2108     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2109         my $result;
2111         if (not defined $session_id) { $session_id = 0; }
2112     my $ldap_handle = &get_ldap_handle();
2113         if(defined($ldap_handle)) {
2114                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2115                 my $mesg= $ldap_handle->search(
2116                         base   => $ldap_base,
2117                         scope  => 'sub',
2118                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2119                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2120                 );
2121                 if($mesg->{'resultCode'} == 0 &&
2122                    $mesg->count != 0) {
2123                    foreach my $entry (@{$mesg->{entries}}) {
2124                            if($entry->exists('FAIrepository')) {
2125                                    # Add an entry for each Repository configured for server
2126                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2127                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2128                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2129                                                    $result= $fai_server_db->add_dbentry( { 
2130                                                                    table => $table_name,
2131                                                                    primkey => ['server', 'release', 'tag'],
2132                                                                    server => $tmp_url,
2133                                                                    release => $tmp_release,
2134                                                                    sections => $tmp_sections,
2135                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2136                                                            } );
2137                                            }
2138                                    }
2139                            }
2140                    }
2141                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2143                 # TODO: Find a way to post the 'create_packages_list_db' event
2144                 if(not defined($dont_create_packages_list)) {
2145                         &create_packages_list_db(undef, undef, $session_id);
2146                 }
2147         }       
2148     
2149     $ldap_handle->disconnect;
2150         return $result;
2154 sub run_create_fai_release_db {
2155     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2156         my $session_id = $session->ID;
2157     my $task = POE::Wheel::Run->new(
2158             Program => sub { &create_fai_release_db($table_name, $session_id) },
2159             StdoutEvent  => "session_run_result",
2160             StderrEvent  => "session_run_debug",
2161             CloseEvent   => "session_run_done",
2162             );
2164     $heap->{task}->{ $task->ID } = $task;
2165     return;
2169 sub create_fai_release_db {
2170         my ($table_name, $session_id) = @_;
2171         my $result;
2173     # used for logging
2174     if (not defined $session_id) { $session_id = 0; }
2176     my $ldap_handle = &get_ldap_handle();
2177         if(defined($ldap_handle)) {
2178                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2179                 my $mesg= $ldap_handle->search(
2180                         base   => $ldap_base,
2181                         scope  => 'sub',
2182                         attrs  => [],
2183                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2184                 );
2185                 if($mesg->{'resultCode'} == 0 &&
2186                         $mesg->count != 0) {
2187                         # Walk through all possible FAI container ou's
2188                         my @sql_list;
2189                         my $timestamp= &get_time();
2190                         foreach my $ou (@{$mesg->{entries}}) {
2191                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2192                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2193                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2194                                         if(@tmp_array) {
2195                                                 foreach my $entry (@tmp_array) {
2196                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2197                                                                 my $sql= 
2198                                                                 "INSERT INTO $table_name "
2199                                                                 ."(timestamp, release, class, type, state) VALUES ("
2200                                                                 .$timestamp.","
2201                                                                 ."'".$entry->{'release'}."',"
2202                                                                 ."'".$entry->{'class'}."',"
2203                                                                 ."'".$entry->{'type'}."',"
2204                                                                 ."'".$entry->{'state'}."')";
2205                                                                 push @sql_list, $sql;
2206                                                         }
2207                                                 }
2208                                         }
2209                                 }
2210                         }
2212                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2213                         if(@sql_list) {
2214                                 unshift @sql_list, "VACUUM";
2215                                 unshift @sql_list, "DELETE FROM $table_name";
2216                                 $fai_release_db->exec_statementlist(\@sql_list);
2217                         }
2218                         daemon_log("$session_id DEBUG: Done with inserting",7);
2219                 }
2220                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2221         }
2222     $ldap_handle->disconnect;
2223         return $result;
2226 sub get_fai_types {
2227         my $tmp_classes = shift || return undef;
2228         my @result;
2230         foreach my $type(keys %{$tmp_classes}) {
2231                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2232                         my $entry = {
2233                                 type => $type,
2234                                 state => $tmp_classes->{$type}[0],
2235                         };
2236                         push @result, $entry;
2237                 }
2238         }
2240         return @result;
2243 sub get_fai_state {
2244         my $result = "";
2245         my $tmp_classes = shift || return $result;
2247         foreach my $type(keys %{$tmp_classes}) {
2248                 if(defined($tmp_classes->{$type}[0])) {
2249                         $result = $tmp_classes->{$type}[0];
2250                         
2251                 # State is equal for all types in class
2252                         last;
2253                 }
2254         }
2256         return $result;
2259 sub resolve_fai_classes {
2260         my ($fai_base, $ldap_handle, $session_id) = @_;
2261         if (not defined $session_id) { $session_id = 0; }
2262         my $result;
2263         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2264         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2265         my $fai_classes;
2267         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2268         my $mesg= $ldap_handle->search(
2269                 base   => $fai_base,
2270                 scope  => 'sub',
2271                 attrs  => ['cn','objectClass','FAIstate'],
2272                 filter => $fai_filter,
2273         );
2274         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2276         if($mesg->{'resultCode'} == 0 &&
2277                 $mesg->count != 0) {
2278                 foreach my $entry (@{$mesg->{entries}}) {
2279                         if($entry->exists('cn')) {
2280                                 my $tmp_dn= $entry->dn();
2282                                 # Skip classname and ou dn parts for class
2283                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2285                                 # Skip classes without releases
2286                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2287                                         next;
2288                                 }
2290                                 my $tmp_cn= $entry->get_value('cn');
2291                                 my $tmp_state= $entry->get_value('FAIstate');
2293                                 my $tmp_type;
2294                                 # Get FAI type
2295                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2296                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2297                                                 $tmp_type= $oclass;
2298                                                 last;
2299                                         }
2300                                 }
2302                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2303                                         # A Subrelease
2304                                         my @sub_releases = split(/,/, $tmp_release);
2306                                         # Walk through subreleases and build hash tree
2307                                         my $hash;
2308                                         while(my $tmp_sub_release = pop @sub_releases) {
2309                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2310                                         }
2311                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2312                                 } else {
2313                                         # A branch, no subrelease
2314                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2315                                 }
2316                         } elsif (!$entry->exists('cn')) {
2317                                 my $tmp_dn= $entry->dn();
2318                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2320                                 # Skip classes without releases
2321                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2322                                         next;
2323                                 }
2325                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2326                                         # A Subrelease
2327                                         my @sub_releases= split(/,/, $tmp_release);
2329                                         # Walk through subreleases and build hash tree
2330                                         my $hash;
2331                                         while(my $tmp_sub_release = pop @sub_releases) {
2332                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2333                                         }
2334                                         # Remove the last two characters
2335                                         chop($hash);
2336                                         chop($hash);
2338                                         eval('$fai_classes->'.$hash.'= {}');
2339                                 } else {
2340                                         # A branch, no subrelease
2341                                         if(!exists($fai_classes->{$tmp_release})) {
2342                                                 $fai_classes->{$tmp_release} = {};
2343                                         }
2344                                 }
2345                         }
2346                 }
2348                 # The hash is complete, now we can honor the copy-on-write based missing entries
2349                 foreach my $release (keys %$fai_classes) {
2350                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2351                 }
2352         }
2353         return $result;
2356 sub apply_fai_inheritance {
2357        my $fai_classes = shift || return {};
2358        my $tmp_classes;
2360        # Get the classes from the branch
2361        foreach my $class (keys %{$fai_classes}) {
2362                # Skip subreleases
2363                if($class =~ /^ou=.*$/) {
2364                        next;
2365                } else {
2366                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2367                }
2368        }
2370        # Apply to each subrelease
2371        foreach my $subrelease (keys %{$fai_classes}) {
2372                if($subrelease =~ /ou=/) {
2373                        foreach my $tmp_class (keys %{$tmp_classes}) {
2374                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2375                                        $fai_classes->{$subrelease}->{$tmp_class} =
2376                                        deep_copy($tmp_classes->{$tmp_class});
2377                                } else {
2378                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2379                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2380                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2381                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2382                                                }
2383                                        }
2384                                }
2385                        }
2386                }
2387        }
2389        # Find subreleases in deeper levels
2390        foreach my $subrelease (keys %{$fai_classes}) {
2391                if($subrelease =~ /ou=/) {
2392                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2393                                if($subsubrelease =~ /ou=/) {
2394                                        apply_fai_inheritance($fai_classes->{$subrelease});
2395                                }
2396                        }
2397                }
2398        }
2400        return $fai_classes;
2403 sub get_fai_release_entries {
2404         my $tmp_classes = shift || return;
2405         my $parent = shift || "";
2406         my @result = shift || ();
2408         foreach my $entry (keys %{$tmp_classes}) {
2409                 if(defined($entry)) {
2410                         if($entry =~ /^ou=.*$/) {
2411                                 my $release_name = $entry;
2412                                 $release_name =~ s/ou=//g;
2413                                 if(length($parent)>0) {
2414                                         $release_name = $parent."/".$release_name;
2415                                 }
2416                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2417                                 foreach my $bufentry(@bufentries) {
2418                                         push @result, $bufentry;
2419                                 }
2420                         } else {
2421                                 my @types = get_fai_types($tmp_classes->{$entry});
2422                                 foreach my $type (@types) {
2423                                         push @result, 
2424                                         {
2425                                                 'class' => $entry,
2426                                                 'type' => $type->{'type'},
2427                                                 'release' => $parent,
2428                                                 'state' => $type->{'state'},
2429                                         };
2430                                 }
2431                         }
2432                 }
2433         }
2435         return @result;
2438 sub deep_copy {
2439         my $this = shift;
2440         if (not ref $this) {
2441                 $this;
2442         } elsif (ref $this eq "ARRAY") {
2443                 [map deep_copy($_), @$this];
2444         } elsif (ref $this eq "HASH") {
2445                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2446         } else { die "what type is $_?" }
2450 sub session_run_result {
2451     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2452     $kernel->sig(CHLD => "child_reap");
2455 sub session_run_debug {
2456     my $result = $_[ARG0];
2457     print STDERR "$result\n";
2460 sub session_run_done {
2461     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2462     delete $heap->{task}->{$task_id};
2466 sub create_sources_list {
2467         my $session_id = shift;
2468         my $ldap_handle = &main::get_ldap_handle;
2469         my $result="/tmp/gosa_si_tmp_sources_list";
2471         # Remove old file
2472         if(stat($result)) {
2473                 unlink($result);
2474                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2475         }
2477         my $fh;
2478         open($fh, ">$result");
2479         if (not defined $fh) {
2480                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2481                 return undef;
2482         }
2483         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2484                 my $mesg=$ldap_handle->search(
2485                         base    => $main::ldap_server_dn,
2486                         scope   => 'base',
2487                         attrs   => 'FAIrepository',
2488                         filter  => 'objectClass=FAIrepositoryServer'
2489                 );
2490                 if($mesg->count) {
2491                         foreach my $entry(@{$mesg->{'entries'}}) {
2492                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2493                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2494                                         my $line = "deb $server $release";
2495                                         $sections =~ s/,/ /g;
2496                                         $line.= " $sections";
2497                                         print $fh $line."\n";
2498                                 }
2499                         }
2500                 }
2501         } else {
2502                 if (defined $main::ldap_server_dn){
2503                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2504                 } else {
2505                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2506                 }
2507         }
2508         close($fh);
2510         return $result;
2514 sub run_create_packages_list_db {
2515     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2516         my $session_id = $session->ID;
2518         my $task = POE::Wheel::Run->new(
2519                                         Priority => +20,
2520                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2521                                         StdoutEvent  => "session_run_result",
2522                                         StderrEvent  => "session_run_debug",
2523                                         CloseEvent   => "session_run_done",
2524                                         );
2525         $heap->{task}->{ $task->ID } = $task;
2529 sub create_packages_list_db {
2530         my ($ldap_handle, $sources_file, $session_id) = @_;
2531         
2532         # it should not be possible to trigger a recreation of packages_list_db
2533         # while packages_list_db is under construction, so set flag packages_list_under_construction
2534         # which is tested befor recreation can be started
2535         if (-r $packages_list_under_construction) {
2536                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2537                 return;
2538         } else {
2539                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2540                 # set packages_list_under_construction to true
2541                 system("touch $packages_list_under_construction");
2542                 @packages_list_statements=();
2543         }
2545         if (not defined $session_id) { $session_id = 0; }
2546         if (not defined $ldap_handle) { 
2547                 $ldap_handle= &get_ldap_handle();
2549                 if (not defined $ldap_handle) {
2550                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2551                         unlink($packages_list_under_construction);
2552                         return;
2553                 }
2554         }
2555         if (not defined $sources_file) { 
2556                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2557                 $sources_file = &create_sources_list($session_id);
2558         }
2560         if (not defined $sources_file) {
2561                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2562                 unlink($packages_list_under_construction);
2563                 return;
2564         }
2566         my $line;
2568         open(CONFIG, "<$sources_file") or do {
2569                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2570                 unlink($packages_list_under_construction);
2571                 return;
2572         };
2574         # Read lines
2575         while ($line = <CONFIG>){
2576                 # Unify
2577                 chop($line);
2578                 $line =~ s/^\s+//;
2579                 $line =~ s/^\s+/ /;
2581                 # Strip comments
2582                 $line =~ s/#.*$//g;
2584                 # Skip empty lines
2585                 if ($line =~ /^\s*$/){
2586                         next;
2587                 }
2589                 # Interpret deb line
2590                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2591                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2592                         my $section;
2593                         foreach $section (split(' ', $sections)){
2594                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2595                         }
2596                 }
2597         }
2599         close (CONFIG);
2602         find(\&cleanup_and_extract, keys( %repo_dirs ));
2603         &main::strip_packages_list_statements();
2604         unshift @packages_list_statements, "VACUUM";
2605         $packages_list_db->exec_statementlist(\@packages_list_statements);
2606         unlink($packages_list_under_construction);
2607         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2608         return;
2611 # This function should do some intensive task to minimize the db-traffic
2612 sub strip_packages_list_statements {
2613     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2614         my @new_statement_list=();
2615         my $hash;
2616         my $insert_hash;
2617         my $update_hash;
2618         my $delete_hash;
2619         my $local_timestamp=get_time();
2621         foreach my $existing_entry (@existing_entries) {
2622                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2623         }
2625         foreach my $statement (@packages_list_statements) {
2626                 if($statement =~ /^INSERT/i) {
2627                         # Assign the values from the insert statement
2628                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2629                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2630                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2631                                 # If section or description has changed, update the DB
2632                                 if( 
2633                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2634                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2635                                 ) {
2636                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2637                                 }
2638                         } else {
2639                                 # Insert a non-existing entry to db
2640                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2641                         }
2642                 } elsif ($statement =~ /^UPDATE/i) {
2643                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2644                         /^update\s+?$main::packages_list_tn\s+?set\s+?template\s*?=\s*?'(.*?)'\s+?where\s+?package\s*?=\s*?'(.*?)'\s+?and\s+?version\s*?=\s*?'(.*?)'\s*?;$/si;
2645                         foreach my $distribution (keys %{$hash}) {
2646                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2647                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2648                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2649                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2650                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2651                                                 my $section;
2652                                                 my $description;
2653                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2654                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2655                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2656                                                 }
2657                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2658                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2659                                                 }
2660                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2661                                         }
2662                                 }
2663                         }
2664                 }
2665         }
2667         # TODO: Check for orphaned entries
2669         # unroll the insert_hash
2670         foreach my $distribution (keys %{$insert_hash}) {
2671                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2672                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2673                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2674                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2675                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2676                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2677                                 ."'$local_timestamp')";
2678                         }
2679                 }
2680         }
2682         # unroll the update hash
2683         foreach my $distribution (keys %{$update_hash}) {
2684                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2685                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2686                                 my $set = "";
2687                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2688                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2689                                 }
2690                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2691                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2692                                 }
2693                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2694                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2695                                 }
2696                                 if(defined($set) and length($set) > 0) {
2697                                         $set .= "timestamp = '$local_timestamp'";
2698                                 } else {
2699                                         next;
2700                                 }
2701                                 push @new_statement_list, 
2702                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2703                                         ." distribution = '$distribution'"
2704                                         ." AND package = '$package'"
2705                                         ." AND version = '$version'";
2706                         }
2707                 }
2708         }
2710         @packages_list_statements = @new_statement_list;
2714 sub parse_package_info {
2715     my ($baseurl, $dist, $section, $session_id)= @_;
2716     my ($package);
2717     if (not defined $session_id) { $session_id = 0; }
2718     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2719     $repo_dirs{ "${repo_path}/pool" } = 1;
2721     foreach $package ("Packages.gz"){
2722         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2723         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2724         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2725     }
2726     
2730 sub get_package {
2731     my ($url, $dest, $session_id)= @_;
2732     if (not defined $session_id) { $session_id = 0; }
2734     my $tpath = dirname($dest);
2735     -d "$tpath" || mkpath "$tpath";
2737     # This is ugly, but I've no time to take a look at "how it works in perl"
2738     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2739         system("gunzip -cd '$dest' > '$dest.in'");
2740         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2741         unlink($dest);
2742         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2743     } else {
2744         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2745     }
2746     return 0;
2750 sub parse_package {
2751     my ($path, $dist, $srv_path, $session_id)= @_;
2752     if (not defined $session_id) { $session_id = 0;}
2753     my ($package, $version, $section, $description);
2754     my $PACKAGES;
2755     my $timestamp = &get_time();
2757     if(not stat("$path.in")) {
2758         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2759         return;
2760     }
2762     open($PACKAGES, "<$path.in");
2763     if(not defined($PACKAGES)) {
2764         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2765         return;
2766     }
2768     # Read lines
2769     while (<$PACKAGES>){
2770         my $line = $_;
2771         # Unify
2772         chop($line);
2774         # Use empty lines as a trigger
2775         if ($line =~ /^\s*$/){
2776             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2777             push(@packages_list_statements, $sql);
2778             $package = "none";
2779             $version = "none";
2780             $section = "none";
2781             $description = "none"; 
2782             next;
2783         }
2785         # Trigger for package name
2786         if ($line =~ /^Package:\s/){
2787             ($package)= ($line =~ /^Package: (.*)$/);
2788             next;
2789         }
2791         # Trigger for version
2792         if ($line =~ /^Version:\s/){
2793             ($version)= ($line =~ /^Version: (.*)$/);
2794             next;
2795         }
2797         # Trigger for description
2798         if ($line =~ /^Description:\s/){
2799             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2800             next;
2801         }
2803         # Trigger for section
2804         if ($line =~ /^Section:\s/){
2805             ($section)= ($line =~ /^Section: (.*)$/);
2806             next;
2807         }
2809         # Trigger for filename
2810         if ($line =~ /^Filename:\s/){
2811             my ($filename) = ($line =~ /^Filename: (.*)$/);
2812             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2813             next;
2814         }
2815     }
2817     close( $PACKAGES );
2818     unlink( "$path.in" );
2822 sub store_fileinfo {
2823     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2825     my %fileinfo = (
2826         'package' => $package,
2827         'dist' => $dist,
2828         'version' => $vers,
2829     );
2831     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2835 sub cleanup_and_extract {
2836     my $fileinfo = $repo_files{ $File::Find::name };
2838     if( defined $fileinfo ) {
2840         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2841         my $sql;
2842         my $package = $fileinfo->{ 'package' };
2843         my $newver = $fileinfo->{ 'version' };
2845         mkpath($dir);
2846         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2848                 if( -f "$dir/DEBIAN/templates" ) {
2850                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 7);
2852                         my $tmpl= "";
2853                         {
2854                                 local $/=undef;
2855                                 open FILE, "$dir/DEBIAN/templates";
2856                                 $tmpl = &encode_base64(<FILE>);
2857                                 close FILE;
2858                         }
2859                         rmtree("$dir/DEBIAN/templates");
2861                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2862                 push @packages_list_statements, $sql;
2863                 }
2864     }
2866     return;
2870 sub register_at_foreign_servers {   
2871     my ($kernel) = $_[KERNEL];
2873     # hole alle bekannten server aus known_server_db
2874     my $server_sql = "SELECT * FROM $known_server_tn";
2875     my $server_res = $known_server_db->exec_statement($server_sql);
2877     # no entries in known_server_db
2878     if (not ref(@$server_res[0]) eq "ARRAY") { 
2879         # TODO
2880     }
2882     # detect already connected clients
2883     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2884     my $client_res = $known_clients_db->exec_statement($client_sql);
2886     # send my server details to all other gosa-si-server within the network
2887     foreach my $hit (@$server_res) {
2888         my $hostname = @$hit[0];
2889         my $hostkey = &create_passwd;
2891         # add already connected clients to registration message 
2892         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2893         &add_content2xml_hash($myhash, 'key', $hostkey);
2894         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2895         
2896         # build registration message and send it
2897         my $foreign_server_msg = &create_xml_string($myhash);
2898         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2899     }
2900     
2901     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2902     return;
2906 #==== MAIN = main ==============================================================
2907 #  parse commandline options
2908 Getopt::Long::Configure( "bundling" );
2909 GetOptions("h|help" => \&usage,
2910         "c|config=s" => \$cfg_file,
2911         "f|foreground" => \$foreground,
2912         "v|verbose+" => \$verbose,
2913         "no-arp+" => \$no_arp,
2914            );
2916 #  read and set config parameters
2917 &check_cmdline_param ;
2918 &read_configfile($cfg_file, %cfg_defaults);
2919 &check_pid;
2921 $SIG{CHLD} = 'IGNORE';
2923 # forward error messages to logfile
2924 if( ! $foreground ) {
2925   open( STDIN,  '+>/dev/null' );
2926   open( STDOUT, '+>&STDIN'    );
2927   open( STDERR, '+>&STDIN'    );
2930 # Just fork, if we are not in foreground mode
2931 if( ! $foreground ) { 
2932     chdir '/'                 or die "Can't chdir to /: $!";
2933     $pid = fork;
2934     setsid                    or die "Can't start a new session: $!";
2935     umask 0;
2936 } else { 
2937     $pid = $$; 
2940 # Do something useful - put our PID into the pid_file
2941 if( 0 != $pid ) {
2942     open( LOCK_FILE, ">$pid_file" );
2943     print LOCK_FILE "$pid\n";
2944     close( LOCK_FILE );
2945     if( !$foreground ) { 
2946         exit( 0 ) 
2947     };
2950 # parse head url and revision from svn
2951 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2952 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2953 $server_headURL = defined $1 ? $1 : 'unknown' ;
2954 $server_revision = defined $2 ? $2 : 'unknown' ;
2955 if ($server_headURL =~ /\/tag\// || 
2956         $server_headURL =~ /\/branches\// ) {
2957     $server_status = "stable"; 
2958 } else {
2959     $server_status = "developmental" ;
2963 daemon_log(" ", 1);
2964 daemon_log("$0 started!", 1);
2965 daemon_log("status: $server_status", 1);
2966 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2968 # connect to incoming_db
2969 unlink($incoming_file_name);
2970 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2971 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2973 # connect to gosa-si job queue
2974 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2975 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2977 # connect to known_clients_db
2978 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2979 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2981 # connect to foreign_clients_db
2982 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2983 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2985 # connect to known_server_db
2986 unlink($known_server_file_name);
2987 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2988 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2990 # connect to login_usr_db
2991 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2992 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2994 # connect to fai_server_db and fai_release_db
2995 unlink($fai_server_file_name);
2996 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2997 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2999 unlink($fai_release_file_name);
3000 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3001 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3003 # connect to packages_list_db
3004 #unlink($packages_list_file_name);
3005 unlink($packages_list_under_construction);
3006 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3007 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3009 # connect to messaging_db
3010 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3011 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3014 # create xml object used for en/decrypting
3015 $xml = new XML::Simple();
3018 # foreign servers 
3019 my @foreign_server_list;
3021 # add foreign server from cfg file
3022 if ($foreign_server_string ne "") {
3023     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3024     foreach my $foreign_server (@cfg_foreign_server_list) {
3025         push(@foreign_server_list, $foreign_server);
3026     }
3029 # add foreign server from dns
3030 my @tmp_servers;
3031 if ( !$server_domain) {
3032     # Try our DNS Searchlist
3033     for my $domain(get_dns_domains()) {
3034         chomp($domain);
3035         my @tmp_domains= &get_server_addresses($domain);
3036         if(@tmp_domains) {
3037             for my $tmp_server(@tmp_domains) {
3038                 push @tmp_servers, $tmp_server;
3039             }
3040         }
3041     }
3042     if(@tmp_servers && length(@tmp_servers)==0) {
3043         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3044     }
3045 } else {
3046     @tmp_servers = &get_server_addresses($server_domain);
3047     if( 0 == @tmp_servers ) {
3048         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3049     }
3051 foreach my $server (@tmp_servers) { 
3052     unshift(@foreign_server_list, $server); 
3054 # eliminate duplicate entries
3055 @foreign_server_list = &del_doubles(@foreign_server_list);
3056 my $all_foreign_server = join(", ", @foreign_server_list);
3057 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3059 # add all found foreign servers to known_server
3060 my $act_timestamp = &get_time();
3061 foreach my $foreign_server (@foreign_server_list) {
3063         # do not add myself to known_server_db
3064         if (&is_local($foreign_server)) { next; }
3065         ######################################
3067     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3068             primkey=>['hostname'],
3069             hostname=>$foreign_server,
3070             status=>'not_jet_registered',
3071             hostkey=>"none",
3072             timestamp=>$act_timestamp,
3073             } );
3077 # import all modules
3078 &import_modules;
3079 # check wether all modules are gosa-si valid passwd check
3080 &password_check;
3083 POE::Component::Server::TCP->new(
3084     Alias => "TCP_SERVER",
3085         Port => $server_port,
3086         ClientInput => sub {
3087         my ($kernel, $input) = @_[KERNEL, ARG0];
3088         push(@tasks, $input);
3089         push(@msgs_to_decrypt, $input);
3090         $kernel->yield("msg_to_decrypt");
3091         },
3092     InlineStates => {
3093         msg_to_decrypt => \&msg_to_decrypt,
3094         next_task => \&next_task,
3095         task_result => \&handle_task_result,
3096         task_done   => \&handle_task_done,
3097         task_debug  => \&handle_task_debug,
3098         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3099     }
3100 );
3102 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3104 # create session for repeatedly checking the job queue for jobs
3105 POE::Session->create(
3106         inline_states => {
3107                 _start => \&session_start,
3108         register_at_foreign_servers => \&register_at_foreign_servers,
3109         sig_handler => \&sig_handler,
3110         next_task => \&next_task,
3111         task_result => \&handle_task_result,
3112         task_done   => \&handle_task_done,
3113         task_debug  => \&handle_task_debug,
3114         watch_for_next_tasks => \&watch_for_next_tasks,
3115         watch_for_new_messages => \&watch_for_new_messages,
3116         watch_for_delivery_messages => \&watch_for_delivery_messages,
3117         watch_for_done_messages => \&watch_for_done_messages,
3118                 watch_for_new_jobs => \&watch_for_new_jobs,
3119         watch_for_modified_jobs => \&watch_for_modified_jobs,
3120         watch_for_done_jobs => \&watch_for_done_jobs,
3121         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3122         watch_for_old_known_clients => \&watch_for_old_known_clients,
3123         create_packages_list_db => \&run_create_packages_list_db,
3124         create_fai_server_db => \&run_create_fai_server_db,
3125         create_fai_release_db => \&run_create_fai_release_db,
3126                 recreate_packages_db => \&run_recreate_packages_db,
3127         session_run_result => \&session_run_result,
3128         session_run_debug => \&session_run_debug,
3129         session_run_done => \&session_run_done,
3130         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3131         }
3132 );
3135 POE::Kernel->run();
3136 exit;