Code

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