Code

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