Code

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