Code

825b040bff5e1fd98f54f733190953be0639e257
[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_ip, $gosa_port, $gosa_timeout,
88     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
89     $wake_on_lan_passwd,
90 );
92 # additional variable which should be globaly accessable
93 our $server_address;
94 our $server_mac_address;
95 our $gosa_address;
96 our $no_arp;
97 our $verbose;
98 our $forground;
99 our $cfg_file;
100 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
102 # dak variables
103 our $dak_base_directory;
104 our $dak_signing_keys_directory;
105 our $dak_queue_directory;
106 our $dak_user;
108 # specifies the verbosity of the daemon_log
109 $verbose = 0 ;
111 # if foreground is not null, script will be not forked to background
112 $foreground = 0 ;
114 # specifies the timeout seconds while checking the online status of a registrating client
115 $ping_timeout = 5;
117 $no_arp = 0;
118 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
119 my @packages_list_statements;
120 my $watch_for_new_jobs_in_progress = 0;
122 # holds all incoming decrypted messages
123 our $incoming_db;
124 our $incoming_tn = 'incoming';
125 my $incoming_file_name;
126 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
127         "timestamp DEFAULT 'none'", 
128         "headertag DEFAULT 'none'",
129                 "targettag DEFAULT 'none'",
130         "xmlmessage DEFAULT 'none'",
131         "module DEFAULT 'none'",
132         "sessionid DEFAULT '0'",
133         );
135 # holds all gosa jobs
136 our $job_db;
137 our $job_queue_tn = 'jobs';
138 my $job_queue_file_name;
139 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
140                 "timestamp DEFAULT 'none'", 
141                 "status DEFAULT 'none'", 
142                 "result DEFAULT 'none'", 
143                 "progress DEFAULT 'none'", 
144         "headertag DEFAULT 'none'", 
145                 "targettag DEFAULT 'none'", 
146                 "xmlmessage DEFAULT 'none'", 
147                 "macaddress DEFAULT 'none'",
148                 "plainname DEFAULT 'none'",
149                 );
151 # holds all other gosa-si-server
152 our $known_server_db;
153 our $known_server_tn = "known_server";
154 my $known_server_file_name;
155 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
157 # holds all registrated clients
158 our $known_clients_db;
159 our $known_clients_tn = "known_clients";
160 my $known_clients_file_name;
161 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
163 # holds all registered clients at a foreign server
164 our $foreign_clients_db;
165 our $foreign_clients_tn = "foreign_clients"; 
166 my $foreign_clients_file_name;
167 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
169 # holds all logged in user at each client 
170 our $login_users_db;
171 our $login_users_tn = "login_users";
172 my $login_users_file_name;
173 my @login_users_col_names = ("client", "user", "timestamp");
175 # holds all fai server, the debian release and tag
176 our $fai_server_db;
177 our $fai_server_tn = "fai_server"; 
178 my $fai_server_file_name;
179 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
181 our $fai_release_db;
182 our $fai_release_tn = "fai_release"; 
183 my $fai_release_file_name;
184 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
186 # holds all packages available from different repositories
187 our $packages_list_db;
188 our $packages_list_tn = "packages_list";
189 my $packages_list_file_name;
190 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
191 my $outdir = "/tmp/packages_list_db";
192 my $arch = "i386"; 
194 # holds all messages which should be delivered to a user
195 our $messaging_db;
196 our $messaging_tn = "messaging"; 
197 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
198         "flag", "direction", "delivery_time", "message", "timestamp" );
199 my $messaging_file_name;
201 # path to directory to store client install log files
202 our $client_fai_log_dir = "/var/log/fai"; 
204 # queue which stores taskes until one of the $max_children children are ready to process the task
205 my @tasks = qw();
206 my @msgs_to_decrypt = qw();
207 my $max_children = 2;
210 %cfg_defaults = (
211 "general" => {
212     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
213     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
214     },
215 "server" => {
216     "port" => [\$server_port, "20081"],
217     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
218     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
219     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
220     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
221     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
222     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
223     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
224     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
225     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
226     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
227     "repo-path"            => [\$repo_path, '/srv/www/repository'],
228     "ldap-uri"             => [\$ldap_uri, ""],
229     "ldap-base"            => [\$ldap_base, ""],
230     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
231     "ldap-admin-password"  => [\$ldap_admin_password, ""],
232     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
233     "max-clients"          => [\$max_clients, 10],
234     "wol-password"           => [\$wake_on_lan_passwd, ""],
235     },
236 "GOsaPackages" => {
237     "ip" => [\$gosa_ip, "0.0.0.0"],
238     "port" => [\$gosa_port, "20082"],
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         "dak-base" => [\$dak_base_directory, "/srv/archive"],
244         "dak-keyring" => [\$dak_signing_keys_directory, "/srv/archive/keyrings"],
245         "dak-queue" => [\$dak_queue_directory, "/srv/archive/queue"],
246         "dak-user" => [\$dak_user, "deb-dak"],
247     },
248 "ClientPackages" => {
249     "key" => [\$ClientPackages_key, "none"],
250     },
251 "ServerPackages"=> {
252     "address"      => [\$foreign_server_string, ""],
253     "domain"  => [\$server_domain, ""],
254     "key"     => [\$ServerPackages_key, "none"],
255     "key-lifetime" => [\$foreign_servers_register_delay, 120],
257 );
260 #===  FUNCTION  ================================================================
261 #         NAME:  usage
262 #   PARAMETERS:  nothing
263 #      RETURNS:  nothing
264 #  DESCRIPTION:  print out usage text to STDERR
265 #===============================================================================
266 sub usage {
267     print STDERR << "EOF" ;
268 usage: $prg [-hvf] [-c config]
270            -h        : this (help) message
271            -c <file> : config file
272            -f        : foreground, process will not be forked to background
273            -v        : be verbose (multiple to increase verbosity)
274            -no-arp   : starts $prg without connection to arp module
275  
276 EOF
277     print "\n" ;
281 #===  FUNCTION  ================================================================
282 #         NAME:  read_configfile
283 #   PARAMETERS:  cfg_file - string -
284 #      RETURNS:  nothing
285 #  DESCRIPTION:  read cfg_file and set variables
286 #===============================================================================
287 sub read_configfile {
288     my $cfg;
289     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
290         if( -r $cfg_file ) {
291             $cfg = Config::IniFiles->new( -file => $cfg_file );
292         } else {
293             print STDERR "Couldn't read config file!\n";
294         }
295     } else {
296         $cfg = Config::IniFiles->new() ;
297     }
298     foreach my $section (keys %cfg_defaults) {
299         foreach my $param (keys %{$cfg_defaults{ $section }}) {
300             my $pinfo = $cfg_defaults{ $section }{ $param };
301             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
302         }
303     }
307 #===  FUNCTION  ================================================================
308 #         NAME:  logging
309 #   PARAMETERS:  level - string - default 'info'
310 #                msg - string -
311 #                facility - string - default 'LOG_DAEMON'
312 #      RETURNS:  nothing
313 #  DESCRIPTION:  function for logging
314 #===============================================================================
315 sub daemon_log {
316     # log into log_file
317     my( $msg, $level ) = @_;
318     if(not defined $msg) { return }
319     if(not defined $level) { $level = 1 }
320     if(defined $log_file){
321         open(LOG_HANDLE, ">>$log_file");
322         chmod 0600, $log_file;
323         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
324             print STDERR "cannot open $log_file: $!";
325             return 
326         }
327         chomp($msg);
328         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
329         if($level <= $verbose){
330             my ($seconds, $minutes, $hours, $monthday, $month,
331                     $year, $weekday, $yearday, $sommertime) = localtime(time);
332             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
333             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
334             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
335             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
336             $month = $monthnames[$month];
337             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
338             $year+=1900;
339             my $name = $prg;
341             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
342             print LOG_HANDLE $log_msg;
343             if( $foreground ) { 
344                 print STDERR $log_msg;
345             }
346         }
347         close( LOG_HANDLE );
348     }
352 #===  FUNCTION  ================================================================
353 #         NAME:  check_cmdline_param
354 #   PARAMETERS:  nothing
355 #      RETURNS:  nothing
356 #  DESCRIPTION:  validates commandline parameter
357 #===============================================================================
358 sub check_cmdline_param () {
359     my $err_config;
360     my $err_counter = 0;
361         if(not defined($cfg_file)) {
362                 $cfg_file = "/etc/gosa-si/server.conf";
363                 if(! -r $cfg_file) {
364                         $err_config = "please specify a config file";
365                         $err_counter += 1;
366                 }
367     }
368     if( $err_counter > 0 ) {
369         &usage( "", 1 );
370         if( defined( $err_config)) { print STDERR "$err_config\n"}
371         print STDERR "\n";
372         exit( -1 );
373     }
377 #===  FUNCTION  ================================================================
378 #         NAME:  check_pid
379 #   PARAMETERS:  nothing
380 #      RETURNS:  nothing
381 #  DESCRIPTION:  handels pid processing
382 #===============================================================================
383 sub check_pid {
384     $pid = -1;
385     # Check, if we are already running
386     if( open(LOCK_FILE, "<$pid_file") ) {
387         $pid = <LOCK_FILE>;
388         if( defined $pid ) {
389             chomp( $pid );
390             if( -f "/proc/$pid/stat" ) {
391                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
392                 if( $stat ) {
393                                         daemon_log("ERROR: Already running",1);
394                     close( LOCK_FILE );
395                     exit -1;
396                 }
397             }
398         }
399         close( LOCK_FILE );
400         unlink( $pid_file );
401     }
403     # create a syslog msg if it is not to possible to open PID file
404     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
405         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
406         if (open(LOCK_FILE, '<', $pid_file)
407                 && ($pid = <LOCK_FILE>))
408         {
409             chomp($pid);
410             $msg .= "(PID $pid)\n";
411         } else {
412             $msg .= "(unable to read PID)\n";
413         }
414         if( ! ($foreground) ) {
415             openlog( $0, "cons,pid", "daemon" );
416             syslog( "warning", $msg );
417             closelog();
418         }
419         else {
420             print( STDERR " $msg " );
421         }
422         exit( -1 );
423     }
426 #===  FUNCTION  ================================================================
427 #         NAME:  import_modules
428 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
429 #                are stored
430 #      RETURNS:  nothing
431 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
432 #                state is on is imported by "require 'file';"
433 #===============================================================================
434 sub import_modules {
435     daemon_log(" ", 1);
437     if (not -e $modules_path) {
438         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
439     }
441     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
442     while (defined (my $file = readdir (DIR))) {
443         if (not $file =~ /(\S*?).pm$/) {
444             next;
445         }
446                 my $mod_name = $1;
448         if( $file =~ /ArpHandler.pm/ ) {
449             if( $no_arp > 0 ) {
450                 next;
451             }
452         }
453         
454         eval { require $file; };
455         if ($@) {
456             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
457             daemon_log("$@", 5);
458                 } else {
459                         my $info = eval($mod_name.'::get_module_info()');
460                         # Only load module if get_module_info() returns a non-null object
461                         if( $info ) {
462                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
463                                 $known_modules->{$mod_name} = $info;
464                                 daemon_log("0 INFO: module $mod_name loaded", 5);
465                         }
466                 }
467     }   
468     close (DIR);
472 #===  FUNCTION  ================================================================
473 #         NAME:  sig_int_handler
474 #   PARAMETERS:  signal - string - signal arose from system
475 #      RETURNS:  noting
476 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
477 #===============================================================================
478 sub sig_int_handler {
479     my ($signal) = @_;
481 #       if (defined($ldap_handle)) {
482 #               $ldap_handle->disconnect;
483 #       }
484     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
485     
487     daemon_log("shutting down gosa-si-server", 1);
488     system("kill `ps -C gosa-si-server -o pid=`");
490 $SIG{INT} = \&sig_int_handler;
493 sub check_key_and_xml_validity {
494     my ($crypted_msg, $module_key, $session_id) = @_;
495     my $msg;
496     my $msg_hash;
497     my $error_string;
498     eval{
499         $msg = &decrypt_msg($crypted_msg, $module_key);
501         if ($msg =~ /<xml>/i){
502             $msg =~ s/\s+/ /g;  # just for better daemon_log
503             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
504             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
506             ##############
507             # check header
508             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
509             my $header_l = $msg_hash->{'header'};
510             if( 1 > @{$header_l} ) { die 'empty header tag'; }
511             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
512             my $header = @{$header_l}[0];
513             if( 0 == length $header) { die 'empty string in header tag'; }
515             ##############
516             # check source
517             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
518             my $source_l = $msg_hash->{'source'};
519             if( 1 > @{$source_l} ) { die 'empty source tag'; }
520             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
521             my $source = @{$source_l}[0];
522             if( 0 == length $source) { die 'source error'; }
524             ##############
525             # check target
526             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
527             my $target_l = $msg_hash->{'target'};
528             if( 1 > @{$target_l} ) { die 'empty target tag'; }
529         }
530     };
531     if($@) {
532         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
533         $msg = undef;
534         $msg_hash = undef;
535     }
537     return ($msg, $msg_hash);
541 sub check_outgoing_xml_validity {
542     my ($msg, $session_id) = @_;
544     my $msg_hash;
545     eval{
546         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
548         ##############
549         # check header
550         my $header_l = $msg_hash->{'header'};
551         if( 1 != @{$header_l} ) {
552             die 'no or more than one headers specified';
553         }
554         my $header = @{$header_l}[0];
555         if( 0 == length $header) {
556             die 'header has length 0';
557         }
559         ##############
560         # check source
561         my $source_l = $msg_hash->{'source'};
562         if( 1 != @{$source_l} ) {
563             die 'no or more than 1 sources specified';
564         }
565         my $source = @{$source_l}[0];
566         if( 0 == length $source) {
567             die 'source has length 0';
568         }
569         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
570                 $source =~ /^GOSA$/i ) {
571             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
572         }
573         
574         ##############
575         # check target  
576         my $target_l = $msg_hash->{'target'};
577         if( 0 == @{$target_l} ) {
578             die "no targets specified";
579         }
580         foreach my $target (@$target_l) {
581             if( 0 == length $target) {
582                 die "target has length 0";
583             }
584             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
585                     $target =~ /^GOSA$/i ||
586                     $target =~ /^\*$/ ||
587                     $target =~ /KNOWN_SERVER/i ||
588                     $target =~ /JOBDB/i ||
589                     $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 ){
590                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
591             }
592         }
593     };
594     if($@) {
595         daemon_log("$session_id WARNING: outgoing msg is not gosa-si envelope conform: ", 5);
596         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 5);
597         $msg_hash = undef;
598     }
600     return ($msg_hash);
604 sub input_from_known_server {
605     my ($input, $remote_ip, $session_id) = @_ ;  
606     my ($msg, $msg_hash, $module);
608     my $sql_statement= "SELECT * FROM known_server";
609     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
611     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
612         my $host_name = $hit->{hostname};
613         if( not $host_name =~ "^$remote_ip") {
614             next;
615         }
616         my $host_key = $hit->{hostkey};
617         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
618         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
620         # check if module can open msg envelope with module key
621         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
622         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
623             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
624             daemon_log("$@", 8);
625             next;
626         }
627         else {
628             $msg = $tmp_msg;
629             $msg_hash = $tmp_msg_hash;
630             $module = "ServerPackages";
631             last;
632         }
633     }
635     if( (!$msg) || (!$msg_hash) || (!$module) ) {
636         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
637     }
638   
639     return ($msg, $msg_hash, $module);
643 sub input_from_known_client {
644     my ($input, $remote_ip, $session_id) = @_ ;  
645     my ($msg, $msg_hash, $module);
647     my $sql_statement= "SELECT * FROM known_clients";
648     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
649     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
650         my $host_name = $hit->{hostname};
651         if( not $host_name =~ /^$remote_ip:\d*$/) {
652                 next;
653                 }
654         my $host_key = $hit->{hostkey};
655         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
656         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
658         # check if module can open msg envelope with module key
659         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
661         if( (!$msg) || (!$msg_hash) ) {
662             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
663             &daemon_log("$@", 8);
664             next;
665         }
666         else {
667             $module = "ClientPackages";
668             last;
669         }
670     }
672     if( (!$msg) || (!$msg_hash) || (!$module) ) {
673         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
674     }
676     return ($msg, $msg_hash, $module);
680 sub input_from_unknown_host {
681     no strict "refs";
682     my ($input, $session_id) = @_ ;
683     my ($msg, $msg_hash, $module);
684     my $error_string;
685     
686         my %act_modules = %$known_modules;
687         
688     while( my ($mod, $info) = each(%act_modules)) {
690         # check a key exists for this module
691         my $module_key = ${$mod."_key"};
692         if( not defined $module_key ) {
693             if( $mod eq 'ArpHandler' ) {
694                 next;
695             }
696             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
697             next;
698         }
699         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
701         # check if module can open msg envelope with module key
702         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
703         if( (not defined $msg) || (not defined $msg_hash) ) {
704             next;
705         }
706         else {
707             $module = $mod;
708             last;
709         }
710     }
712     if( (!$msg) || (!$msg_hash) || (!$module)) {
713         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
714     }
716     return ($msg, $msg_hash, $module);
720 sub create_ciphering {
721     my ($passwd) = @_;
722         if((!defined($passwd)) || length($passwd)==0) {
723                 $passwd = "";
724         }
725     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
726     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
727     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
728     $my_cipher->set_iv($iv);
729     return $my_cipher;
733 sub encrypt_msg {
734     my ($msg, $key) = @_;
735     my $my_cipher = &create_ciphering($key);
736     my $len;
737     {
738             use bytes;
739             $len= 16-length($msg)%16;
740     }
741     $msg = "\0"x($len).$msg;
742     $msg = $my_cipher->encrypt($msg);
743     chomp($msg = &encode_base64($msg));
744     # there are no newlines allowed inside msg
745     $msg=~ s/\n//g;
746     return $msg;
750 sub decrypt_msg {
752     my ($msg, $key) = @_ ;
753     $msg = &decode_base64($msg);
754     my $my_cipher = &create_ciphering($key);
755     $msg = $my_cipher->decrypt($msg); 
756     $msg =~ s/\0*//g;
757     return $msg;
761 sub get_encrypt_key {
762     my ($target) = @_ ;
763     my $encrypt_key;
764     my $error = 0;
766     # target can be in known_server
767     if( not defined $encrypt_key ) {
768         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
769         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
770         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
771             my $host_name = $hit->{hostname};
772             if( $host_name ne $target ) {
773                 next;
774             }
775             $encrypt_key = $hit->{hostkey};
776             last;
777         }
778     }
780     # target can be in known_client
781     if( not defined $encrypt_key ) {
782         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
783         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
784         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
785             my $host_name = $hit->{hostname};
786             if( $host_name ne $target ) {
787                 next;
788             }
789             $encrypt_key = $hit->{hostkey};
790             last;
791         }
792     }
794     return $encrypt_key;
798 #===  FUNCTION  ================================================================
799 #         NAME:  open_socket
800 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
801 #                [PeerPort] string necessary if port not appended by PeerAddr
802 #      RETURNS:  socket IO::Socket::INET
803 #  DESCRIPTION:  open a socket to PeerAddr
804 #===============================================================================
805 sub open_socket {
806     my ($PeerAddr, $PeerPort) = @_ ;
807     if(defined($PeerPort)){
808         $PeerAddr = $PeerAddr.":".$PeerPort;
809     }
810     my $socket;
811     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
812             Porto => "tcp",
813             Type => SOCK_STREAM,
814             Timeout => 5,
815             );
816     if(not defined $socket) {
817         return;
818     }
819 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
820     return $socket;
824 # moved to GosaSupportDaemon: 03-06-2008: rettenbe
825 #===  FUNCTION  ================================================================
826 #         NAME:  get_ip 
827 #   PARAMETERS:  interface name (i.e. eth0)
828 #      RETURNS:  (ip address) 
829 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
830 #===============================================================================
831 #sub get_ip {
832 #       my $ifreq= shift;
833 #       my $result= "";
834 #       my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
835 #       my $proto= getprotobyname('ip');
837 #       socket SOCKET, PF_INET, SOCK_DGRAM, $proto
838 #               or die "socket: $!";
840 #       if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
841 #               my ($if, $sin)    = unpack 'a16 a16', $ifreq;
842 #               my ($port, $addr) = sockaddr_in $sin;
843 #               my $ip            = inet_ntoa $addr;
845 #               if ($ip && length($ip) > 0) {
846 #                       $result = $ip;
847 #               }
848 #       }
850 #       return $result;
851 #}
854 sub get_local_ip_for_remote_ip {
855         my $remote_ip= shift;
856         my $result="0.0.0.0";
858         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
859                 if($remote_ip eq "127.0.0.1") {
860                         $result = "127.0.0.1";
861                 } else {
862                         my $PROC_NET_ROUTE= ('/proc/net/route');
864                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
865                                 or die "Could not open $PROC_NET_ROUTE";
867                         my @ifs = <PROC_NET_ROUTE>;
869                         close(PROC_NET_ROUTE);
871                         # Eat header line
872                         shift @ifs;
873                         chomp @ifs;
874                         foreach my $line(@ifs) {
875                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
876                                 my $destination;
877                                 my $mask;
878                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
879                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
880                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
881                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
882                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
883                                         # destination matches route, save mac and exit
884                                         $result= &get_ip($Iface);
885                                         last;
886                                 }
887                         }
888                 }
889         } else {
890                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
891         }
892         return $result;
896 sub send_msg_to_target {
897     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
898     my $error = 0;
899     my $header;
900     my $timestamp = &get_time();
901     my $new_status;
902     my $act_status;
903     my ($sql_statement, $res);
904   
905     if( $msg_header ) {
906         $header = "'$msg_header'-";
907     } else {
908         $header = "";
909     }
911         # Patch the source ip
912         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
913                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
914                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
915         }
917     # encrypt xml msg
918     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
920     # opensocket
921     my $socket = &open_socket($address);
922     if( !$socket ) {
923         daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
924         $error++;
925     }
926     
927     if( $error == 0 ) {
928         # send xml msg
929         print $socket $crypted_msg."\n";
931         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
932         daemon_log("$session_id DEBUG: message:\n$msg", 9);
933         
934     }
936     # close socket in any case
937     if( $socket ) {
938         close $socket;
939     }
941     if( $error > 0 ) { $new_status = "down"; }
942     else { $new_status = $msg_header; }
945     # known_clients
946     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
947     $res = $known_clients_db->select_dbentry($sql_statement);
948     if( keys(%$res) == 1) {
949         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
950         if ($act_status eq "down" && $new_status eq "down") {
951             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
952             $res = $known_clients_db->del_dbentry($sql_statement);
953             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
954         } else { 
955             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
956             $res = $known_clients_db->update_dbentry($sql_statement);
957             if($new_status eq "down"){
958                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
959             } else {
960                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
961             }
962         }
963     }
965     # known_server
966     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
967     $res = $known_server_db->select_dbentry($sql_statement);
968     if( keys(%$res) == 1) {
969         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
970         if ($act_status eq "down" && $new_status eq "down") {
971             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
972             $res = $known_server_db->del_dbentry($sql_statement);
973             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
974         } 
975         else { 
976             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
977             $res = $known_server_db->update_dbentry($sql_statement);
978             if($new_status eq "down"){
979                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
980             } else {
981                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
982             }
983         }
984     }
985     return $error; 
989 sub update_jobdb_status_for_send_msgs {
990     my ($answer, $error) = @_;
991     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
992         my $jobdb_id = $1;
993             
994         # sending msg faild
995         if( $error ) {
996             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
997                 my $sql_statement = "UPDATE $job_queue_tn ".
998                     "SET status='error', result='can not deliver msg, please consult log file' ".
999                     "WHERE id=$jobdb_id";
1000                 my $res = $job_db->update_dbentry($sql_statement);
1001             }
1003         # sending msg was successful
1004         } else {
1005             my $sql_statement = "UPDATE $job_queue_tn ".
1006                 "SET status='done' ".
1007                 "WHERE id=$jobdb_id AND status='processed'";
1008             my $res = $job_db->update_dbentry($sql_statement);
1009         }
1010     }
1014 sub sig_handler {
1015         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1016         daemon_log("0 INFO got signal '$signal'", 1); 
1017         $kernel->sig_handled();
1018         return;
1022 sub msg_to_decrypt {
1023     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1024     my $session_id = $session->ID;
1025     my ($msg, $msg_hash, $module);
1026     my $error = 0;
1028     # hole neue msg aus @msgs_to_decrypt
1029     my $next_msg = shift @msgs_to_decrypt;
1030     
1031     # entschlüssle sie
1033     # msg is from a new client or gosa
1034     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1035     # msg is from a gosa-si-server
1036     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1037         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1038     }
1039     # msg is from a gosa-si-client
1040     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1041         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1042     }
1043     # an error occurred
1044     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1045         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1046         # could not understand a msg from its server the client cause a re-registering process
1047         daemon_log("$session_id INFO cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1048                 "' to cause a re-registering of the client if necessary", 5);
1049         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1050         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1051         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1052             my $host_name = $hit->{'hostname'};
1053             my $host_key = $hit->{'hostkey'};
1054             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1055             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1056             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1057         }
1058         $error++;
1059     }
1062     my $header;
1063     my $target;
1064     my $source;
1065     my $done = 0;
1066     my $sql;
1067     my $res;
1069     # check whether this message should be processed here
1070     if ($error == 0) {
1071         $header = @{$msg_hash->{'header'}}[0];
1072         $target = @{$msg_hash->{'target'}}[0];
1073         $source = @{$msg_hash->{'source'}}[0];
1074                 my $not_found_in_known_clients_db = 0;
1075                 my $not_found_in_known_server_db = 0;
1076                 my $not_found_in_foreign_clients_db = 0;
1077         my $local_address;
1078         my ($target_ip, $target_port) = split(':', $target);
1079                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1080                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1081                 } else {
1082             $local_address = $server_address;
1083         }
1085         # target and source is equal to GOSA -> process here
1086         if (not $done) {
1087             if ($target eq "GOSA" && $source eq "GOSA") {
1088                 $done = 1;                    
1089             }
1090         }
1092         # target is own address without forward_to_gosa-tag -> process here
1093         if (not $done) {
1094             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1095                 $done = 1;
1096                 if ($source eq "GOSA") {
1097                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1098                 }
1099                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1100             }
1101         }
1103         # target is a client address in known_clients -> process here
1104                 if (not $done) {
1105                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1106                                 $res = $known_clients_db->select_dbentry($sql);
1107                                 if (keys(%$res) > 0) {
1108                                                 $done = 1; 
1109                                                 my $hostname = $res->{1}->{'hostname'};
1110                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1111                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1112                                 } else {
1113                                                 $not_found_in_known_clients_db = 1;
1114                                 }
1115                 }
1116         
1117         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1118         if (not $done) {
1119             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1120             my $gosa_at;
1121             my $gosa_session_id;
1122             if (($target eq $local_address) && (defined $forward_to_gosa)){
1123                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1124                 if ($gosa_at ne $local_address) {
1125                     $done = 1;
1126                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1127                 }
1128             }
1129         }
1131         # if message should be processed here -> add message to incoming_db
1132                 if ($done) {
1133                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1134                                 # so gosa-si-server knows how to process this kind of messages
1135                                 if ($header =~ /^gosa_/ || $header =~ /job_/) {
1136                                                 $module = "GosaPackages";
1137                                 }
1139                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1140                                                                 primkey=>[],
1141                                                                 headertag=>$header,
1142                                                                 targettag=>$target,
1143                                                                 xmlmessage=>&encode_base64($msg),
1144                                                                 timestamp=>&get_time,
1145                                                                 module=>$module,
1146                                                                 sessionid=>$session_id,
1147                                                                 } );
1148                 }
1150         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1151         if (not $done) {
1152             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1153             my $gosa_at;
1154             my $gosa_session_id;
1155             if (($target eq $local_address) && (defined $forward_to_gosa)){
1156                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1157                 if ($gosa_at eq $local_address) {
1158                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1159                     if( defined $session_reference ) {
1160                         $heap = $session_reference->get_heap();
1161                     }
1162                     if(exists $heap->{'client'}) {
1163                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1164                         $heap->{'client'}->put($msg);
1165                     }
1166                     $done = 1;
1167                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1168                 }
1169             }
1171         }
1173         # target is a client address in foreign_clients -> forward to registration server
1174         if (not $done) {
1175             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1176             $res = $foreign_clients_db->select_dbentry($sql);
1177             if (keys(%$res) > 0) {
1178                 my $hostname = $res->{1}->{'hostname'};
1179                 my $regserver = $res->{1}->{'regserver'};
1180                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1181                 my $res = $known_server_db->select_dbentry($sql);
1182                 if (keys(%$res) > 0) {
1183                     my $regserver_key = $res->{1}->{'hostkey'};
1184                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1185                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1186                     if ($source eq "GOSA") {
1187                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1188                     }
1189                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1190                 }
1191                 $done = 1;
1192                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1193             } else {
1194                                 $not_found_in_foreign_clients_db = 1;
1195                         }
1196         }
1198         # target is a server address -> forward to server
1199         if (not $done) {
1200             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1201             $res = $known_server_db->select_dbentry($sql);
1202             if (keys(%$res) > 0) {
1203                 my $hostkey = $res->{1}->{'hostkey'};
1205                 if ($source eq "GOSA") {
1206                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1207                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1209                 }
1211                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1212                 $done = 1;
1213                 #print STDERR "target is a server address -> forward to server\n";
1214             } else {
1215                                 $not_found_in_known_server_db = 1;
1216                         }
1217         }
1219                 
1220                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1221                 if ( $not_found_in_foreign_clients_db 
1222                                                 && $not_found_in_known_server_db
1223                                                 && $not_found_in_known_clients_db) {
1224                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1225                                                                 primkey=>[],
1226                                                                 headertag=>$header,
1227                                                                 targettag=>$target,
1228                                                                 xmlmessage=>&encode_base64($msg),
1229                                                                 timestamp=>&get_time,
1230                                                                 module=>$module,
1231                                                                 sessionid=>$session_id,
1232                                                                 } );
1233                                 $done = 1;
1234                 }
1237         if (not $done) {
1238             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1239             if ($source eq "GOSA") {
1240                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1241                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1243                 my $session_reference = $kernel->ID_id_to_session($session_id);
1244                 if( defined $session_reference ) {
1245                     $heap = $session_reference->get_heap();
1246                 }
1247                 if(exists $heap->{'client'}) {
1248                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1249                     $heap->{'client'}->put($error_msg);
1250                 }
1251             }
1252         }
1254     }
1256     return;
1260 sub next_task {
1261     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1262     my $running_task = POE::Wheel::Run->new(
1263             Program => sub { process_task($session, $heap, $task) },
1264             StdioFilter => POE::Filter::Reference->new(),
1265             StdoutEvent  => "task_result",
1266             StderrEvent  => "task_debug",
1267             CloseEvent   => "task_done",
1268             );
1269     $heap->{task}->{ $running_task->ID } = $running_task;
1272 sub handle_task_result {
1273     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1274     my $client_answer = $result->{'answer'};
1275     if( $client_answer =~ s/session_id=(\d+)$// ) {
1276         my $session_id = $1;
1277         if( defined $session_id ) {
1278             my $session_reference = $kernel->ID_id_to_session($session_id);
1279             if( defined $session_reference ) {
1280                 $heap = $session_reference->get_heap();
1281             }
1282         }
1284         if(exists $heap->{'client'}) {
1285             $heap->{'client'}->put($client_answer);
1286         }
1287     }
1288     $kernel->sig(CHLD => "child_reap");
1291 sub handle_task_debug {
1292     my $result = $_[ARG0];
1293     print STDERR "$result\n";
1296 sub handle_task_done {
1297     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1298     delete $heap->{task}->{$task_id};
1301 sub process_task {
1302     no strict "refs";
1303     my ($session, $heap, $task) = @_;
1304     my $error = 0;
1305     my $answer_l;
1306     my ($answer_header, @answer_target_l, $answer_source);
1307     my $client_answer = "";
1309     # prepare all variables needed to process message
1310     #my $msg = $task->{'xmlmessage'};
1311     my $msg = &decode_base64($task->{'xmlmessage'});
1312     my $incoming_id = $task->{'id'};
1313     my $module = $task->{'module'};
1314     my $header =  $task->{'headertag'};
1315     my $session_id = $task->{'sessionid'};
1316     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1317     my $source = @{$msg_hash->{'source'}}[0];
1318     
1319     # set timestamp of incoming client uptodate, so client will not 
1320     # be deleted from known_clients because of expiration
1321     my $act_time = &get_time();
1322     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1323     my $res = $known_clients_db->exec_statement($sql);
1325     ######################
1326     # process incoming msg
1327     if( $error == 0) {
1328         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1329         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1330         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1332         if ( 0 < @{$answer_l} ) {
1333             my $answer_str = join("\n", @{$answer_l});
1334             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1335                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1336             }
1337             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1338         } else {
1339             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1340         }
1342     }
1343     if( !$answer_l ) { $error++ };
1345     ########
1346     # answer
1347     if( $error == 0 ) {
1349         foreach my $answer ( @{$answer_l} ) {
1350             # check outgoing msg to xml validity
1351             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1352             if( not defined $answer_hash ) { next; }
1353             
1354             $answer_header = @{$answer_hash->{'header'}}[0];
1355             @answer_target_l = @{$answer_hash->{'target'}};
1356             $answer_source = @{$answer_hash->{'source'}}[0];
1358             # deliver msg to all targets 
1359             foreach my $answer_target ( @answer_target_l ) {
1361                 # targets of msg are all gosa-si-clients in known_clients_db
1362                 if( $answer_target eq "*" ) {
1363                     # answer is for all clients
1364                     my $sql_statement= "SELECT * FROM known_clients";
1365                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1366                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1367                         my $host_name = $hit->{hostname};
1368                         my $host_key = $hit->{hostkey};
1369                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1370                         &update_jobdb_status_for_send_msgs($answer, $error);
1371                     }
1372                 }
1374                 # targets of msg are all gosa-si-server in known_server_db
1375                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1376                     # answer is for all server in known_server
1377                     my $sql_statement= "SELECT * FROM $known_server_tn";
1378                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1379                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1380                         my $host_name = $hit->{hostname};
1381                         my $host_key = $hit->{hostkey};
1382                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1383                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1384                         &update_jobdb_status_for_send_msgs($answer, $error);
1385                     }
1386                 }
1388                 # target of msg is GOsa
1389                                 elsif( $answer_target eq "GOSA" ) {
1390                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1391                                         my $add_on = "";
1392                     if( defined $session_id ) {
1393                         $add_on = ".session_id=$session_id";
1394                     }
1395                     # answer is for GOSA and has to returned to connected client
1396                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1397                     $client_answer = $gosa_answer.$add_on;
1398                 }
1400                 # target of msg is job queue at this host
1401                 elsif( $answer_target eq "JOBDB") {
1402                     $answer =~ /<header>(\S+)<\/header>/;   
1403                     my $header;
1404                     if( defined $1 ) { $header = $1; }
1405                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1406                     &update_jobdb_status_for_send_msgs($answer, $error);
1407                 }
1409                 # target of msg is a mac address
1410                 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 ) {
1411                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1412                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1413                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1414                     my $found_ip_flag = 0;
1415                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1416                         my $host_name = $hit->{hostname};
1417                         my $host_key = $hit->{hostkey};
1418                         $answer =~ s/$answer_target/$host_name/g;
1419                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1420                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1421                         &update_jobdb_status_for_send_msgs($answer, $error);
1422                         $found_ip_flag++ ;
1423                     }   
1424                     if( $found_ip_flag == 0) {
1425                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1426                     }
1428                 #  answer is for one specific host   
1429                 } else {
1430                     # get encrypt_key
1431                     my $encrypt_key = &get_encrypt_key($answer_target);
1432                     if( not defined $encrypt_key ) {
1433                         # unknown target
1434                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1435                         next;
1436                     }
1437                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1438                     &update_jobdb_status_for_send_msgs($answer, $error);
1439                 }
1440             }
1441         }
1442     }
1444     my $filter = POE::Filter::Reference->new();
1445     my %result = ( 
1446             status => "seems ok to me",
1447             answer => $client_answer,
1448             );
1450     my $output = $filter->put( [ \%result ] );
1451     print @$output;
1456 sub session_start {
1457     my ($kernel) = $_[KERNEL];
1458     $global_kernel = $kernel;
1459     $kernel->yield('register_at_foreign_servers');
1460         $kernel->yield('create_fai_server_db', $fai_server_tn );
1461         $kernel->yield('create_fai_release_db', $fai_release_tn );
1462     $kernel->yield('watch_for_next_tasks');
1463         $kernel->sig(USR1 => "sig_handler");
1464         $kernel->sig(USR2 => "recreate_packages_db");
1465         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1466         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1467         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1468     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1469         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1470     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1475 sub watch_for_done_jobs {
1476     my ($kernel,$heap) = @_[KERNEL, HEAP];
1478     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE status='done'";
1479         my $res = $job_db->select_dbentry( $sql_statement );
1481     while( my ($id, $hit) = each %{$res} ) {
1482         my $jobdb_id = $hit->{id};
1483         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1484         my $res = $job_db->del_dbentry($sql_statement); 
1485     }
1487     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1491 sub watch_for_new_jobs {
1492         if($watch_for_new_jobs_in_progress == 0) {
1493                 $watch_for_new_jobs_in_progress = 1;
1494                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1496                 # check gosa job queue for jobs with executable timestamp
1497                 my $timestamp = &get_time();
1498                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1499                 my $res = $job_db->exec_statement( $sql_statement );
1501                 # Merge all new jobs that would do the same actions
1502                 my @drops;
1503                 my $hits;
1504                 foreach my $hit (reverse @{$res} ) {
1505                         my $macaddress= lc @{$hit}[8];
1506                         my $headertag= @{$hit}[5];
1507                         if(
1508                                 defined($hits->{$macaddress}) &&
1509                                 defined($hits->{$macaddress}->{$headertag}) &&
1510                                 defined($hits->{$macaddress}->{$headertag}[0])
1511                         ) {
1512                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1513                         }
1514                         $hits->{$macaddress}->{$headertag}= $hit;
1515                 }
1517                 # Delete new jobs with a matching job in state 'processing'
1518                 foreach my $macaddress (keys %{$hits}) {
1519                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1520                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1521                                 if(defined($jobdb_id)) {
1522                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1523                                         my $res = $job_db->exec_statement( $sql_statement );
1524                                         foreach my $hit (@{$res}) {
1525                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1526                                         }
1527                                 } else {
1528                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1529                                 }
1530                         }
1531                 }
1533                 # Commit deletion
1534                 $job_db->exec_statementlist(\@drops);
1536                 # Look for new jobs that could be executed
1537                 foreach my $macaddress (keys %{$hits}) {
1539                         # Look if there is an executing job
1540                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1541                         my $res = $job_db->exec_statement( $sql_statement );
1543                         # Skip new jobs for host if there is a processing job
1544                         if(defined($res) and defined @{$res}[0]) {
1545                                 next;
1546                         }
1548                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1549                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1550                                 if(defined($jobdb_id)) {
1551                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1553                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1554                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1555                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1557                                         # expect macaddress is unique!!!!!!
1558                                         my $target = $res_hash->{1}->{hostname};
1560                                         # change header
1561                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1563                                         # add sqlite_id
1564                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1566                                         $job_msg =~ /<header>(\S+)<\/header>/;
1567                                         my $header = $1 ;
1568                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1570                                         # update status in job queue to 'processing'
1571                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1572                                         my $res = $job_db->update_dbentry($sql_statement);
1573 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1575                                         # We don't want parallel processing
1576                                         last;
1577                                 }
1578                         }
1579                 }
1581                 $watch_for_new_jobs_in_progress = 0;
1582                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1583         }
1587 sub watch_for_new_messages {
1588     my ($kernel,$heap) = @_[KERNEL, HEAP];
1589     my @coll_user_msg;   # collection list of outgoing messages
1590     
1591     # check messaging_db for new incoming messages with executable timestamp
1592     my $timestamp = &get_time();
1593     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1594     my $res = $messaging_db->exec_statement( $sql_statement );
1595         foreach my $hit (@{$res}) {
1597         # create outgoing messages
1598         my $message_to = @{$hit}[3];
1599         # translate message_to to plain login name
1600         my @message_to_l = split(/,/, $message_to);  
1601                 my %receiver_h; 
1602                 foreach my $receiver (@message_to_l) {
1603                         if ($receiver =~ /^u_([\s\S]*)$/) {
1604                                 $receiver_h{$1} = 0;
1605                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1606                                 my $group_name = $1;
1607                                 # fetch all group members from ldap and add them to receiver hash
1608                                 my $ldap_handle = &get_ldap_handle();
1609                                 if (defined $ldap_handle) {
1610                                                 my $mesg = $ldap_handle->search(
1611                                                                                 base => $ldap_base,
1612                                                                                 scope => 'sub',
1613                                                                                 attrs => ['memberUid'],
1614                                                                                 filter => "cn=$group_name",
1615                                                                                 );
1616                                                 if ($mesg->count) {
1617                                                                 my @entries = $mesg->entries;
1618                                                                 foreach my $entry (@entries) {
1619                                                                                 my @receivers= $entry->get_value("memberUid");
1620                                                                                 foreach my $receiver (@receivers) { 
1621                                                                                                 $receiver_h{$1} = 0;
1622                                                                                 }
1623                                                                 }
1624                                                 } 
1625                                                 # translating errors ?
1626                                                 if ($mesg->code) {
1627                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1628                                                 }
1629                                 # ldap handle error ?           
1630                                 } else {
1631                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1632                                 }
1633                         } else {
1634                                 my $sbjct = &encode_base64(@{$hit}[1]);
1635                                 my $msg = &encode_base64(@{$hit}[7]);
1636                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1637                         }
1638                 }
1639                 my @receiver_l = keys(%receiver_h);
1641         my $message_id = @{$hit}[0];
1643         #add each outgoing msg to messaging_db
1644         my $receiver;
1645         foreach $receiver (@receiver_l) {
1646             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1647                 "VALUES ('".
1648                 $message_id."', '".    # id
1649                 @{$hit}[1]."', '".     # subject
1650                 @{$hit}[2]."', '".     # message_from
1651                 $receiver."', '".      # message_to
1652                 "none"."', '".         # flag
1653                 "out"."', '".          # direction
1654                 @{$hit}[6]."', '".     # delivery_time
1655                 @{$hit}[7]."', '".     # message
1656                 $timestamp."'".     # timestamp
1657                 ")";
1658             &daemon_log("M DEBUG: $sql_statement", 1);
1659             my $res = $messaging_db->exec_statement($sql_statement);
1660             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1661         }
1663         # set incoming message to flag d=deliverd
1664         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1665         &daemon_log("M DEBUG: $sql_statement", 7);
1666         $res = $messaging_db->update_dbentry($sql_statement);
1667         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1668     }
1670     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1671     return;
1674 sub watch_for_delivery_messages {
1675     my ($kernel, $heap) = @_[KERNEL, HEAP];
1677     # select outgoing messages
1678     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1679     #&daemon_log("0 DEBUG: $sql", 7);
1680     my $res = $messaging_db->exec_statement( $sql_statement );
1681     
1682     # build out msg for each    usr
1683     foreach my $hit (@{$res}) {
1684         my $receiver = @{$hit}[3];
1685         my $msg_id = @{$hit}[0];
1686         my $subject = @{$hit}[1];
1687         my $message = @{$hit}[7];
1689         # resolve usr -> host where usr is logged in
1690         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1691         #&daemon_log("0 DEBUG: $sql", 7);
1692         my $res = $login_users_db->exec_statement($sql);
1694         # reciver is logged in nowhere
1695         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1697                 my $send_succeed = 0;
1698                 foreach my $hit (@$res) {
1699                                 my $receiver_host = @$hit[0];
1700                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1702                                 # fetch key to encrypt msg propperly for usr/host
1703                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1704                                 &daemon_log("0 DEBUG: $sql", 7);
1705                                 my $res = $known_clients_db->exec_statement($sql);
1707                                 # host is already down
1708                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1710                                 # host is on
1711                                 my $receiver_key = @{@{$res}[0]}[2];
1712                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1713                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1714                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1715                                 if ($error == 0 ) {
1716                                         $send_succeed++ ;
1717                                 }
1718                 }
1720                 if ($send_succeed) {
1721                                 # set outgoing msg at db to deliverd
1722                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1723                                 &daemon_log("0 DEBUG: $sql", 7);
1724                                 my $res = $messaging_db->exec_statement($sql); 
1725                 }
1726         }
1728     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1729     return;
1733 sub watch_for_done_messages {
1734     my ($kernel,$heap) = @_[KERNEL, HEAP];
1736     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1737     #&daemon_log("0 DEBUG: $sql", 7);
1738     my $res = $messaging_db->exec_statement($sql); 
1740     foreach my $hit (@{$res}) {
1741         my $msg_id = @{$hit}[0];
1743         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1744         #&daemon_log("0 DEBUG: $sql", 7); 
1745         my $res = $messaging_db->exec_statement($sql);
1747         # not all usr msgs have been seen till now
1748         if ( ref(@$res[0]) eq "ARRAY") { next; }
1749         
1750         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1751         #&daemon_log("0 DEBUG: $sql", 7);
1752         $res = $messaging_db->exec_statement($sql);
1753     
1754     }
1756     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1757     return;
1761 sub watch_for_old_known_clients {
1762     my ($kernel,$heap) = @_[KERNEL, HEAP];
1764     my $sql_statement = "SELECT * FROM $known_clients_tn";
1765     my $res = $known_clients_db->select_dbentry( $sql_statement );
1767     my $act_time = int(&get_time());
1769     while ( my ($hit_num, $hit) = each %$res) {
1770         my $expired_timestamp = int($hit->{'timestamp'});
1771         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1772         my $dt = DateTime->new( year   => $1,
1773                 month  => $2,
1774                 day    => $3,
1775                 hour   => $4,
1776                 minute => $5,
1777                 second => $6,
1778                 );
1780         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1781         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1782         if ($act_time > $expired_timestamp) {
1783             my $hostname = $hit->{'hostname'};
1784             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1785             my $del_res = $known_clients_db->exec_statement($del_sql);
1787             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1788         }
1790     }
1792     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1796 sub watch_for_next_tasks {
1797     my ($kernel,$heap) = @_[KERNEL, HEAP];
1799     my $sql = "SELECT * FROM $incoming_tn";
1800     my $res = $incoming_db->select_dbentry($sql);
1802     while ( my ($hit_num, $hit) = each %$res) {
1803         my $headertag = $hit->{'headertag'};
1804         if ($headertag =~ /^answer_(\d+)/) {
1805             # do not start processing, this message is for a still running POE::Wheel
1806             next;
1807         }
1808         my $message_id = $hit->{'id'};
1809         $kernel->yield('next_task', $hit);
1811         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1812         my $res = $incoming_db->exec_statement($sql);
1813     }
1815     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1819 sub get_ldap_handle {
1820         my ($session_id) = @_;
1821         my $heap;
1822         my $ldap_handle;
1824         if (not defined $session_id ) { $session_id = 0 };
1825         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1827         if ($session_id == 0) {
1828                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1829                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1830                 $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!"); 
1832         } else {
1833                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1834                 if( defined $session_reference ) {
1835                         $heap = $session_reference->get_heap();
1836                 }
1838                 if (not defined $heap) {
1839                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1840                         return;
1841                 }
1843                 # TODO: This "if" is nonsense, because it doesn't prove that the
1844                 #       used handle is still valid - or if we've to reconnect...
1845                 #if (not exists $heap->{ldap_handle}) {
1846                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1847                         $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!"); 
1848                         $heap->{ldap_handle} = $ldap_handle;
1849                 #}
1850         }
1851         return $ldap_handle;
1855 sub change_fai_state {
1856     my ($st, $targets, $session_id) = @_;
1857     $session_id = 0 if not defined $session_id;
1858     # Set FAI state to localboot
1859     my %mapActions= (
1860         reboot    => '',
1861         update    => 'softupdate',
1862         localboot => 'localboot',
1863         reinstall => 'install',
1864         rescan    => '',
1865         wake      => '',
1866         memcheck  => 'memcheck',
1867         sysinfo   => 'sysinfo',
1868         install   => 'install',
1869     );
1871     # Return if this is unknown
1872     if (!exists $mapActions{ $st }){
1873         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1874       return;
1875     }
1877     my $state= $mapActions{ $st };
1879     my $ldap_handle = &get_ldap_handle($session_id);
1880     if( defined($ldap_handle) ) {
1882       # Build search filter for hosts
1883         my $search= "(&(objectClass=GOhard)";
1884         foreach (@{$targets}){
1885             $search.= "(macAddress=$_)";
1886         }
1887         $search.= ")";
1889       # If there's any host inside of the search string, procress them
1890         if (!($search =~ /macAddress/)){
1891             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1892             return;
1893         }
1895       # Perform search for Unit Tag
1896       my $mesg = $ldap_handle->search(
1897           base   => $ldap_base,
1898           scope  => 'sub',
1899           attrs  => ['dn', 'FAIstate', 'objectClass'],
1900           filter => "$search"
1901           );
1903           if ($mesg->count) {
1904                   my @entries = $mesg->entries;
1905                   if (0 == @entries) {
1906                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
1907                   }
1909                   foreach my $entry (@entries) {
1910                           # Only modify entry if it is not set to '$state'
1911                           if ($entry->get_value("FAIstate") ne "$state"){
1912                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1913                                   my $result;
1914                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1915                                   if (exists $tmp{'FAIobject'}){
1916                                           if ($state eq ''){
1917                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1918                                                           delete => [ FAIstate => [] ] ]);
1919                                           } else {
1920                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1921                                                           replace => [ FAIstate => $state ] ]);
1922                                           }
1923                                   } elsif ($state ne ''){
1924                                           $result= $ldap_handle->modify($entry->dn, changes => [
1925                                                   add     => [ objectClass => 'FAIobject' ],
1926                                                   add     => [ FAIstate => $state ] ]);
1927                                   }
1929                                   # Errors?
1930                                   if ($result->code){
1931                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1932                                   }
1933                           } else {
1934                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1935                           }  
1936                   }
1937           } else {
1938                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
1939           }
1941     # if no ldap handle defined
1942     } else {
1943         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1944     }
1946         return;
1950 sub change_goto_state {
1951     my ($st, $targets, $session_id) = @_;
1952     $session_id = 0  if not defined $session_id;
1954     # Switch on or off?
1955     my $state= $st eq 'active' ? 'active': 'locked';
1957     my $ldap_handle = &get_ldap_handle($session_id);
1958     if( defined($ldap_handle) ) {
1960       # Build search filter for hosts
1961       my $search= "(&(objectClass=GOhard)";
1962       foreach (@{$targets}){
1963         $search.= "(macAddress=$_)";
1964       }
1965       $search.= ")";
1967       # If there's any host inside of the search string, procress them
1968       if (!($search =~ /macAddress/)){
1969         return;
1970       }
1972       # Perform search for Unit Tag
1973       my $mesg = $ldap_handle->search(
1974           base   => $ldap_base,
1975           scope  => 'sub',
1976           attrs  => ['dn', 'gotoMode'],
1977           filter => "$search"
1978           );
1980       if ($mesg->count) {
1981         my @entries = $mesg->entries;
1982         foreach my $entry (@entries) {
1984           # Only modify entry if it is not set to '$state'
1985           if ($entry->get_value("gotoMode") ne $state){
1987             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1988             my $result;
1989             $result= $ldap_handle->modify($entry->dn, changes => [
1990                                                 replace => [ gotoMode => $state ] ]);
1992             # Errors?
1993             if ($result->code){
1994               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1995             }
1997           }
1998         }
1999       } else {
2000                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2001           }
2003     }
2007 sub run_recreate_packages_db {
2008     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2009     my $session_id = $session->ID;
2010         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 4);
2011         $kernel->yield('create_fai_release_db');
2012         $kernel->yield('create_fai_server_db');
2013         return;
2017 sub run_create_fai_server_db {
2018     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2019     my $session_id = $session->ID;
2020     my $task = POE::Wheel::Run->new(
2021             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2022             StdoutEvent  => "session_run_result",
2023             StderrEvent  => "session_run_debug",
2024             CloseEvent   => "session_run_done",
2025             );
2027     $heap->{task}->{ $task->ID } = $task;
2028     return;
2032 sub create_fai_server_db {
2033     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2034         my $result;
2036         if (not defined $session_id) { $session_id = 0; }
2037     my $ldap_handle = &get_ldap_handle();
2038         if(defined($ldap_handle)) {
2039                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2040                 my $mesg= $ldap_handle->search(
2041                         base   => $ldap_base,
2042                         scope  => 'sub',
2043                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2044                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2045                 );
2046                 if($mesg->{'resultCode'} == 0 &&
2047                    $mesg->count != 0) {
2048                    foreach my $entry (@{$mesg->{entries}}) {
2049                            if($entry->exists('FAIrepository')) {
2050                                    # Add an entry for each Repository configured for server
2051                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2052                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2053                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2054                                                    $result= $fai_server_db->add_dbentry( { 
2055                                                                    table => $table_name,
2056                                                                    primkey => ['server', 'release', 'tag'],
2057                                                                    server => $tmp_url,
2058                                                                    release => $tmp_release,
2059                                                                    sections => $tmp_sections,
2060                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2061                                                            } );
2062                                            }
2063                                    }
2064                            }
2065                    }
2066                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2068                 # TODO: Find a way to post the 'create_packages_list_db' event
2069                 if(not defined($dont_create_packages_list)) {
2070                         &create_packages_list_db(undef, undef, $session_id);
2071                 }
2072         }       
2073     
2074     $ldap_handle->disconnect;
2075         return $result;
2079 sub run_create_fai_release_db {
2080     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2081         my $session_id = $session->ID;
2082     my $task = POE::Wheel::Run->new(
2083             Program => sub { &create_fai_release_db($table_name, $session_id) },
2084             StdoutEvent  => "session_run_result",
2085             StderrEvent  => "session_run_debug",
2086             CloseEvent   => "session_run_done",
2087             );
2089     $heap->{task}->{ $task->ID } = $task;
2090     return;
2094 sub create_fai_release_db {
2095         my ($table_name, $session_id) = @_;
2096         my $result;
2098     # used for logging
2099     if (not defined $session_id) { $session_id = 0; }
2101     my $ldap_handle = &get_ldap_handle();
2102         if(defined($ldap_handle)) {
2103                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2104                 my $mesg= $ldap_handle->search(
2105                         base   => $ldap_base,
2106                         scope  => 'sub',
2107                         attrs  => [],
2108                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2109                 );
2110                 if($mesg->{'resultCode'} == 0 &&
2111                         $mesg->count != 0) {
2112                         # Walk through all possible FAI container ou's
2113                         my @sql_list;
2114                         my $timestamp= &get_time();
2115                         foreach my $ou (@{$mesg->{entries}}) {
2116                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2117                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2118                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2119                                         if(@tmp_array) {
2120                                                 foreach my $entry (@tmp_array) {
2121                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2122                                                                 my $sql= 
2123                                                                 "INSERT INTO $table_name "
2124                                                                 ."(timestamp, release, class, type, state) VALUES ("
2125                                                                 .$timestamp.","
2126                                                                 ."'".$entry->{'release'}."',"
2127                                                                 ."'".$entry->{'class'}."',"
2128                                                                 ."'".$entry->{'type'}."',"
2129                                                                 ."'".$entry->{'state'}."')";
2130                                                                 push @sql_list, $sql;
2131                                                         }
2132                                                 }
2133                                         }
2134                                 }
2135                         }
2137                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2138                         if(@sql_list) {
2139                                 unshift @sql_list, "VACUUM";
2140                                 unshift @sql_list, "DELETE FROM $table_name";
2141                                 $fai_release_db->exec_statementlist(\@sql_list);
2142                         }
2143                         daemon_log("$session_id DEBUG: Done with inserting",7);
2144                 }
2145                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2146         }
2147     $ldap_handle->disconnect;
2148         return $result;
2151 sub get_fai_types {
2152         my $tmp_classes = shift || return undef;
2153         my @result;
2155         foreach my $type(keys %{$tmp_classes}) {
2156                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2157                         my $entry = {
2158                                 type => $type,
2159                                 state => $tmp_classes->{$type}[0],
2160                         };
2161                         push @result, $entry;
2162                 }
2163         }
2165         return @result;
2168 sub get_fai_state {
2169         my $result = "";
2170         my $tmp_classes = shift || return $result;
2172         foreach my $type(keys %{$tmp_classes}) {
2173                 if(defined($tmp_classes->{$type}[0])) {
2174                         $result = $tmp_classes->{$type}[0];
2175                         
2176                 # State is equal for all types in class
2177                         last;
2178                 }
2179         }
2181         return $result;
2184 sub resolve_fai_classes {
2185         my ($fai_base, $ldap_handle, $session_id) = @_;
2186         if (not defined $session_id) { $session_id = 0; }
2187         my $result;
2188         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2189         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2190         my $fai_classes;
2192         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2193         my $mesg= $ldap_handle->search(
2194                 base   => $fai_base,
2195                 scope  => 'sub',
2196                 attrs  => ['cn','objectClass','FAIstate'],
2197                 filter => $fai_filter,
2198         );
2199         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2201         if($mesg->{'resultCode'} == 0 &&
2202                 $mesg->count != 0) {
2203                 foreach my $entry (@{$mesg->{entries}}) {
2204                         if($entry->exists('cn')) {
2205                                 my $tmp_dn= $entry->dn();
2207                                 # Skip classname and ou dn parts for class
2208                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2210                                 # Skip classes without releases
2211                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2212                                         next;
2213                                 }
2215                                 my $tmp_cn= $entry->get_value('cn');
2216                                 my $tmp_state= $entry->get_value('FAIstate');
2218                                 my $tmp_type;
2219                                 # Get FAI type
2220                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2221                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2222                                                 $tmp_type= $oclass;
2223                                                 last;
2224                                         }
2225                                 }
2227                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2228                                         # A Subrelease
2229                                         my @sub_releases = split(/,/, $tmp_release);
2231                                         # Walk through subreleases and build hash tree
2232                                         my $hash;
2233                                         while(my $tmp_sub_release = pop @sub_releases) {
2234                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2235                                         }
2236                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2237                                 } else {
2238                                         # A branch, no subrelease
2239                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2240                                 }
2241                         } elsif (!$entry->exists('cn')) {
2242                                 my $tmp_dn= $entry->dn();
2243                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2245                                 # Skip classes without releases
2246                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2247                                         next;
2248                                 }
2250                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2251                                         # A Subrelease
2252                                         my @sub_releases= split(/,/, $tmp_release);
2254                                         # Walk through subreleases and build hash tree
2255                                         my $hash;
2256                                         while(my $tmp_sub_release = pop @sub_releases) {
2257                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2258                                         }
2259                                         # Remove the last two characters
2260                                         chop($hash);
2261                                         chop($hash);
2263                                         eval('$fai_classes->'.$hash.'= {}');
2264                                 } else {
2265                                         # A branch, no subrelease
2266                                         if(!exists($fai_classes->{$tmp_release})) {
2267                                                 $fai_classes->{$tmp_release} = {};
2268                                         }
2269                                 }
2270                         }
2271                 }
2273                 # The hash is complete, now we can honor the copy-on-write based missing entries
2274                 foreach my $release (keys %$fai_classes) {
2275                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2276                 }
2277         }
2278         return $result;
2281 sub apply_fai_inheritance {
2282        my $fai_classes = shift || return {};
2283        my $tmp_classes;
2285        # Get the classes from the branch
2286        foreach my $class (keys %{$fai_classes}) {
2287                # Skip subreleases
2288                if($class =~ /^ou=.*$/) {
2289                        next;
2290                } else {
2291                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2292                }
2293        }
2295        # Apply to each subrelease
2296        foreach my $subrelease (keys %{$fai_classes}) {
2297                if($subrelease =~ /ou=/) {
2298                        foreach my $tmp_class (keys %{$tmp_classes}) {
2299                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2300                                        $fai_classes->{$subrelease}->{$tmp_class} =
2301                                        deep_copy($tmp_classes->{$tmp_class});
2302                                } else {
2303                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2304                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2305                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2306                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2307                                                }
2308                                        }
2309                                }
2310                        }
2311                }
2312        }
2314        # Find subreleases in deeper levels
2315        foreach my $subrelease (keys %{$fai_classes}) {
2316                if($subrelease =~ /ou=/) {
2317                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2318                                if($subsubrelease =~ /ou=/) {
2319                                        apply_fai_inheritance($fai_classes->{$subrelease});
2320                                }
2321                        }
2322                }
2323        }
2325        return $fai_classes;
2328 sub get_fai_release_entries {
2329         my $tmp_classes = shift || return;
2330         my $parent = shift || "";
2331         my @result = shift || ();
2333         foreach my $entry (keys %{$tmp_classes}) {
2334                 if(defined($entry)) {
2335                         if($entry =~ /^ou=.*$/) {
2336                                 my $release_name = $entry;
2337                                 $release_name =~ s/ou=//g;
2338                                 if(length($parent)>0) {
2339                                         $release_name = $parent."/".$release_name;
2340                                 }
2341                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2342                                 foreach my $bufentry(@bufentries) {
2343                                         push @result, $bufentry;
2344                                 }
2345                         } else {
2346                                 my @types = get_fai_types($tmp_classes->{$entry});
2347                                 foreach my $type (@types) {
2348                                         push @result, 
2349                                         {
2350                                                 'class' => $entry,
2351                                                 'type' => $type->{'type'},
2352                                                 'release' => $parent,
2353                                                 'state' => $type->{'state'},
2354                                         };
2355                                 }
2356                         }
2357                 }
2358         }
2360         return @result;
2363 sub deep_copy {
2364         my $this = shift;
2365         if (not ref $this) {
2366                 $this;
2367         } elsif (ref $this eq "ARRAY") {
2368                 [map deep_copy($_), @$this];
2369         } elsif (ref $this eq "HASH") {
2370                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2371         } else { die "what type is $_?" }
2375 sub session_run_result {
2376     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2377     $kernel->sig(CHLD => "child_reap");
2380 sub session_run_debug {
2381     my $result = $_[ARG0];
2382     print STDERR "$result\n";
2385 sub session_run_done {
2386     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2387     delete $heap->{task}->{$task_id};
2391 sub create_sources_list {
2392         my $session_id = shift;
2393         my $ldap_handle = &main::get_ldap_handle;
2394         my $result="/tmp/gosa_si_tmp_sources_list";
2396         # Remove old file
2397         if(stat($result)) {
2398                 unlink($result);
2399                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2400         }
2402         my $fh;
2403         open($fh, ">$result");
2404         if (not defined $fh) {
2405                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2406                 return undef;
2407         }
2408         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2409                 my $mesg=$ldap_handle->search(
2410                         base    => $main::ldap_server_dn,
2411                         scope   => 'base',
2412                         attrs   => 'FAIrepository',
2413                         filter  => 'objectClass=FAIrepositoryServer'
2414                 );
2415                 if($mesg->count) {
2416                         foreach my $entry(@{$mesg->{'entries'}}) {
2417                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2418                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2419                                         my $line = "deb $server $release";
2420                                         $sections =~ s/,/ /g;
2421                                         $line.= " $sections";
2422                                         print $fh $line."\n";
2423                                 }
2424                         }
2425                 }
2426         } else {
2427                 if (defined $main::ldap_server_dn){
2428                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2429                 } else {
2430                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2431                 }
2432         }
2433         close($fh);
2435         return $result;
2439 sub run_create_packages_list_db {
2440     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2441         my $session_id = $session->ID;
2443         my $task = POE::Wheel::Run->new(
2444                                         Priority => +20,
2445                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2446                                         StdoutEvent  => "session_run_result",
2447                                         StderrEvent  => "session_run_debug",
2448                                         CloseEvent   => "session_run_done",
2449                                         );
2450         $heap->{task}->{ $task->ID } = $task;
2454 sub create_packages_list_db {
2455         my ($ldap_handle, $sources_file, $session_id) = @_;
2456         
2457         # it should not be possible to trigger a recreation of packages_list_db
2458         # while packages_list_db is under construction, so set flag packages_list_under_construction
2459         # which is tested befor recreation can be started
2460         if (-r $packages_list_under_construction) {
2461                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2462                 return;
2463         } else {
2464                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2465                 # set packages_list_under_construction to true
2466                 system("touch $packages_list_under_construction");
2467                 @packages_list_statements=();
2468         }
2470         if (not defined $session_id) { $session_id = 0; }
2471         if (not defined $ldap_handle) { 
2472                 $ldap_handle= &get_ldap_handle();
2474                 if (not defined $ldap_handle) {
2475                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2476                         unlink($packages_list_under_construction);
2477                         return;
2478                 }
2479         }
2480         if (not defined $sources_file) { 
2481                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2482                 $sources_file = &create_sources_list($session_id);
2483         }
2485         if (not defined $sources_file) {
2486                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2487                 unlink($packages_list_under_construction);
2488                 return;
2489         }
2491         my $line;
2493         open(CONFIG, "<$sources_file") or do {
2494                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2495                 unlink($packages_list_under_construction);
2496                 return;
2497         };
2499         # Read lines
2500         while ($line = <CONFIG>){
2501                 # Unify
2502                 chop($line);
2503                 $line =~ s/^\s+//;
2504                 $line =~ s/^\s+/ /;
2506                 # Strip comments
2507                 $line =~ s/#.*$//g;
2509                 # Skip empty lines
2510                 if ($line =~ /^\s*$/){
2511                         next;
2512                 }
2514                 # Interpret deb line
2515                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2516                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2517                         my $section;
2518                         foreach $section (split(' ', $sections)){
2519                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2520                         }
2521                 }
2522         }
2524         close (CONFIG);
2526         find(\&cleanup_and_extract, keys( %repo_dirs ));
2527         &main::strip_packages_list_statements();
2528         unshift @packages_list_statements, "VACUUM";
2529         $packages_list_db->exec_statementlist(\@packages_list_statements);
2530         unlink($packages_list_under_construction);
2531         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2532         return;
2535 # This function should do some intensive task to minimize the db-traffic
2536 sub strip_packages_list_statements {
2537     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2538         my @new_statement_list=();
2539         my $hash;
2540         my $insert_hash;
2541         my $update_hash;
2542         my $delete_hash;
2543         my $local_timestamp=get_time();
2545         foreach my $existing_entry (@existing_entries) {
2546                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2547         }
2549         foreach my $statement (@packages_list_statements) {
2550                 if($statement =~ /^INSERT/i) {
2551                         # Assign the values from the insert statement
2552                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2553                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2554                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2555                                 # If section or description has changed, update the DB
2556                                 if( 
2557                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2558                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2559                                 ) {
2560                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2561                                 }
2562                         } else {
2563                                 # Insert a non-existing entry to db
2564                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2565                         }
2566                 } elsif ($statement =~ /^UPDATE/i) {
2567                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2568                         /^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;
2569                         foreach my $distribution (keys %{$hash}) {
2570                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2571                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2572                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2573                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2574                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2575                                                 my $section;
2576                                                 my $description;
2577                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2578                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2579                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2580                                                 }
2581                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2582                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2583                                                 }
2584                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2585                                         }
2586                                 }
2587                         }
2588                 }
2589         }
2591         # TODO: Check for orphaned entries
2593         # unroll the insert_hash
2594         foreach my $distribution (keys %{$insert_hash}) {
2595                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2596                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2597                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2598                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2599                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2600                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2601                                 ."'$local_timestamp')";
2602                         }
2603                 }
2604         }
2606         # unroll the update hash
2607         foreach my $distribution (keys %{$update_hash}) {
2608                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2609                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2610                                 my $set = "";
2611                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2612                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2613                                 }
2614                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2615                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2616                                 }
2617                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2618                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2619                                 }
2620                                 if(defined($set) and length($set) > 0) {
2621                                         $set .= "timestamp = '$local_timestamp'";
2622                                 } else {
2623                                         next;
2624                                 }
2625                                 push @new_statement_list, 
2626                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2627                                         ." distribution = '$distribution'"
2628                                         ." AND package = '$package'"
2629                                         ." AND version = '$version'";
2630                         }
2631                 }
2632         }
2634         @packages_list_statements = @new_statement_list;
2638 sub parse_package_info {
2639     my ($baseurl, $dist, $section, $session_id)= @_;
2640     my ($package);
2641     if (not defined $session_id) { $session_id = 0; }
2642     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2643     $repo_dirs{ "${repo_path}/pool" } = 1;
2645     foreach $package ("Packages.gz"){
2646         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2647         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2648         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2649     }
2650     
2654 sub get_package {
2655     my ($url, $dest, $session_id)= @_;
2656     if (not defined $session_id) { $session_id = 0; }
2658     my $tpath = dirname($dest);
2659     -d "$tpath" || mkpath "$tpath";
2661     # This is ugly, but I've no time to take a look at "how it works in perl"
2662     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2663         system("gunzip -cd '$dest' > '$dest.in'");
2664         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2665         unlink($dest);
2666         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2667     } else {
2668         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2669     }
2670     return 0;
2674 sub parse_package {
2675     my ($path, $dist, $srv_path, $session_id)= @_;
2676     if (not defined $session_id) { $session_id = 0;}
2677     my ($package, $version, $section, $description);
2678     my $PACKAGES;
2679     my $timestamp = &get_time();
2681     if(not stat("$path.in")) {
2682         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2683         return;
2684     }
2686     open($PACKAGES, "<$path.in");
2687     if(not defined($PACKAGES)) {
2688         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2689         return;
2690     }
2692     # Read lines
2693     while (<$PACKAGES>){
2694         my $line = $_;
2695         # Unify
2696         chop($line);
2698         # Use empty lines as a trigger
2699         if ($line =~ /^\s*$/){
2700             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2701             push(@packages_list_statements, $sql);
2702             $package = "none";
2703             $version = "none";
2704             $section = "none";
2705             $description = "none"; 
2706             next;
2707         }
2709         # Trigger for package name
2710         if ($line =~ /^Package:\s/){
2711             ($package)= ($line =~ /^Package: (.*)$/);
2712             next;
2713         }
2715         # Trigger for version
2716         if ($line =~ /^Version:\s/){
2717             ($version)= ($line =~ /^Version: (.*)$/);
2718             next;
2719         }
2721         # Trigger for description
2722         if ($line =~ /^Description:\s/){
2723             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2724             next;
2725         }
2727         # Trigger for section
2728         if ($line =~ /^Section:\s/){
2729             ($section)= ($line =~ /^Section: (.*)$/);
2730             next;
2731         }
2733         # Trigger for filename
2734         if ($line =~ /^Filename:\s/){
2735             my ($filename) = ($line =~ /^Filename: (.*)$/);
2736             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2737             next;
2738         }
2739     }
2741     close( $PACKAGES );
2742     unlink( "$path.in" );
2746 sub store_fileinfo {
2747     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2749     my %fileinfo = (
2750         'package' => $package,
2751         'dist' => $dist,
2752         'version' => $vers,
2753     );
2755     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2759 sub cleanup_and_extract {
2760     my $fileinfo = $repo_files{ $File::Find::name };
2762     if( defined $fileinfo ) {
2764         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2765         my $sql;
2766         my $package = $fileinfo->{ 'package' };
2767         my $newver = $fileinfo->{ 'version' };
2769         mkpath($dir);
2770         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2772                 if( -f "$dir/DEBIAN/templates" ) {
2774                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2776                         my $tmpl= "";
2777                         {
2778                                 local $/=undef;
2779                                 open FILE, "$dir/DEBIAN/templates";
2780                                 $tmpl = &encode_base64(<FILE>);
2781                                 close FILE;
2782                         }
2783                         rmtree("$dir/DEBIAN/templates");
2785                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2786                 push @packages_list_statements, $sql;
2787                 }
2788     }
2790     return;
2794 sub register_at_foreign_servers {   
2795     my ($kernel) = $_[KERNEL];
2797     # hole alle bekannten server aus known_server_db
2798     my $server_sql = "SELECT * FROM $known_server_tn";
2799     my $server_res = $known_server_db->exec_statement($server_sql);
2801     # no entries in known_server_db
2802     if (not ref(@$server_res[0]) eq "ARRAY") { 
2803         # TODO
2804     }
2806     # detect already connected clients
2807     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2808     my $client_res = $known_clients_db->exec_statement($client_sql);
2810     # send my server details to all other gosa-si-server within the network
2811     foreach my $hit (@$server_res) {
2812         my $hostname = @$hit[0];
2813         my $hostkey = &create_passwd;
2815         # add already connected clients to registration message 
2816         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2817         &add_content2xml_hash($myhash, 'key', $hostkey);
2818         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2819         
2820         # build registration message and send it
2821         my $foreign_server_msg = &create_xml_string($myhash);
2822         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2823     }
2824     
2825     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2826     return;
2830 #==== MAIN = main ==============================================================
2831 #  parse commandline options
2832 Getopt::Long::Configure( "bundling" );
2833 GetOptions("h|help" => \&usage,
2834         "c|config=s" => \$cfg_file,
2835         "f|foreground" => \$foreground,
2836         "v|verbose+" => \$verbose,
2837         "no-arp+" => \$no_arp,
2838            );
2840 #  read and set config parameters
2841 &check_cmdline_param ;
2842 &read_configfile;
2843 &check_pid;
2845 $SIG{CHLD} = 'IGNORE';
2847 # forward error messages to logfile
2848 if( ! $foreground ) {
2849   open( STDIN,  '+>/dev/null' );
2850   open( STDOUT, '+>&STDIN'    );
2851   open( STDERR, '+>&STDIN'    );
2854 # Just fork, if we are not in foreground mode
2855 if( ! $foreground ) { 
2856     chdir '/'                 or die "Can't chdir to /: $!";
2857     $pid = fork;
2858     setsid                    or die "Can't start a new session: $!";
2859     umask 0;
2860 } else { 
2861     $pid = $$; 
2864 # Do something useful - put our PID into the pid_file
2865 if( 0 != $pid ) {
2866     open( LOCK_FILE, ">$pid_file" );
2867     print LOCK_FILE "$pid\n";
2868     close( LOCK_FILE );
2869     if( !$foreground ) { 
2870         exit( 0 ) 
2871     };
2874 # parse head url and revision from svn
2875 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2876 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2877 $server_headURL = defined $1 ? $1 : 'unknown' ;
2878 $server_revision = defined $2 ? $2 : 'unknown' ;
2879 if ($server_headURL =~ /\/tag\// || 
2880         $server_headURL =~ /\/branches\// ) {
2881     $server_status = "stable"; 
2882 } else {
2883     $server_status = "developmental" ;
2887 daemon_log(" ", 1);
2888 daemon_log("$0 started!", 1);
2889 daemon_log("status: $server_status", 1);
2890 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2892 # connect to incoming_db
2893 unlink($incoming_file_name);
2894 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2895 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2897 # connect to gosa-si job queue
2898 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2899 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2901 # connect to known_clients_db
2902 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2903 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2905 # connect to foreign_clients_db
2906 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2907 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2909 # connect to known_server_db
2910 unlink($known_server_file_name);
2911 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2912 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2914 # connect to login_usr_db
2915 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2916 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2918 # connect to fai_server_db and fai_release_db
2919 unlink($fai_server_file_name);
2920 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2921 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2923 unlink($fai_release_file_name);
2924 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2925 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2927 # connect to packages_list_db
2928 #unlink($packages_list_file_name);
2929 unlink($packages_list_under_construction);
2930 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2931 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2933 # connect to messaging_db
2934 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2935 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2938 # create xml object used for en/decrypting
2939 $xml = new XML::Simple();
2942 # foreign servers 
2943 my @foreign_server_list;
2945 # add foreign server from cfg file
2946 if ($foreign_server_string ne "") {
2947     my @cfg_foreign_server_list = split(",", $foreign_server_string);
2948     foreach my $foreign_server (@cfg_foreign_server_list) {
2949         push(@foreign_server_list, $foreign_server);
2950     }
2953 # add foreign server from dns
2954 my @tmp_servers;
2955 if ( !$server_domain) {
2956     # Try our DNS Searchlist
2957     for my $domain(get_dns_domains()) {
2958         chomp($domain);
2959         my @tmp_domains= &get_server_addresses($domain);
2960         if(@tmp_domains) {
2961             for my $tmp_server(@tmp_domains) {
2962                 push @tmp_servers, $tmp_server;
2963             }
2964         }
2965     }
2966     if(@tmp_servers && length(@tmp_servers)==0) {
2967         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2968     }
2969 } else {
2970     @tmp_servers = &get_server_addresses($server_domain);
2971     if( 0 == @tmp_servers ) {
2972         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2973     }
2975 foreach my $server (@tmp_servers) { 
2976     unshift(@foreign_server_list, $server); 
2978 # eliminate duplicate entries
2979 @foreign_server_list = &del_doubles(@foreign_server_list);
2980 my $all_foreign_server = join(", ", @foreign_server_list);
2981 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
2983 # add all found foreign servers to known_server
2984 my $act_timestamp = &get_time();
2985 foreach my $foreign_server (@foreign_server_list) {
2987         # do not add myself to known_server_db
2988         if (&is_local($foreign_server)) { next; }
2989         ######################################
2991     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
2992             primkey=>['hostname'],
2993             hostname=>$foreign_server,
2994             status=>'not_jet_registered',
2995             hostkey=>"none",
2996             timestamp=>$act_timestamp,
2997             } );
3001 POE::Component::Server::TCP->new(
3002     Alias => "TCP_SERVER",
3003         Port => $server_port,
3004         ClientInput => sub {
3005         my ($kernel, $input) = @_[KERNEL, ARG0];
3006         push(@tasks, $input);
3007         push(@msgs_to_decrypt, $input);
3008         $kernel->yield("msg_to_decrypt");
3009         },
3010     InlineStates => {
3011         msg_to_decrypt => \&msg_to_decrypt,
3012         next_task => \&next_task,
3013         task_result => \&handle_task_result,
3014         task_done   => \&handle_task_done,
3015         task_debug  => \&handle_task_debug,
3016         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3017     }
3018 );
3020 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3022 # create session for repeatedly checking the job queue for jobs
3023 POE::Session->create(
3024         inline_states => {
3025                 _start => \&session_start,
3026         register_at_foreign_servers => \&register_at_foreign_servers,
3027         sig_handler => \&sig_handler,
3028         next_task => \&next_task,
3029         task_result => \&handle_task_result,
3030         task_done   => \&handle_task_done,
3031         task_debug  => \&handle_task_debug,
3032         watch_for_next_tasks => \&watch_for_next_tasks,
3033         watch_for_new_messages => \&watch_for_new_messages,
3034         watch_for_delivery_messages => \&watch_for_delivery_messages,
3035         watch_for_done_messages => \&watch_for_done_messages,
3036                 watch_for_new_jobs => \&watch_for_new_jobs,
3037         watch_for_done_jobs => \&watch_for_done_jobs,
3038         watch_for_old_known_clients => \&watch_for_old_known_clients,
3039         create_packages_list_db => \&run_create_packages_list_db,
3040         create_fai_server_db => \&run_create_fai_server_db,
3041         create_fai_release_db => \&run_create_fai_release_db,
3042                 recreate_packages_db => \&run_recreate_packages_db,
3043         session_run_result => \&session_run_result,
3044         session_run_debug => \&session_run_debug,
3045         session_run_done => \&session_run_done,
3046         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3047         }
3048 );
3051 # import all modules
3052 &import_modules;
3054 # TODO
3055 # check wether all modules are gosa-si valid passwd check
3059 POE::Kernel->run();
3060 exit;