Code

3864ad237190ee8cdae34703979ba68da9d3d4fc
[gosa.git] / trunk / 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 #===============================================================================
23 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
25 use strict;
26 use warnings;
27 use Getopt::Long;
28 use Config::IniFiles;
29 use POSIX;
31 use Fcntl;
32 use IO::Socket::INET;
33 use IO::Handle;
34 use IO::Select;
35 use Symbol qw(qualify_to_ref);
36 use Crypt::Rijndael;
37 use MIME::Base64;
38 use Digest::MD5  qw(md5 md5_hex md5_base64);
39 use XML::Simple;
40 use Data::Dumper;
41 use Sys::Syslog qw( :DEFAULT setlogsock);
42 use Cwd;
43 use File::Spec;
44 use File::Basename;
45 use File::Find;
46 use File::Copy;
47 use File::Path;
48 use GOSA::GosaSupportDaemon;
49 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
50 use Net::LDAP;
51 use Net::LDAP::Util qw(:escape);
52 use Time::HiRes qw( usleep);
54 # revision number of server and program name
55 my $server_headURL;
56 my $server_revision;
57 my $server_status;
58 our $prg= basename($0);
60 my $db_module = "DBsqlite";
61 {
62 no strict "refs";
63 require ("GOSA/".$db_module.".pm");
64 ("GOSA/".$db_module)->import;
65 daemon_log("0 INFO: importing database module '$db_module'", 1);
66 }
68 my $modules_path = "/usr/lib/gosa-si/modules";
69 use lib "/usr/lib/gosa-si/modules";
71 our $global_kernel;
72 my ($foreground, $ping_timeout);
73 my ($server);
74 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
75 my ($messaging_db_loop_delay);
76 my ($procid, $pid);
77 my ($arp_fifo);
78 my ($xml);
79 my $sources_list;
80 my $max_clients;
81 my %repo_files=();
82 my $repo_path;
83 my %repo_dirs=();
85 # Variables declared in config file are always set to 'our'
86 our (%cfg_defaults, $log_file, $pid_file, 
87     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
88     $arp_activ, $gosa_unit_tag,
89     $GosaPackages_key, $gosa_timeout,
90     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
91     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
92     $arp_enabled, $arp_interface,
93     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
94                 $new_systems_ou,
95 );
97 # additional variable which should be globaly accessable
98 our $server_address;
99 our $server_mac_address;
100 our $gosa_address;
101 our $no_arp;
102 our $verbose;
103 our $forground;
104 our $cfg_file;
105 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
106 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
107 our $known_modules;
108 our $root_uid;
109 our $adm_gid;
112 # specifies the verbosity of the daemon_log
113 $verbose = 0 ;
115 # if foreground is not null, script will be not forked to background
116 $foreground = 0 ;
118 # specifies the timeout seconds while checking the online status of a registrating client
119 $ping_timeout = 5;
121 $no_arp = 0;
122 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
123 my @packages_list_statements;
124 my $watch_for_new_jobs_in_progress = 0;
126 # holds all incoming decrypted messages
127 our $incoming_db;
128 our $incoming_tn = 'incoming';
129 my $incoming_file_name;
130 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
131         "timestamp VARCHAR(14) DEFAULT 'none'", 
132         "headertag VARCHAR(255) DEFAULT 'none'",
133         "targettag VARCHAR(255) DEFAULT 'none'",
134         "xmlmessage TEXT",
135         "module VARCHAR(255) DEFAULT 'none'",
136         "sessionid VARCHAR(255) DEFAULT '0'",
137 );
139 # holds all gosa jobs
140 our $job_db;
141 our $job_queue_tn = 'jobs';
142 my $job_queue_file_name;
143 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
144         "timestamp VARCHAR(14) DEFAULT 'none'", 
145         "status VARCHAR(255) DEFAULT 'none'", 
146         "result TEXT",
147         "progress VARCHAR(255) DEFAULT 'none'",
148         "headertag VARCHAR(255) DEFAULT 'none'",
149         "targettag VARCHAR(255) DEFAULT 'none'", 
150         "xmlmessage TEXT", 
151         "macaddress VARCHAR(17) DEFAULT 'none'",
152         "plainname VARCHAR(255) DEFAULT 'none'",
153         "siserver VARCHAR(255) DEFAULT 'none'",
154         "modified INTEGER DEFAULT '0'",
155 );
157 # holds all other gosa-si-server
158 our $known_server_db;
159 our $known_server_tn = "known_server";
160 my $known_server_file_name;
161 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
163 # holds all registrated clients
164 our $known_clients_db;
165 our $known_clients_tn = "known_clients";
166 my $known_clients_file_name;
167 my @known_clients_col_names = ("hostname VARCHAR(255)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "timestamp VARCHAR(14)", "macaddress VARCHAR(17)", "events TEXT", "keylifetime VARCHAR(255)");
169 # holds all registered clients at a foreign server
170 our $foreign_clients_db;
171 our $foreign_clients_tn = "foreign_clients"; 
172 my $foreign_clients_file_name;
173 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
175 # holds all logged in user at each client 
176 our $login_users_db;
177 our $login_users_tn = "login_users";
178 my $login_users_file_name;
179 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
181 # holds all fai server, the debian release and tag
182 our $fai_server_db;
183 our $fai_server_tn = "fai_server"; 
184 my $fai_server_file_name;
185 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
187 our $fai_release_db;
188 our $fai_release_tn = "fai_release"; 
189 my $fai_release_file_name;
190 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
192 # holds all packages available from different repositories
193 our $packages_list_db;
194 our $packages_list_tn = "packages_list";
195 my $packages_list_file_name;
196 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
197 my $outdir = "/tmp/packages_list_db";
198 my $arch = "i386"; 
200 # holds all messages which should be delivered to a user
201 our $messaging_db;
202 our $messaging_tn = "messaging"; 
203 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
204         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
205 my $messaging_file_name;
207 # path to directory to store client install log files
208 our $client_fai_log_dir = "/var/log/fai"; 
210 # queue which stores taskes until one of the $max_children children are ready to process the task
211 #my @tasks = qw();
212 my @msgs_to_decrypt = qw();
213 my $max_children = 2;
216 # loop delay for job queue to look for opsi jobs
217 my $job_queue_opsi_delay = 10;
218 our $opsi_client;
219 our $opsi_url;
220  
221 # Lifetime of logged in user information. If no update information comes after n seconds, 
222 # the user is expeceted to be no longer logged in or the host is no longer running. Because
223 # of this, the user is deleted from login_users_db
224 our $logged_in_user_date_of_expiry = 600;
227 %cfg_defaults = (
228 "general" => {
229     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
230     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
231     },
232 "server" => {
233     "ip"                    => [\$server_ip, "0.0.0.0"],
234     "port"                  => [\$server_port, "20081"],
235     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
236     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
237     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
238     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
239     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
240     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
241     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
242     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
243     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
244     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
245     "repo-path"             => [\$repo_path, '/srv/www/repository'],
246     "ldap-uri"              => [\$ldap_uri, ""],
247     "ldap-base"             => [\$ldap_base, ""],
248     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
249     "ldap-admin-password"   => [\$ldap_admin_password, ""],
250     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
251     "max-clients"           => [\$max_clients, 10],
252     "wol-password"          => [\$wake_on_lan_passwd, ""],
253                 "mysql-username"        => [\$mysql_username, "gosa_si"],
254                 "mysql-password"        => [\$mysql_password, ""],
255                 "mysql-database"        => [\$mysql_database, "gosa_si"],
256                 "mysql-host"            => [\$mysql_host, "127.0.0.1"],
257     },
258 "GOsaPackages" => {
259     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
260     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
261     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
262     "key" => [\$GosaPackages_key, "none"],
263                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
264     },
265 "ClientPackages" => {
266     "key" => [\$ClientPackages_key, "none"],
267     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
268     },
269 "ServerPackages"=> {
270     "address"      => [\$foreign_server_string, ""],
271     "dns-lookup"            => [\$dns_lookup, "true"],
272     "domain"  => [\$server_domain, ""],
273     "key"     => [\$ServerPackages_key, "none"],
274     "key-lifetime" => [\$foreign_servers_register_delay, 120],
275     "job-synchronization-enabled" => [\$job_synchronization, "true"],
276     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
277     },
278 "ArpHandler" => {
279     "enabled"   => [\$arp_enabled, "true"],
280     "interface" => [\$arp_interface, "all"],
281         },
282 "Opsi" => {
283     "enabled"  => [\$opsi_enabled, "false"], 
284     "server"   => [\$opsi_server, "localhost"],
285     "admin"    => [\$opsi_admin, "opsi-admin"],
286     "password" => [\$opsi_password, "secret"],
287    },
289 );
292 #===  FUNCTION  ================================================================
293 #         NAME:  usage
294 #   PARAMETERS:  nothing
295 #      RETURNS:  nothing
296 #  DESCRIPTION:  print out usage text to STDERR
297 #===============================================================================
298 sub usage {
299     print STDERR << "EOF" ;
300 usage: $prg [-hvf] [-c config]
302            -h        : this (help) message
303            -c <file> : config file
304            -f        : foreground, process will not be forked to background
305            -v        : be verbose (multiple to increase verbosity)
306            -no-arp   : starts $prg without connection to arp module
307  
308 EOF
309     print "\n" ;
313 #===  FUNCTION  ================================================================
314 #         NAME:  logging
315 #   PARAMETERS:  level - string - default 'info'
316 #                msg - string -
317 #                facility - string - default 'LOG_DAEMON'
318 #      RETURNS:  nothing
319 #  DESCRIPTION:  function for logging
320 #===============================================================================
321 sub daemon_log {
322     # log into log_file
323     my( $msg, $level ) = @_;
324     if(not defined $msg) { return }
325     if(not defined $level) { $level = 1 }
326     if(defined $log_file){
327         open(LOG_HANDLE, ">>$log_file");
328         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
329             print STDERR "cannot open $log_file: $!";
330             return 
331         }
332         chomp($msg);
333         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
334         if($level <= $verbose){
335             my ($seconds, $minutes, $hours, $monthday, $month,
336                     $year, $weekday, $yearday, $sommertime) = localtime(time);
337             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
338             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
339             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
340             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
341             $month = $monthnames[$month];
342             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
343             $year+=1900;
344             my $name = $prg;
346             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
347             print LOG_HANDLE $log_msg;
348             if( $foreground ) { 
349                 print STDERR $log_msg;
350             }
351         }
352         close( LOG_HANDLE );
353     }
357 #===  FUNCTION  ================================================================
358 #         NAME:  check_cmdline_param
359 #   PARAMETERS:  nothing
360 #      RETURNS:  nothing
361 #  DESCRIPTION:  validates commandline parameter
362 #===============================================================================
363 sub check_cmdline_param () {
364     my $err_config;
365     my $err_counter = 0;
366         if(not defined($cfg_file)) {
367                 $cfg_file = "/etc/gosa-si/server.conf";
368                 if(! -r $cfg_file) {
369                         $err_config = "please specify a config file";
370                         $err_counter += 1;
371                 }
372     }
373     if( $err_counter > 0 ) {
374         &usage( "", 1 );
375         if( defined( $err_config)) { print STDERR "$err_config\n"}
376         print STDERR "\n";
377         exit( -1 );
378     }
382 #===  FUNCTION  ================================================================
383 #         NAME:  check_pid
384 #   PARAMETERS:  nothing
385 #      RETURNS:  nothing
386 #  DESCRIPTION:  handels pid processing
387 #===============================================================================
388 sub check_pid {
389     $pid = -1;
390     # Check, if we are already running
391     if( open(LOCK_FILE, "<$pid_file") ) {
392         $pid = <LOCK_FILE>;
393         if( defined $pid ) {
394             chomp( $pid );
395             if( -f "/proc/$pid/stat" ) {
396                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
397                 if( $stat ) {
398                                         daemon_log("ERROR: Already running",1);
399                     close( LOCK_FILE );
400                     exit -1;
401                 }
402             }
403         }
404         close( LOCK_FILE );
405         unlink( $pid_file );
406     }
408     # create a syslog msg if it is not to possible to open PID file
409     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
410         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
411         if (open(LOCK_FILE, '<', $pid_file)
412                 && ($pid = <LOCK_FILE>))
413         {
414             chomp($pid);
415             $msg .= "(PID $pid)\n";
416         } else {
417             $msg .= "(unable to read PID)\n";
418         }
419         if( ! ($foreground) ) {
420             openlog( $0, "cons,pid", "daemon" );
421             syslog( "warning", $msg );
422             closelog();
423         }
424         else {
425             print( STDERR " $msg " );
426         }
427         exit( -1 );
428     }
431 #===  FUNCTION  ================================================================
432 #         NAME:  import_modules
433 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
434 #                are stored
435 #      RETURNS:  nothing
436 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
437 #                state is on is imported by "require 'file';"
438 #===============================================================================
439 sub import_modules {
440     daemon_log(" ", 1);
442     if (not -e $modules_path) {
443         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
444     }
446     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
447     while (defined (my $file = readdir (DIR))) {
448         if (not $file =~ /(\S*?).pm$/) {
449             next;
450         }
451                 my $mod_name = $1;
453         # ArpHandler switch
454         if( $file =~ /ArpHandler.pm/ ) {
455             if( $arp_enabled eq "false" ) { next; }
456         }
457         
458         eval { require $file; };
459         if ($@) {
460             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
461             daemon_log("$@", 1);
462             exit;
463                 } else {
464                         my $info = eval($mod_name.'::get_module_info()');
465                         # Only load module if get_module_info() returns a non-null object
466                         if( $info ) {
467                                 my ($input_address, $input_key, $event_hash) = @{$info};
468                                 $known_modules->{$mod_name} = $info;
469                                 daemon_log("0 INFO: module $mod_name loaded", 5);
470                         }
471                 }
472     }   
474     close (DIR);
477 #===  FUNCTION  ================================================================
478 #         NAME:  password_check
479 #   PARAMETERS:  nothing
480 #      RETURNS:  nothing
481 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
482 #                the same password
483 #===============================================================================
484 sub password_check {
485     my $passwd_hash = {};
486     while (my ($mod_name, $mod_info) = each %$known_modules) {
487         my $mod_passwd = @$mod_info[1];
488         if (not defined $mod_passwd) { next; }
489         if (not exists $passwd_hash->{$mod_passwd}) {
490             $passwd_hash->{$mod_passwd} = $mod_name;
492         # escalates critical error
493         } else {
494             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
495             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
496             exit( -1 );
497         }
498     }
503 #===  FUNCTION  ================================================================
504 #         NAME:  sig_int_handler
505 #   PARAMETERS:  signal - string - signal arose from system
506 #      RETURNS:  nothing
507 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
508 #===============================================================================
509 sub sig_int_handler {
510     my ($signal) = @_;
512 #       if (defined($ldap_handle)) {
513 #               $ldap_handle->disconnect;
514 #       }
515     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
516     
518     daemon_log("shutting down gosa-si-server", 1);
519     system("kill `ps -C gosa-si-server -o pid=`");
521 $SIG{INT} = \&sig_int_handler;
524 sub check_key_and_xml_validity {
525     my ($crypted_msg, $module_key, $session_id) = @_;
526     my $msg;
527     my $msg_hash;
528     my $error_string;
529     eval{
530         $msg = &decrypt_msg($crypted_msg, $module_key);
532         if ($msg =~ /<xml>/i){
533             $msg =~ s/\s+/ /g;  # just for better daemon_log
534             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
535             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
537             ##############
538             # check header
539             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
540             my $header_l = $msg_hash->{'header'};
541             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
542             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
543             my $header = @{$header_l}[0];
544             if( 0 == length $header) { die 'empty string in header tag'; }
546             ##############
547             # check source
548             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
549             my $source_l = $msg_hash->{'source'};
550             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
551             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
552             my $source = @{$source_l}[0];
553             if( 0 == length $source) { die 'source error'; }
555             ##############
556             # check target
557             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
558             my $target_l = $msg_hash->{'target'};
559             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
560         }
561     };
562     if($@) {
563         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
564         $msg = undef;
565         $msg_hash = undef;
566     }
568     return ($msg, $msg_hash);
572 sub check_outgoing_xml_validity {
573     my ($msg, $session_id) = @_;
575     my $msg_hash;
576     eval{
577         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
579         ##############
580         # check header
581         my $header_l = $msg_hash->{'header'};
582         if( 1 != @{$header_l} ) {
583             die 'no or more than one headers specified';
584         }
585         my $header = @{$header_l}[0];
586         if( 0 == length $header) {
587             die 'header has length 0';
588         }
590         ##############
591         # check source
592         my $source_l = $msg_hash->{'source'};
593         if( 1 != @{$source_l} ) {
594             die 'no or more than 1 sources specified';
595         }
596         my $source = @{$source_l}[0];
597         if( 0 == length $source) {
598             die 'source has length 0';
599         }
601                                 # Check if source contains hostname instead of ip address
602                                 if(not $source =~ /^[a-z0-9\.]+:\d+$/i) {
603                                                 my ($hostname,$port) = split(/:/, $source);
604                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
605                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
606                                                         # Write ip address to $source variable
607                                                         $source = "$ip_address:$port";
608                                                 }
609                                 }
610         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
611                 $source =~ /^GOSA$/i) {
612             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
613         }
614         
615         ##############
616         # check target  
617         my $target_l = $msg_hash->{'target'};
618         if( 0 == @{$target_l} ) {
619             die "no targets specified";
620         }
621         foreach my $target (@$target_l) {
622             if( 0 == length $target) {
623                 die "target has length 0";
624             }
625             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
626                     $target =~ /^GOSA$/i ||
627                     $target =~ /^\*$/ ||
628                     $target =~ /KNOWN_SERVER/i ||
629                     $target =~ /JOBDB/i ||
630                     $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 ){
631                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
632             }
633         }
634     };
635     if($@) {
636         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
637         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
638         $msg_hash = undef;
639     }
641     return ($msg_hash);
645 sub input_from_known_server {
646     my ($input, $remote_ip, $session_id) = @_ ;  
647     my ($msg, $msg_hash, $module);
649     my $sql_statement= "SELECT * FROM known_server";
650     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
652     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
653         my $host_name = $hit->{hostname};
654         if( not $host_name =~ "^$remote_ip") {
655             next;
656         }
657         my $host_key = $hit->{hostkey};
658         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
659         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
661         # check if module can open msg envelope with module key
662         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
663         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
664             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
665             daemon_log("$@", 8);
666             next;
667         }
668         else {
669             $msg = $tmp_msg;
670             $msg_hash = $tmp_msg_hash;
671             $module = "ServerPackages";
672             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
673             last;
674         }
675     }
677     if( (!$msg) || (!$msg_hash) || (!$module) ) {
678         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
679     }
680   
681     return ($msg, $msg_hash, $module);
685 sub input_from_known_client {
686     my ($input, $remote_ip, $session_id) = @_ ;  
687     my ($msg, $msg_hash, $module);
689     my $sql_statement= "SELECT * FROM known_clients";
690     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
691     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
692         my $host_name = $hit->{hostname};
693         if( not $host_name =~ /^$remote_ip:\d*$/) {
694                 next;
695                 }
696         my $host_key = $hit->{hostkey};
697         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
698         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
700         # check if module can open msg envelope with module key
701         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
703         if( (!$msg) || (!$msg_hash) ) {
704             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
705             &daemon_log("$@", 8);
706             next;
707         }
708         else {
709             $module = "ClientPackages";
710             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
711             last;
712         }
713     }
715     if( (!$msg) || (!$msg_hash) || (!$module) ) {
716         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
717     }
719     return ($msg, $msg_hash, $module);
723 sub input_from_unknown_host {
724         no strict "refs";
725         my ($input, $session_id) = @_ ;
726         my ($msg, $msg_hash, $module);
727         my $error_string;
729         my %act_modules = %$known_modules;
731         while( my ($mod, $info) = each(%act_modules)) {
733                 # check a key exists for this module
734                 my $module_key = ${$mod."_key"};
735                 if( not defined $module_key ) {
736                         if( $mod eq 'ArpHandler' ) {
737                                 next;
738                         }
739                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
740                         next;
741                 }
742                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
744                 # check if module can open msg envelope with module key
745                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
746                 if( (not defined $msg) || (not defined $msg_hash) ) {
747                         next;
748                 } else {
749                         $module = $mod;
750             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
751                         last;
752                 }
753         }
755         if( (!$msg) || (!$msg_hash) || (!$module)) {
756                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
757         }
759         return ($msg, $msg_hash, $module);
763 sub create_ciphering {
764     my ($passwd) = @_;
765         if((!defined($passwd)) || length($passwd)==0) {
766                 $passwd = "";
767         }
768     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
769     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
770     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
771     $my_cipher->set_iv($iv);
772     return $my_cipher;
776 sub encrypt_msg {
777     my ($msg, $key) = @_;
778     my $my_cipher = &create_ciphering($key);
779     my $len;
780     {
781             use bytes;
782             $len= 16-length($msg)%16;
783     }
784     $msg = "\0"x($len).$msg;
785     $msg = $my_cipher->encrypt($msg);
786     chomp($msg = &encode_base64($msg));
787     # there are no newlines allowed inside msg
788     $msg=~ s/\n//g;
789     return $msg;
793 sub decrypt_msg {
795     my ($msg, $key) = @_ ;
796     $msg = &decode_base64($msg);
797     my $my_cipher = &create_ciphering($key);
798     $msg = $my_cipher->decrypt($msg); 
799     $msg =~ s/\0*//g;
800     return $msg;
804 sub get_encrypt_key {
805     my ($target) = @_ ;
806     my $encrypt_key;
807     my $error = 0;
809     # target can be in known_server
810     if( not defined $encrypt_key ) {
811         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
812         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
813         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
814             my $host_name = $hit->{hostname};
815             if( $host_name ne $target ) {
816                 next;
817             }
818             $encrypt_key = $hit->{hostkey};
819             last;
820         }
821     }
823     # target can be in known_client
824     if( not defined $encrypt_key ) {
825         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
826         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
827         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
828             my $host_name = $hit->{hostname};
829             if( $host_name ne $target ) {
830                 next;
831             }
832             $encrypt_key = $hit->{hostkey};
833             last;
834         }
835     }
837     return $encrypt_key;
841 #===  FUNCTION  ================================================================
842 #         NAME:  open_socket
843 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
844 #                [PeerPort] string necessary if port not appended by PeerAddr
845 #      RETURNS:  socket IO::Socket::INET
846 #  DESCRIPTION:  open a socket to PeerAddr
847 #===============================================================================
848 sub open_socket {
849     my ($PeerAddr, $PeerPort) = @_ ;
850     if(defined($PeerPort)){
851         $PeerAddr = $PeerAddr.":".$PeerPort;
852     }
853     my $socket;
854     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
855             Porto => "tcp",
856             Type => SOCK_STREAM,
857             Timeout => 5,
858             );
859     if(not defined $socket) {
860         return;
861     }
862 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
863     return $socket;
867 sub send_msg_to_target {
868     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
869     my $error = 0;
870     my $header;
871     my $timestamp = &get_time();
872     my $new_status;
873     my $act_status;
874     my ($sql_statement, $res);
875   
876     if( $msg_header ) {
877         $header = "'$msg_header'-";
878     } else {
879         $header = "";
880     }
882         # Patch the source ip
883         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
884                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
885                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
886         }
888     # encrypt xml msg
889     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
891     # opensocket
892     my $socket = &open_socket($address);
893     if( !$socket ) {
894         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
895         $error++;
896     }
897     
898     if( $error == 0 ) {
899         # send xml msg
900         print $socket $crypted_msg."\n";
902         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
903         daemon_log("$session_id DEBUG: message:\n$msg", 9);
904         
905     }
907     # close socket in any case
908     if( $socket ) {
909         close $socket;
910     }
912     if( $error > 0 ) { $new_status = "down"; }
913     else { $new_status = $msg_header; }
916     # known_clients
917     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
918     $res = $known_clients_db->select_dbentry($sql_statement);
919     if( keys(%$res) == 1) {
920         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
921         if ($act_status eq "down" && $new_status eq "down") {
922             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
923             $res = $known_clients_db->del_dbentry($sql_statement);
924             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
925         } else { 
926             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
927             $res = $known_clients_db->update_dbentry($sql_statement);
928             if($new_status eq "down"){
929                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
930             } else {
931                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
932             }
933         }
934     }
936     # known_server
937     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
938     $res = $known_server_db->select_dbentry($sql_statement);
939     if( keys(%$res) == 1) {
940         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
941         if ($act_status eq "down" && $new_status eq "down") {
942             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
943             $res = $known_server_db->del_dbentry($sql_statement);
944             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
945         } 
946         else { 
947             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
948             $res = $known_server_db->update_dbentry($sql_statement);
949             if($new_status eq "down"){
950                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
951             } else {
952                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
953             }
954         }
955     }
956     return $error; 
960 sub update_jobdb_status_for_send_msgs {
961     my ($session_id, $answer, $error) = @_;
962     &daemon_log("$session_id DEBUG: try to update job status", 7); 
963     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
964         my $jobdb_id = $1;
965     
966         $answer =~ /<header>(.*)<\/header>/;
967         my $job_header = $1;
969         $answer =~ /<target>(.*)<\/target>/;
970         my $job_target = $1;
971             
972         # Sending msg failed
973         if( $error ) {
975             # Set jobs to done, jobs do not need to deliver their message in any case
976             if (($job_header eq "trigger_action_localboot")
977                     ||($job_header eq "trigger_action_lock")
978                     ||($job_header eq "trigger_action_halt") 
979                     ) {
980                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
981                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
982                 my $res = $job_db->update_dbentry($sql_statement);
983                 
984             # Reactivate jobs, jobs need to deliver their message
985             } elsif (($job_header eq "trigger_action_activate")
986                     ||($job_header eq "trigger_action_update")
987                     ||($job_header eq "trigger_action_reinstall") 
988                     ||($job_header eq "trigger_activate_new")
989                     ) {
990                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
992             # For all other messages
993             } else {
994                 my $sql_statement = "UPDATE $job_queue_tn ".
995                     "SET status='error', result='can not deliver msg, please consult log file' ".
996                     "WHERE id=$jobdb_id";
997                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
998                 my $res = $job_db->update_dbentry($sql_statement);
999             }
1001         # Sending msg was successful
1002         } else {
1003             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1004             # jobs reinstall, update, inst_update do themself setting to done
1005             if (($job_header eq "trigger_action_localboot")
1006                     ||($job_header eq "trigger_action_lock")
1007                     ||($job_header eq "trigger_action_activate")
1008                     ||($job_header eq "trigger_action_halt") 
1009                     ||($job_header eq "trigger_action_reboot")
1010                     ||($job_header eq "trigger_action_wake")
1011                     ||($job_header eq "trigger_wake")
1012                     ) {
1014                 my $sql_statement = "UPDATE $job_queue_tn ".
1015                     "SET status='done' ".
1016                     "WHERE id=$jobdb_id AND status='processed'";
1017                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1018                 my $res = $job_db->update_dbentry($sql_statement);
1019             } else { 
1020                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1021             } 
1022         } 
1023     } else { 
1024         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1025     }
1028 sub reactivate_job_with_delay {
1029     my ($session_id, $target, $header, $delay) = @_ ;
1030     # Sometimes the client is still booting or does not wake up, in this case reactivate the job (if it exists) with a delay of n sec
1031     
1032     if (not defined $delay) { $delay = 30 } ;
1033     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1035     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1036     my $res = $job_db->update_dbentry($sql);
1037     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1038             "cause client '$target' is currently not available", 5);
1039     daemon_log("$session_id $sql", 7);                             
1040     return;
1044 sub sig_handler {
1045         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1046         daemon_log("0 INFO got signal '$signal'", 1); 
1047         $kernel->sig_handled();
1048         return;
1052 sub msg_to_decrypt {
1053         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1054         my $session_id = $session->ID;
1055         my ($msg, $msg_hash, $module);
1056         my $error = 0;
1058         # fetch new msg out of @msgs_to_decrypt
1059         my $tmp_next_msg = shift @msgs_to_decrypt;
1060     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1062         # msg is from a new client or gosa
1063         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1065         # msg is from a gosa-si-server
1066         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1067                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1068         }
1069         # msg is from a gosa-si-client
1070         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1071                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1072         }
1073         # an error occurred
1074         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1075                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1076                 # could not understand a msg from its server the client cause a re-registering process
1077         my $remote_ip = $heap->{'remote_ip'};
1078         my $remote_port = $heap->{'remote_port'};
1079         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1080         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1082                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1083                         "' to cause a re-registering of the client if necessary", 3);
1084                 $error++;
1085         }
1088         my $header;
1089         my $target;
1090         my $source;
1091         my $done = 0;
1092         my $sql;
1093         my $res;
1095         # check whether this message should be processed here
1096         if ($error == 0) {
1097                 $header = @{$msg_hash->{'header'}}[0];
1098                 $target = @{$msg_hash->{'target'}}[0];
1099                 $source = @{$msg_hash->{'source'}}[0];
1100                 my $not_found_in_known_clients_db = 0;
1101                 my $not_found_in_known_server_db = 0;
1102                 my $not_found_in_foreign_clients_db = 0;
1103                 my $local_address;
1104                 my $local_mac;
1105                 my ($target_ip, $target_port) = split(':', $target);
1107                 # Determine the local ip address if target is an ip address
1108                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1109                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1110                 } else {
1111                         $local_address = $server_address;
1112                 }
1114                 # Determine the local mac address if target is a mac address
1115                 if ($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) {
1116                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1117                         my $network_interface= &get_interface_for_ip($loc_ip);
1118                         $local_mac = &get_mac_for_interface($network_interface);
1119                 } else {
1120                         $local_mac = $server_mac_address;
1121                 }
1123                 # target and source is equal to GOSA -> process here
1124                 if (not $done) {
1125                         if ($target eq "GOSA" && $source eq "GOSA") {
1126                                 $done = 1;                    
1127                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1128                         }
1129                 }
1131                 # target is own address without forward_to_gosa-tag -> process here
1132                 if (not $done) {
1133                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1134                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1135                                 $done = 1;
1136                                 if ($source eq "GOSA") {
1137                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1138                                 }
1139                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1140                         }
1141                 }
1143                 # target is a client address in known_clients -> process here
1144                 if (not $done) {
1145                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1146                         $res = $known_clients_db->select_dbentry($sql);
1147                         if (keys(%$res) > 0) {
1148                                 $done = 1; 
1149                                 my $hostname = $res->{1}->{'hostname'};
1150                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1151                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1152                                 if ($source eq "GOSA") {
1153                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1154                                 }
1155                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1157                         } else {
1158                                 $not_found_in_known_clients_db = 1;
1159                         }
1160                 }
1162                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1163                 if (not $done) {
1164                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1165                         my $gosa_at;
1166                         my $gosa_session_id;
1167                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1168                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1169                                 if ($gosa_at ne $local_address) {
1170                                         $done = 1;
1171                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1172                                 }
1173                         }
1174                 }
1176                 # if message should be processed here -> add message to incoming_db
1177                 if ($done) {
1178                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1179                         # so gosa-si-server knows how to process this kind of messages
1180                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1181                                 $module = "GosaPackages";
1182                         }
1184                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1185                                         primkey=>[],
1186                                         headertag=>$header,
1187                                         targettag=>$target,
1188                                         xmlmessage=>&encode_base64($msg),
1189                                         timestamp=>&get_time,
1190                                         module=>$module,
1191                                         sessionid=>$session_id,
1192                                 } );
1194                 }
1196                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1197                 if (not $done) {
1198                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1199                         my $gosa_at;
1200                         my $gosa_session_id;
1201                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1202                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1203                                 if ($gosa_at eq $local_address) {
1204                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1205                                         if( defined $session_reference ) {
1206                                                 $heap = $session_reference->get_heap();
1207                                         }
1208                                         if(exists $heap->{'client'}) {
1209                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1210                                                 $heap->{'client'}->put($msg);
1211                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1212                                         }
1213                                         $done = 1;
1214                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1215                                 }
1216                         }
1218                 }
1220                 # target is a client address in foreign_clients -> forward to registration server
1221                 if (not $done) {
1222                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1223                         $res = $foreign_clients_db->select_dbentry($sql);
1224                         if (keys(%$res) > 0) {
1225                                 my $hostname = $res->{1}->{'hostname'};
1226                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1227                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1228                                 my $regserver = $res->{1}->{'regserver'};
1229                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1230                                 my $res = $known_server_db->select_dbentry($sql);
1231                                 if (keys(%$res) > 0) {
1232                                         my $regserver_key = $res->{1}->{'hostkey'};
1233                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1234                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1235                                         if ($source eq "GOSA") {
1236                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1237                                         }
1238                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1239                                 }
1240                                 $done = 1;
1241                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1242                         } else {
1243                                 $not_found_in_foreign_clients_db = 1;
1244                         }
1245                 }
1247                 # target is a server address -> forward to server
1248                 if (not $done) {
1249                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1250                         $res = $known_server_db->select_dbentry($sql);
1251                         if (keys(%$res) > 0) {
1252                                 my $hostkey = $res->{1}->{'hostkey'};
1254                                 if ($source eq "GOSA") {
1255                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1256                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1258                                 }
1260                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1261                                 $done = 1;
1262                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1263                         } else {
1264                                 $not_found_in_known_server_db = 1;
1265                         }
1266                 }
1269                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1270                 if ( $not_found_in_foreign_clients_db 
1271                         && $not_found_in_known_server_db
1272                         && $not_found_in_known_clients_db) {
1273                         &daemon_log("$session_id DEBUG: target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here", 7);
1274             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1275                 $module = "GosaPackages"; 
1276             }
1277                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1278                                         primkey=>[],
1279                                         headertag=>$header,
1280                                         targettag=>$target,
1281                                         xmlmessage=>&encode_base64($msg),
1282                                         timestamp=>&get_time,
1283                                         module=>$module,
1284                                         sessionid=>$session_id,
1285                                 } );
1286                         $done = 1;
1287                 }
1290                 if (not $done) {
1291                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1292                         if ($source eq "GOSA") {
1293                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1294                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1296                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1297                                 if( defined $session_reference ) {
1298                                         $heap = $session_reference->get_heap();
1299                                 }
1300                                 if(exists $heap->{'client'}) {
1301                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1302                                         $heap->{'client'}->put($error_msg);
1303                                 }
1304                         }
1305                 }
1307         }
1309         return;
1313 sub next_task {
1314     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1315     my $running_task = POE::Wheel::Run->new(
1316             Program => sub { process_task($session, $heap, $task) },
1317             StdioFilter => POE::Filter::Reference->new(),
1318             StdoutEvent  => "task_result",
1319             StderrEvent  => "task_debug",
1320             CloseEvent   => "task_done",
1321             );
1322     $heap->{task}->{ $running_task->ID } = $running_task;
1325 sub handle_task_result {
1326     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1327     my $client_answer = $result->{'answer'};
1328     if( $client_answer =~ s/session_id=(\d+)$// ) {
1329         my $session_id = $1;
1330         if( defined $session_id ) {
1331             my $session_reference = $kernel->ID_id_to_session($session_id);
1332             if( defined $session_reference ) {
1333                 $heap = $session_reference->get_heap();
1334             }
1335         }
1337         if(exists $heap->{'client'}) {
1338             $heap->{'client'}->put($client_answer);
1339         }
1340     }
1341     $kernel->sig(CHLD => "child_reap");
1344 sub handle_task_debug {
1345     my $result = $_[ARG0];
1346     print STDERR "$result\n";
1349 sub handle_task_done {
1350     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1351     delete $heap->{task}->{$task_id};
1354 sub process_task {
1355     no strict "refs";
1356     #CHECK: Not @_[...]?
1357     my ($session, $heap, $task) = @_;
1358     my $error = 0;
1359     my $answer_l;
1360     my ($answer_header, @answer_target_l, $answer_source);
1361     my $client_answer = "";
1363     # prepare all variables needed to process message
1364     #my $msg = $task->{'xmlmessage'};
1365     my $msg = &decode_base64($task->{'xmlmessage'});
1366     my $incoming_id = $task->{'id'};
1367     my $module = $task->{'module'};
1368     my $header =  $task->{'headertag'};
1369     my $session_id = $task->{'sessionid'};
1370                 my $msg_hash;
1371                 eval {
1372         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1373                 }; 
1374                 daemon_log("ERROR: XML failure '$@'") if ($@);
1375     my $source = @{$msg_hash->{'source'}}[0];
1376     
1377     # set timestamp of incoming client uptodate, so client will not 
1378     # be deleted from known_clients because of expiration
1379     my $cur_time = &get_time();
1380     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1381     my $res = $known_clients_db->exec_statement($sql);
1383     ######################
1384     # process incoming msg
1385     if( $error == 0) {
1386         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1387         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1388         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1390         if ( 0 < @{$answer_l} ) {
1391             my $answer_str = join("\n", @{$answer_l});
1392             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1393                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1394             }
1395             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1396         } else {
1397             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1398         }
1400     }
1401     if( !$answer_l ) { $error++ };
1403     ########
1404     # answer
1405     if( $error == 0 ) {
1407         foreach my $answer ( @{$answer_l} ) {
1408             # check outgoing msg to xml validity
1409             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1410             if( not defined $answer_hash ) { next; }
1411             
1412             $answer_header = @{$answer_hash->{'header'}}[0];
1413             @answer_target_l = @{$answer_hash->{'target'}};
1414             $answer_source = @{$answer_hash->{'source'}}[0];
1416             # deliver msg to all targets 
1417             foreach my $answer_target ( @answer_target_l ) {
1419                 # targets of msg are all gosa-si-clients in known_clients_db
1420                 if( $answer_target eq "*" ) {
1421                     # answer is for all clients
1422                     my $sql_statement= "SELECT * FROM known_clients";
1423                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1424                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1425                         my $host_name = $hit->{hostname};
1426                         my $host_key = $hit->{hostkey};
1427                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1428                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1429                     }
1430                 }
1432                 # targets of msg are all gosa-si-server in known_server_db
1433                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1434                     # answer is for all server in known_server
1435                     my $sql_statement= "SELECT * FROM $known_server_tn";
1436                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1437                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1438                         my $host_name = $hit->{hostname};
1439                         my $host_key = $hit->{hostkey};
1440                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1441                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1442                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1443                     }
1444                 }
1446                 # target of msg is GOsa
1447                                 elsif( $answer_target eq "GOSA" ) {
1448                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1449                                         my $add_on = "";
1450                     if( defined $session_id ) {
1451                         $add_on = ".session_id=$session_id";
1452                     }
1453                     # answer is for GOSA and has to returned to connected client
1454                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1455                     $client_answer = $gosa_answer.$add_on;
1456                 }
1458                 # target of msg is job queue at this host
1459                 elsif( $answer_target eq "JOBDB") {
1460                     $answer =~ /<header>(\S+)<\/header>/;   
1461                     my $header;
1462                     if( defined $1 ) { $header = $1; }
1463                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1464                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1465                 }
1467                 # Target of msg is a mac address
1468                 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 ) {
1469                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1471                     # Looking for macaddress in known_clients
1472                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1473                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1474                     my $found_ip_flag = 0;
1475                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1476                         my $host_name = $hit->{hostname};
1477                         my $host_key = $hit->{hostkey};
1478                         $answer =~ s/$answer_target/$host_name/g;
1479                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1480                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1481                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1482                         $found_ip_flag++ ;
1483                     }   
1485                     # Looking for macaddress in foreign_clients
1486                     if ($found_ip_flag == 0) {
1487                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1488                         my $res = $foreign_clients_db->select_dbentry($sql);
1489                         while( my ($hit_num, $hit) = each %{ $res } ) {
1490                             my $host_name = $hit->{hostname};
1491                             my $reg_server = $hit->{regserver};
1492                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1493                             
1494                             # Fetch key for reg_server
1495                             my $reg_server_key;
1496                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1497                             my $res = $known_server_db->select_dbentry($sql);
1498                             if (exists $res->{1}) {
1499                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1500                             } else {
1501                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1502                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1503                                 $reg_server_key = undef;
1504                             }
1506                             # Send answer to server where client is registered
1507                             if (defined $reg_server_key) {
1508                                 $answer =~ s/$answer_target/$host_name/g;
1509                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1510                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1511                                 $found_ip_flag++ ;
1512                             }
1513                         }
1514                     }
1516                     # No mac to ip matching found
1517                     if( $found_ip_flag == 0) {
1518                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1519                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1520                     }
1522                 # Answer is for one specific host   
1523                 } else {
1524                     # get encrypt_key
1525                     my $encrypt_key = &get_encrypt_key($answer_target);
1526                     if( not defined $encrypt_key ) {
1527                         # unknown target
1528                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1529                         next;
1530                     }
1531                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1532                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1533                 }
1534             }
1535         }
1536     }
1538     my $filter = POE::Filter::Reference->new();
1539     my %result = ( 
1540             status => "seems ok to me",
1541             answer => $client_answer,
1542             );
1544     my $output = $filter->put( [ \%result ] );
1545     print @$output;
1550 sub session_start {
1551     my ($kernel) = $_[KERNEL];
1552     $global_kernel = $kernel;
1553     $kernel->yield('register_at_foreign_servers');
1554         $kernel->yield('create_fai_server_db', $fai_server_tn );
1555         $kernel->yield('create_fai_release_db', $fai_release_tn );
1556     $kernel->yield('watch_for_next_tasks');
1557         $kernel->sig(USR1 => "sig_handler");
1558         $kernel->sig(USR2 => "recreate_packages_db");
1559         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1560         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1561     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1562         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1563     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1564         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1565     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1567     # Start opsi check
1568     if ($opsi_enabled eq "true") {
1569         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1570     }
1575 sub watch_for_done_jobs {
1576         #CHECK: $heap for what?
1577         my ($kernel,$heap) = @_[KERNEL, HEAP];
1579         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1580         my $res = $job_db->select_dbentry( $sql_statement );
1582         while( my ($id, $hit) = each %{$res} ) {
1583                 my $jobdb_id = $hit->{id};
1584                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1585                 my $res = $job_db->del_dbentry($sql_statement); 
1586         }
1588         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1592 sub watch_for_opsi_jobs {
1593     my ($kernel) = $_[KERNEL];
1595     # This is not very nice to look for opsi install jobs, but headertag has to be trigger_action_reinstall. The only way to identify a 
1596     # opsi install job is to parse the xml message. There is still the correct header.
1597     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1598         my $res = $job_db->select_dbentry( $sql_statement );
1600     # Ask OPSI for an update of the running jobs
1601     while (my ($id, $hit) = each %$res ) {
1602         # Determine current parameters of the job
1603         my $hostId = $hit->{'plainname'};
1604         my $macaddress = $hit->{'macaddress'};
1605         my $progress = $hit->{'progress'};
1607         my $result= {};
1608         
1609         # For hosts, only return the products that are or get installed
1610         my $callobj;
1611         $callobj = {
1612             method  => 'getProductStates_hash',
1613             params  => [ $hostId ],
1614             id  => 1,
1615         };
1616         
1617         my $hres = $opsi_client->call($opsi_url, $callobj);
1618         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1619         if (not &check_opsi_res($hres)) {
1620             my $htmp= $hres->result->{$hostId};
1621         
1622             # Check state != not_installed or action == setup -> load and add
1623             my $products= 0;
1624             my $installed= 0;
1625             my $installing = 0;
1626             my $error= 0;  
1627             my @installed_list;
1628             my @error_list;
1629             my $act_status = "none";
1630             foreach my $product (@{$htmp}){
1632                 if ($product->{'installationStatus'} ne "not_installed" or
1633                         $product->{'actionRequest'} eq "setup"){
1635                     # Increase number of products for this host
1636                     $products++;
1637         
1638                     if ($product->{'installationStatus'} eq "failed"){
1639                         $result->{$product->{'productId'}}= "error";
1640                         unshift(@error_list, $product->{'productId'});
1641                         $error++;
1642                     }
1643                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1644                         $result->{$product->{'productId'}}= "installed";
1645                         unshift(@installed_list, $product->{'productId'});
1646                         $installed++;
1647                     }
1648                     if ($product->{'installationStatus'} eq "installing"){
1649                         $result->{$product->{'productId'}}= "installing";
1650                         $installing++;
1651                         $act_status = "installing - ".$product->{'productId'};
1652                     }
1653                 }
1654             }
1655         
1656             # Estimate "rough" progress, avoid division by zero
1657             if ($products == 0) {
1658                 $result->{'progress'}= 0;
1659             } else {
1660                 $result->{'progress'}= int($installed * 100 / $products);
1661             }
1663             # Set updates in job queue
1664             if ((not $error) && (not $installing) && ($installed)) {
1665                 $act_status = "installed - ".join(", ", @installed_list);
1666             }
1667             if ($error) {
1668                 $act_status = "error - ".join(", ", @error_list);
1669             }
1670             if ($progress ne $result->{'progress'} ) {
1671                 # Updating progress and result 
1672                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1673                 my $update_res = $job_db->update_dbentry($update_statement);
1674             }
1675             if ($progress eq 100) { 
1676                 # Updateing status
1677                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1678                 if ($error) {
1679                     $done_statement .= "status='error'";
1680                 } else {
1681                     $done_statement .= "status='done'";
1682                 }
1683                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1684                 my $done_res = $job_db->update_dbentry($done_statement);
1685             }
1688         }
1689     }
1691     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1695 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1696 sub watch_for_modified_jobs {
1697     my ($kernel,$heap) = @_[KERNEL, HEAP];
1699     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1700     my $res = $job_db->select_dbentry( $sql_statement );
1701     
1702     # if db contains no jobs which should be update, do nothing
1703     if (keys %$res != 0) {
1705         if ($job_synchronization  eq "true") {
1706             # make out of the db result a gosa-si message   
1707             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1708  
1709             # update all other SI-server
1710             &inform_all_other_si_server($update_msg);
1711         }
1713         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1714         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1715         $res = $job_db->update_dbentry($sql_statement);
1716     }
1718     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1722 sub watch_for_new_jobs {
1723         if($watch_for_new_jobs_in_progress == 0) {
1724                 $watch_for_new_jobs_in_progress = 1;
1725                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1727                 # check gosa job quaeue for jobs with executable timestamp
1728                 my $timestamp = &get_time();
1729                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1730                 my $res = $job_db->exec_statement( $sql_statement );
1732                 # Merge all new jobs that would do the same actions
1733                 my @drops;
1734                 my $hits;
1735                 foreach my $hit (reverse @{$res} ) {
1736                         my $macaddress= lc @{$hit}[8];
1737                         my $headertag= @{$hit}[5];
1738                         if(
1739                                 defined($hits->{$macaddress}) &&
1740                                 defined($hits->{$macaddress}->{$headertag}) &&
1741                                 defined($hits->{$macaddress}->{$headertag}[0])
1742                         ) {
1743                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1744                         }
1745                         $hits->{$macaddress}->{$headertag}= $hit;
1746                 }
1748                 # Delete new jobs with a matching job in state 'processing'
1749                 foreach my $macaddress (keys %{$hits}) {
1750                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1751                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1752                                 if(defined($jobdb_id)) {
1753                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1754                                         my $res = $job_db->exec_statement( $sql_statement );
1755                                         foreach my $hit (@{$res}) {
1756                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1757                                         }
1758                                 } else {
1759                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1760                                 }
1761                         }
1762                 }
1764                 # Commit deletion
1765                 $job_db->exec_statementlist(\@drops);
1767                 # Look for new jobs that could be executed
1768                 foreach my $macaddress (keys %{$hits}) {
1770                         # Look if there is an executing job
1771                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1772                         my $res = $job_db->exec_statement( $sql_statement );
1774                         # Skip new jobs for host if there is a processing job
1775                         if(defined($res) and defined @{$res}[0]) {
1776                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1777                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1778                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1779                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1780                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1781                                         if(defined($res_2) and defined @{$res_2}[0]) {
1782                                                 # Set status from goto-activation to 'waiting' and update timestamp
1783                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1784                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1785                                         }
1786                                 }
1787                                 next;
1788                         }
1790                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1791                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1792                                 if(defined($jobdb_id)) {
1793                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1795                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1796                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1797                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1799                                         # expect macaddress is unique!!!!!!
1800                                         my $target = $res_hash->{1}->{hostname};
1802                                         # change header
1803                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1805                                         # add sqlite_id
1806                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1808                                         $job_msg =~ /<header>(\S+)<\/header>/;
1809                                         my $header = $1 ;
1810                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1812                                         # update status in job queue to ...
1813                     # ... 'processing', for jobs: 'reinstall', 'update'
1814                     if (($header =~ /gosa_trigger_action_reinstall/) 
1815                             || ($header =~ /gosa_trigger_activate_new/)
1816                             || ($header =~ /gosa_trigger_action_update/)) {
1817                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1818                         my $dbres = $job_db->update_dbentry($sql_statement);
1819                     }
1821                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1822                     else {
1823                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1824                         my $dbres = $job_db->update_dbentry($sql_statement);
1825                     }
1826                 
1828                                         # We don't want parallel processing
1829                                         last;
1830                                 }
1831                         }
1832                 }
1834                 $watch_for_new_jobs_in_progress = 0;
1835                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1836         }
1840 sub watch_for_new_messages {
1841     my ($kernel,$heap) = @_[KERNEL, HEAP];
1842     my @coll_user_msg;   # collection list of outgoing messages
1843     
1844     # check messaging_db for new incoming messages with executable timestamp
1845     my $timestamp = &get_time();
1846     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1847     my $res = $messaging_db->exec_statement( $sql_statement );
1848         foreach my $hit (@{$res}) {
1850         # create outgoing messages
1851         my $message_to = @{$hit}[3];
1852         # translate message_to to plain login name
1853         my @message_to_l = split(/,/, $message_to);  
1854                 my %receiver_h; 
1855                 foreach my $receiver (@message_to_l) {
1856                         if ($receiver =~ /^u_([\s\S]*)$/) {
1857                                 $receiver_h{$1} = 0;
1858                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1859                                 my $group_name = $1;
1860                                 # fetch all group members from ldap and add them to receiver hash
1861                                 my $ldap_handle = &get_ldap_handle();
1862                                 if (defined $ldap_handle) {
1863                                                 my $mesg = $ldap_handle->search(
1864                                                                                 base => $ldap_base,
1865                                                                                 scope => 'sub',
1866                                                                                 attrs => ['memberUid'],
1867                                                                                 filter => "cn=$group_name",
1868                                                                                 );
1869                                                 if ($mesg->count) {
1870                                                                 my @entries = $mesg->entries;
1871                                                                 foreach my $entry (@entries) {
1872                                                                                 my @receivers= $entry->get_value("memberUid");
1873                                                                                 foreach my $receiver (@receivers) { 
1874                                                                                                 $receiver_h{$receiver} = 0;
1875                                                                                 }
1876                                                                 }
1877                                                 } 
1878                                                 # translating errors ?
1879                                                 if ($mesg->code) {
1880                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1881                                                 }
1882                                 # ldap handle error ?           
1883                                 } else {
1884                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1885                                 }
1886                         } else {
1887                                 my $sbjct = &encode_base64(@{$hit}[1]);
1888                                 my $msg = &encode_base64(@{$hit}[7]);
1889                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1890                         }
1891                 }
1892                 my @receiver_l = keys(%receiver_h);
1894         my $message_id = @{$hit}[0];
1896         #add each outgoing msg to messaging_db
1897         my $receiver;
1898         foreach $receiver (@receiver_l) {
1899             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1900                 "VALUES ('".
1901                 $message_id."', '".    # id
1902                 @{$hit}[1]."', '".     # subject
1903                 @{$hit}[2]."', '".     # message_from
1904                 $receiver."', '".      # message_to
1905                 "none"."', '".         # flag
1906                 "out"."', '".          # direction
1907                 @{$hit}[6]."', '".     # delivery_time
1908                 @{$hit}[7]."', '".     # message
1909                 $timestamp."'".     # timestamp
1910                 ")";
1911             &daemon_log("M DEBUG: $sql_statement", 1);
1912             my $res = $messaging_db->exec_statement($sql_statement);
1913             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1914         }
1916         # set incoming message to flag d=deliverd
1917         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1918         &daemon_log("M DEBUG: $sql_statement", 7);
1919         $res = $messaging_db->update_dbentry($sql_statement);
1920         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1921     }
1923     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1924     return;
1927 sub watch_for_delivery_messages {
1928     my ($kernel, $heap) = @_[KERNEL, HEAP];
1930     # select outgoing messages
1931     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1932     #&daemon_log("0 DEBUG: $sql", 7);
1933     my $res = $messaging_db->exec_statement( $sql_statement );
1934     
1935     # build out msg for each    usr
1936     foreach my $hit (@{$res}) {
1937         my $receiver = @{$hit}[3];
1938         my $msg_id = @{$hit}[0];
1939         my $subject = @{$hit}[1];
1940         my $message = @{$hit}[7];
1942         # resolve usr -> host where usr is logged in
1943         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1944         #&daemon_log("0 DEBUG: $sql", 7);
1945         my $res = $login_users_db->exec_statement($sql);
1947         # receiver is logged in nowhere
1948         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1950         # receiver ist logged in at a client registered at local server
1951                 my $send_succeed = 0;
1952                 foreach my $hit (@$res) {
1953                                 my $receiver_host = @$hit[0];
1954                 my $delivered2host = 0;
1955                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1957                                 # Looking for host in know_clients_db 
1958                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1959                                 my $res = $known_clients_db->exec_statement($sql);
1961                 # Host is known in known_clients_db
1962                 if (ref(@$res[0]) eq "ARRAY") {
1963                     my $receiver_key = @{@{$res}[0]}[2];
1964                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1965                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1966                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1967                     if ($error == 0 ) {
1968                         $send_succeed++ ;
1969                         $delivered2host++ ;
1970                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1971                     } else {
1972                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1973                     }
1974                 }
1975                 
1976                 # Message already send, do not need to do anything more, otherwise ...
1977                 if ($delivered2host) { next;}
1978     
1979                 # ...looking for host in foreign_clients_db
1980                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
1981                 $res = $foreign_clients_db->exec_statement($sql);
1982   
1983                                 # Host is known in foreign_clients_db 
1984                                 if (ref(@$res[0]) eq "ARRAY") { 
1985                     my $registration_server = @{@{$res}[0]}[2];
1986                     
1987                     # Fetch encryption key for registration server
1988                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
1989                     my $res = $known_server_db->exec_statement($sql);
1990                     if (ref(@$res[0]) eq "ARRAY") { 
1991                         my $registration_server_key = @{@{$res}[0]}[3];
1992                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1993                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1994                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
1995                         if ($error == 0 ) {
1996                             $send_succeed++ ;
1997                             $delivered2host++ ;
1998                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
1999                         } else {
2000                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2001                         }
2003                     } else {
2004                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2005                                 "registrated at server '$registration_server', ".
2006                                 "but no data available in known_server_db ", 1); 
2007                     }
2008                 }
2009                 
2010                 if (not $delivered2host) {
2011                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2012                 }
2013                 }
2015                 if ($send_succeed) {
2016                                 # set outgoing msg at db to deliverd
2017                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2018                                 my $res = $messaging_db->exec_statement($sql); 
2019                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2020                 } else {
2021             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2022         }
2023         }
2025     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2026     return;
2030 sub watch_for_done_messages {
2031     my ($kernel,$heap) = @_[KERNEL, HEAP];
2033     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2034     #&daemon_log("0 DEBUG: $sql", 7);
2035     my $res = $messaging_db->exec_statement($sql); 
2037     foreach my $hit (@{$res}) {
2038         my $msg_id = @{$hit}[0];
2040         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2041         #&daemon_log("0 DEBUG: $sql", 7); 
2042         my $res = $messaging_db->exec_statement($sql);
2044         # not all usr msgs have been seen till now
2045         if ( ref(@$res[0]) eq "ARRAY") { next; }
2046         
2047         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2048         #&daemon_log("0 DEBUG: $sql", 7);
2049         $res = $messaging_db->exec_statement($sql);
2050     
2051     }
2053     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2054     return;
2058 sub watch_for_old_known_clients {
2059     my ($kernel,$heap) = @_[KERNEL, HEAP];
2061     my $sql_statement = "SELECT * FROM $known_clients_tn";
2062     my $res = $known_clients_db->select_dbentry( $sql_statement );
2064     my $cur_time = int(&get_time());
2066     while ( my ($hit_num, $hit) = each %$res) {
2067         my $expired_timestamp = int($hit->{'timestamp'});
2068         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2069         my $dt = DateTime->new( year   => $1,
2070                 month  => $2,
2071                 day    => $3,
2072                 hour   => $4,
2073                 minute => $5,
2074                 second => $6,
2075                 );
2077         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2078         $expired_timestamp = $dt->ymd('').$dt->hms('');
2079         if ($cur_time > $expired_timestamp) {
2080             my $hostname = $hit->{'hostname'};
2081             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2082             my $del_res = $known_clients_db->exec_statement($del_sql);
2084             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2085         }
2087     }
2089     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2093 sub watch_for_next_tasks {
2094     my ($kernel,$heap) = @_[KERNEL, HEAP];
2096     my $sql = "SELECT * FROM $incoming_tn";
2097     my $res = $incoming_db->select_dbentry($sql);
2098     
2099     while ( my ($hit_num, $hit) = each %$res) {
2100         my $headertag = $hit->{'headertag'};
2101         if ($headertag =~ /^answer_(\d+)/) {
2102             # do not start processing, this message is for a still running POE::Wheel
2103             next;
2104         }
2105         my $message_id = $hit->{'id'};
2106         my $session_id = $hit->{'sessionid'};
2107         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2108         $kernel->yield('next_task', $hit);
2110         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2111         my $res = $incoming_db->exec_statement($sql);
2112     }
2114     $kernel->delay_set('watch_for_next_tasks', 1); 
2118 sub get_ldap_handle {
2119         my ($session_id) = @_;
2120         my $heap;
2121         my $ldap_handle;
2123         if (not defined $session_id ) { $session_id = 0 };
2124         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2126         if ($session_id == 0) {
2127                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
2128                 $ldap_handle = Net::LDAP->new( $ldap_uri );
2129                 if (defined $ldap_handle) {
2130                         $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!"); 
2131                 } else {
2132                         daemon_log("$session_id ERROR: creation of a new LDAP handle failed (ldap_uri '$ldap_uri')");
2133                 }
2135         } else {
2136                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2137                 if( defined $session_reference ) {
2138                         $heap = $session_reference->get_heap();
2139                 }
2141                 if (not defined $heap) {
2142                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
2143                         return;
2144                 }
2146                 # TODO: This "if" is nonsense, because it doesn't prove that the
2147                 #       used handle is still valid - or if we've to reconnect...
2148                 #if (not exists $heap->{ldap_handle}) {
2149                         $ldap_handle = Net::LDAP->new( $ldap_uri );
2150                         $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!"); 
2151                         $heap->{ldap_handle} = $ldap_handle;
2152                 #}
2153         }
2154         return $ldap_handle;
2158 sub change_fai_state {
2159     my ($st, $targets, $session_id) = @_;
2160     $session_id = 0 if not defined $session_id;
2161     # Set FAI state to localboot
2162     my %mapActions= (
2163         reboot    => '',
2164         update    => 'softupdate',
2165         localboot => 'localboot',
2166         reinstall => 'install',
2167         rescan    => '',
2168         wake      => '',
2169         memcheck  => 'memcheck',
2170         sysinfo   => 'sysinfo',
2171         install   => 'install',
2172     );
2174     # Return if this is unknown
2175     if (!exists $mapActions{ $st }){
2176         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2177       return;
2178     }
2180     my $state= $mapActions{ $st };
2182     my $ldap_handle = &get_ldap_handle($session_id);
2183     if( defined($ldap_handle) ) {
2185       # Build search filter for hosts
2186         my $search= "(&(objectClass=GOhard)";
2187         foreach (@{$targets}){
2188             $search.= "(macAddress=$_)";
2189         }
2190         $search.= ")";
2192       # If there's any host inside of the search string, procress them
2193         if (!($search =~ /macAddress/)){
2194             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2195             return;
2196         }
2198       # Perform search for Unit Tag
2199       my $mesg = $ldap_handle->search(
2200           base   => $ldap_base,
2201           scope  => 'sub',
2202           attrs  => ['dn', 'FAIstate', 'objectClass'],
2203           filter => "$search"
2204           );
2206           if ($mesg->count) {
2207                   my @entries = $mesg->entries;
2208                   if (0 == @entries) {
2209                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2210                   }
2212                   foreach my $entry (@entries) {
2213                           # Only modify entry if it is not set to '$state'
2214                           if ($entry->get_value("FAIstate") ne "$state"){
2215                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2216                                   my $result;
2217                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2218                                   if (exists $tmp{'FAIobject'}){
2219                                           if ($state eq ''){
2220                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2221                                           } else {
2222                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2223                                           }
2224                                   } elsif ($state ne ''){
2225                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2226                                   }
2228                                   # Errors?
2229                                   if ($result->code){
2230                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2231                                   }
2232                           } else {
2233                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2234                           }  
2235                   }
2236           } else {
2237                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2238           }
2240     # if no ldap handle defined
2241     } else {
2242         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2243     }
2245         return;
2249 sub change_goto_state {
2250     my ($st, $targets, $session_id) = @_;
2251     $session_id = 0  if not defined $session_id;
2253     # Switch on or off?
2254     my $state= $st eq 'active' ? 'active': 'locked';
2256     my $ldap_handle = &get_ldap_handle($session_id);
2257     if( defined($ldap_handle) ) {
2259       # Build search filter for hosts
2260       my $search= "(&(objectClass=GOhard)";
2261       foreach (@{$targets}){
2262         $search.= "(macAddress=$_)";
2263       }
2264       $search.= ")";
2266       # If there's any host inside of the search string, procress them
2267       if (!($search =~ /macAddress/)){
2268         return;
2269       }
2271       # Perform search for Unit Tag
2272       my $mesg = $ldap_handle->search(
2273           base   => $ldap_base,
2274           scope  => 'sub',
2275           attrs  => ['dn', 'gotoMode'],
2276           filter => "$search"
2277           );
2279       if ($mesg->count) {
2280         my @entries = $mesg->entries;
2281         foreach my $entry (@entries) {
2283           # Only modify entry if it is not set to '$state'
2284           if ($entry->get_value("gotoMode") ne $state){
2286             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2287             my $result;
2288             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2290             # Errors?
2291             if ($result->code){
2292               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2293             }
2295           }
2296         }
2297       } else {
2298                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2299           }
2301     }
2305 sub run_recreate_packages_db {
2306     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2307     my $session_id = $session->ID;
2308         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2309         $kernel->yield('create_fai_release_db', $fai_release_tn);
2310         $kernel->yield('create_fai_server_db', $fai_server_tn);
2311         return;
2315 sub run_create_fai_server_db {
2316     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2317     my $session_id = $session->ID;
2318     my $task = POE::Wheel::Run->new(
2319             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2320             StdoutEvent  => "session_run_result",
2321             StderrEvent  => "session_run_debug",
2322             CloseEvent   => "session_run_done",
2323             );
2325     $heap->{task}->{ $task->ID } = $task;
2326     return;
2330 sub create_fai_server_db {
2331         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2332         my $result;
2334         if (not defined $session_id) { $session_id = 0; }
2335         my $ldap_handle = &get_ldap_handle();
2336         if(defined($ldap_handle)) {
2337                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2338                 my $mesg= $ldap_handle->search(
2339                         base   => $ldap_base,
2340                         scope  => 'sub',
2341                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2342                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2343                 );
2344                 if($mesg->{'resultCode'} == 0 &&
2345                         $mesg->count != 0) {
2346                         foreach my $entry (@{$mesg->{entries}}) {
2347                                 if($entry->exists('FAIrepository')) {
2348                                         # Add an entry for each Repository configured for server
2349                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2350                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2351                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2352                                                 $result= $fai_server_db->add_dbentry( { 
2353                                                                 table => $table_name,
2354                                                                 primkey => ['server', 'fai_release', 'tag'],
2355                                                                 server => $tmp_url,
2356                                                                 fai_release => $tmp_release,
2357                                                                 sections => $tmp_sections,
2358                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2359                                                         } );
2360                                         }
2361                                 }
2362                         }
2363                 }
2364                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2366                 # TODO: Find a way to post the 'create_packages_list_db' event
2367                 if(not defined($dont_create_packages_list)) {
2368                         &create_packages_list_db(undef, undef, $session_id);
2369                 }
2370         }       
2372         $ldap_handle->disconnect;
2373         return $result;
2377 sub run_create_fai_release_db {
2378         my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2379         my $session_id = $session->ID;
2380         my $task = POE::Wheel::Run->new(
2381                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2382                 StdoutEvent  => "session_run_result",
2383                 StderrEvent  => "session_run_debug",
2384                 CloseEvent   => "session_run_done",
2385         );
2387         $heap->{task}->{ $task->ID } = $task;
2388         return;
2392 sub create_fai_release_db {
2393         my ($table_name, $session_id) = @_;
2394         my $result;
2396         # used for logging
2397         if (not defined $session_id) { $session_id = 0; }
2399         my $ldap_handle = &get_ldap_handle();
2400         if(defined($ldap_handle)) {
2401                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2402                 my $mesg= $ldap_handle->search(
2403                         base   => $ldap_base,
2404                         scope  => 'sub',
2405                         attrs  => [],
2406                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2407                 );
2408                 if(($mesg->code == 0) && ($mesg->count != 0))
2409                 {
2410                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2412                         # Walk through all possible FAI container ou's
2413                         my @sql_list;
2414                         my $timestamp= &get_time();
2415                         foreach my $ou (@{$mesg->{entries}}) {
2416                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2417                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2418                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2419                                         if(@tmp_array) {
2420                                                 foreach my $entry (@tmp_array) {
2421                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2422                                                                 my $sql= 
2423                                                                 "INSERT INTO $table_name "
2424                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2425                                                                 .$timestamp.","
2426                                                                 ."'".$entry->{'release'}."',"
2427                                                                 ."'".$entry->{'class'}."',"
2428                                                                 ."'".$entry->{'type'}."',"
2429                                                                 ."'".$entry->{'state'}."')";
2430                                                                 push @sql_list, $sql;
2431                                                         }
2432                                                 }
2433                                         }
2434                                 }
2435                         }
2437                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2438                         if(@sql_list) {
2439                                 unshift @sql_list, "VACUUM";
2440                                 unshift @sql_list, "DELETE FROM $table_name";
2441                                 $fai_release_db->exec_statementlist(\@sql_list);
2442                         }
2443                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2444                 } else {
2445                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2446                 }
2447                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2448         }
2449         $ldap_handle->disconnect;
2450         return $result;
2453 sub get_fai_types {
2454         my $tmp_classes = shift || return undef;
2455         my @result;
2457         foreach my $type(keys %{$tmp_classes}) {
2458                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2459                         my $entry = {
2460                                 type => $type,
2461                                 state => $tmp_classes->{$type}[0],
2462                         };
2463                         push @result, $entry;
2464                 }
2465         }
2467         return @result;
2470 sub get_fai_state {
2471         my $result = "";
2472         my $tmp_classes = shift || return $result;
2474         foreach my $type(keys %{$tmp_classes}) {
2475                 if(defined($tmp_classes->{$type}[0])) {
2476                         $result = $tmp_classes->{$type}[0];
2477                         
2478                 # State is equal for all types in class
2479                         last;
2480                 }
2481         }
2483         return $result;
2486 sub resolve_fai_classes {
2487         my ($fai_base, $ldap_handle, $session_id) = @_;
2488         if (not defined $session_id) { $session_id = 0; }
2489         my $result;
2490         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2491         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2492         my $fai_classes;
2494         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2495         my $mesg= $ldap_handle->search(
2496                 base   => $fai_base,
2497                 scope  => 'sub',
2498                 attrs  => ['cn','objectClass','FAIstate'],
2499                 filter => $fai_filter,
2500         );
2501         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2503         if($mesg->{'resultCode'} == 0 &&
2504                 $mesg->count != 0) {
2505                 foreach my $entry (@{$mesg->{entries}}) {
2506                         if($entry->exists('cn')) {
2507                                 my $tmp_dn= $entry->dn();
2508                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2509                                         - length($fai_base) - 1 );
2511                                 # Skip classname and ou dn parts for class
2512                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2514                                 # Skip classes without releases
2515                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2516                                         next;
2517                                 }
2519                                 my $tmp_cn= $entry->get_value('cn');
2520                                 my $tmp_state= $entry->get_value('FAIstate');
2522                                 my $tmp_type;
2523                                 # Get FAI type
2524                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2525                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2526                                                 $tmp_type= $oclass;
2527                                                 last;
2528                                         }
2529                                 }
2531                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2532                                         # A Subrelease
2533                                         my @sub_releases = split(/,/, $tmp_release);
2535                                         # Walk through subreleases and build hash tree
2536                                         my $hash;
2537                                         while(my $tmp_sub_release = pop @sub_releases) {
2538                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2539                                         }
2540                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2541                                 } else {
2542                                         # A branch, no subrelease
2543                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2544                                 }
2545                         } elsif (!$entry->exists('cn')) {
2546                                 my $tmp_dn= $entry->dn();
2547                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2548                                         - length($fai_base) - 1 );
2549                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2551                                 # Skip classes without releases
2552                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2553                                         next;
2554                                 }
2556                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2557                                         # A Subrelease
2558                                         my @sub_releases= split(/,/, $tmp_release);
2560                                         # Walk through subreleases and build hash tree
2561                                         my $hash;
2562                                         while(my $tmp_sub_release = pop @sub_releases) {
2563                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2564                                         }
2565                                         # Remove the last two characters
2566                                         chop($hash);
2567                                         chop($hash);
2569                                         eval('$fai_classes->'.$hash.'= {}');
2570                                 } else {
2571                                         # A branch, no subrelease
2572                                         if(!exists($fai_classes->{$tmp_release})) {
2573                                                 $fai_classes->{$tmp_release} = {};
2574                                         }
2575                                 }
2576                         }
2577                 }
2579                 # The hash is complete, now we can honor the copy-on-write based missing entries
2580                 foreach my $release (keys %$fai_classes) {
2581                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2582                 }
2583         }
2584         return $result;
2587 sub apply_fai_inheritance {
2588        my $fai_classes = shift || return {};
2589        my $tmp_classes;
2591        # Get the classes from the branch
2592        foreach my $class (keys %{$fai_classes}) {
2593                # Skip subreleases
2594                if($class =~ /^ou=.*$/) {
2595                        next;
2596                } else {
2597                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2598                }
2599        }
2601        # Apply to each subrelease
2602        foreach my $subrelease (keys %{$fai_classes}) {
2603                if($subrelease =~ /ou=/) {
2604                        foreach my $tmp_class (keys %{$tmp_classes}) {
2605                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2606                                        $fai_classes->{$subrelease}->{$tmp_class} =
2607                                        deep_copy($tmp_classes->{$tmp_class});
2608                                } else {
2609                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2610                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2611                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2612                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2613                                                }
2614                                        }
2615                                }
2616                        }
2617                }
2618        }
2620        # Find subreleases in deeper levels
2621        foreach my $subrelease (keys %{$fai_classes}) {
2622                if($subrelease =~ /ou=/) {
2623                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2624                                if($subsubrelease =~ /ou=/) {
2625                                        apply_fai_inheritance($fai_classes->{$subrelease});
2626                                }
2627                        }
2628                }
2629        }
2631        return $fai_classes;
2634 sub get_fai_release_entries {
2635         my $tmp_classes = shift || return;
2636         my $parent = shift || "";
2637         my @result = shift || ();
2639         foreach my $entry (keys %{$tmp_classes}) {
2640                 if(defined($entry)) {
2641                         if($entry =~ /^ou=.*$/) {
2642                                 my $release_name = $entry;
2643                                 $release_name =~ s/ou=//g;
2644                                 if(length($parent)>0) {
2645                                         $release_name = $parent."/".$release_name;
2646                                 }
2647                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2648                                 foreach my $bufentry(@bufentries) {
2649                                         push @result, $bufentry;
2650                                 }
2651                         } else {
2652                                 my @types = get_fai_types($tmp_classes->{$entry});
2653                                 foreach my $type (@types) {
2654                                         push @result, 
2655                                         {
2656                                                 'class' => $entry,
2657                                                 'type' => $type->{'type'},
2658                                                 'release' => $parent,
2659                                                 'state' => $type->{'state'},
2660                                         };
2661                                 }
2662                         }
2663                 }
2664         }
2666         return @result;
2669 sub deep_copy {
2670         my $this = shift;
2671         if (not ref $this) {
2672                 $this;
2673         } elsif (ref $this eq "ARRAY") {
2674                 [map deep_copy($_), @$this];
2675         } elsif (ref $this eq "HASH") {
2676                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2677         } else { die "what type is $_?" }
2681 sub session_run_result {
2682     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2683     $kernel->sig(CHLD => "child_reap");
2686 sub session_run_debug {
2687     my $result = $_[ARG0];
2688     print STDERR "$result\n";
2691 sub session_run_done {
2692     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2693     delete $heap->{task}->{$task_id};
2697 sub create_sources_list {
2698         my $session_id = shift;
2699         my $ldap_handle = &main::get_ldap_handle;
2700         my $result="/tmp/gosa_si_tmp_sources_list";
2702         # Remove old file
2703         if(stat($result)) {
2704                 unlink($result);
2705                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2706         }
2708         my $fh;
2709         open($fh, ">$result");
2710         if (not defined $fh) {
2711                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2712                 return undef;
2713         }
2714         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2715                 my $mesg=$ldap_handle->search(
2716                         base    => $main::ldap_server_dn,
2717                         scope   => 'base',
2718                         attrs   => 'FAIrepository',
2719                         filter  => 'objectClass=FAIrepositoryServer'
2720                 );
2721                 if($mesg->count) {
2722                         foreach my $entry(@{$mesg->{'entries'}}) {
2723                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2724                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2725                                         my $line = "deb $server $release";
2726                                         $sections =~ s/,/ /g;
2727                                         $line.= " $sections";
2728                                         print $fh $line."\n";
2729                                 }
2730                         }
2731                 }
2732         } else {
2733                 if (defined $main::ldap_server_dn){
2734                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2735                 } else {
2736                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2737                 }
2738         }
2739         close($fh);
2741         return $result;
2745 sub run_create_packages_list_db {
2746     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2747         my $session_id = $session->ID;
2749         my $task = POE::Wheel::Run->new(
2750                                         Priority => +20,
2751                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2752                                         StdoutEvent  => "session_run_result",
2753                                         StderrEvent  => "session_run_debug",
2754                                         CloseEvent   => "session_run_done",
2755                                         );
2756         $heap->{task}->{ $task->ID } = $task;
2760 sub create_packages_list_db {
2761         my ($ldap_handle, $sources_file, $session_id) = @_;
2762         
2763         # it should not be possible to trigger a recreation of packages_list_db
2764         # while packages_list_db is under construction, so set flag packages_list_under_construction
2765         # which is tested befor recreation can be started
2766         if (-r $packages_list_under_construction) {
2767                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2768                 return;
2769         } else {
2770                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2771                 # set packages_list_under_construction to true
2772                 system("touch $packages_list_under_construction");
2773                 @packages_list_statements=();
2774         }
2776         if (not defined $session_id) { $session_id = 0; }
2777         if (not defined $ldap_handle) { 
2778                 $ldap_handle= &get_ldap_handle();
2780                 if (not defined $ldap_handle) {
2781                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2782                         unlink($packages_list_under_construction);
2783                         return;
2784                 }
2785         }
2786         if (not defined $sources_file) { 
2787                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2788                 $sources_file = &create_sources_list($session_id);
2789         }
2791         if (not defined $sources_file) {
2792                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2793                 unlink($packages_list_under_construction);
2794                 return;
2795         }
2797         my $line;
2799         open(CONFIG, "<$sources_file") or do {
2800                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2801                 unlink($packages_list_under_construction);
2802                 return;
2803         };
2805         # Read lines
2806         while ($line = <CONFIG>){
2807                 # Unify
2808                 chop($line);
2809                 $line =~ s/^\s+//;
2810                 $line =~ s/^\s+/ /;
2812                 # Strip comments
2813                 $line =~ s/#.*$//g;
2815                 # Skip empty lines
2816                 if ($line =~ /^\s*$/){
2817                         next;
2818                 }
2820                 # Interpret deb line
2821                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2822                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2823                         my $section;
2824                         foreach $section (split(' ', $sections)){
2825                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2826                         }
2827                 }
2828         }
2830         close (CONFIG);
2832         if(keys(%repo_dirs)) {
2833                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2834                 &main::strip_packages_list_statements();
2835                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2836         }
2837         unlink($packages_list_under_construction);
2838         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2839         return;
2842 # This function should do some intensive task to minimize the db-traffic
2843 sub strip_packages_list_statements {
2844         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2845         my @new_statement_list=();
2846         my $hash;
2847         my $insert_hash;
2848         my $update_hash;
2849         my $delete_hash;
2850         my $known_packages_hash;
2851         my $local_timestamp=get_time();
2853         foreach my $existing_entry (@existing_entries) {
2854                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2855         }
2857         foreach my $statement (@packages_list_statements) {
2858                 if($statement =~ /^INSERT/i) {
2859                         # Assign the values from the insert statement
2860                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2861                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2862                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2863                                 # If section or description has changed, update the DB
2864                                 if( 
2865                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2866                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2867                                 ) {
2868                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2869                                 } else {
2870                                         # package is already present in database. cache this knowledge for later use
2871                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2872                                 }
2873                         } else {
2874                                 # Insert a non-existing entry to db
2875                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2876                         }
2877                 } elsif ($statement =~ /^UPDATE/i) {
2878                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2879                         /^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;
2880                         foreach my $distribution (keys %{$hash}) {
2881                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2882                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2883                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2884                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2885                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2886                                                 my $section;
2887                                                 my $description;
2888                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2889                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2890                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2891                                                 }
2892                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2893                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2894                                                 }
2895                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2896                                         }
2897                                 }
2898                         }
2899                 }
2900         }
2902         # Check for orphaned entries
2903         foreach my $existing_entry (@existing_entries) {
2904                 my $distribution= @{$existing_entry}[0];
2905                 my $package= @{$existing_entry}[1];
2906                 my $version= @{$existing_entry}[2];
2907                 my $section= @{$existing_entry}[3];
2909                 if(
2910                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2911                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2912                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2913                 ) {
2914                         next;
2915                 } else {
2916                         # Insert entry to delete hash
2917                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2918                 }
2919         }
2921         # unroll the insert hash
2922         foreach my $distribution (keys %{$insert_hash}) {
2923                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2924                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2925                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2926                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2927                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2928                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2929                                 ."'$local_timestamp')";
2930                         }
2931                 }
2932         }
2934         # unroll the update hash
2935         foreach my $distribution (keys %{$update_hash}) {
2936                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2937                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2938                                 my $set = "";
2939                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2940                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2941                                 }
2942                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2943                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2944                                 }
2945                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2946                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2947                                 }
2948                                 if(defined($set) and length($set) > 0) {
2949                                         $set .= "timestamp = '$local_timestamp'";
2950                                 } else {
2951                                         next;
2952                                 }
2953                                 push @new_statement_list, 
2954                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2955                                 ." distribution = '$distribution'"
2956                                 ." AND package = '$package'"
2957                                 ." AND version = '$version'";
2958                         }
2959                 }
2960         }
2961         
2962         # unroll the delete hash
2963         foreach my $distribution (keys %{$delete_hash}) {
2964                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2965                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2966                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2967                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2968                         }
2969                 }
2970         }
2972         unshift(@new_statement_list, "VACUUM");
2974         @packages_list_statements = @new_statement_list;
2978 sub parse_package_info {
2979     my ($baseurl, $dist, $section, $session_id)= @_;
2980     my ($package);
2981     if (not defined $session_id) { $session_id = 0; }
2982     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2983     $repo_dirs{ "${repo_path}/pool" } = 1;
2985     foreach $package ("Packages.gz"){
2986         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2987         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2988         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2989     }
2990     
2994 sub get_package {
2995     my ($url, $dest, $session_id)= @_;
2996     if (not defined $session_id) { $session_id = 0; }
2998     my $tpath = dirname($dest);
2999     -d "$tpath" || mkpath "$tpath";
3001     # This is ugly, but I've no time to take a look at "how it works in perl"
3002     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3003         system("gunzip -cd '$dest' > '$dest.in'");
3004         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3005         unlink($dest);
3006         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3007     } else {
3008         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3009     }
3010     return 0;
3014 sub parse_package {
3015     my ($path, $dist, $srv_path, $session_id)= @_;
3016     if (not defined $session_id) { $session_id = 0;}
3017     my ($package, $version, $section, $description);
3018     my $PACKAGES;
3019     my $timestamp = &get_time();
3021     if(not stat("$path.in")) {
3022         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3023         return;
3024     }
3026     open($PACKAGES, "<$path.in");
3027     if(not defined($PACKAGES)) {
3028         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3029         return;
3030     }
3032     # Read lines
3033     while (<$PACKAGES>){
3034         my $line = $_;
3035         # Unify
3036         chop($line);
3038         # Use empty lines as a trigger
3039         if ($line =~ /^\s*$/){
3040             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3041             push(@packages_list_statements, $sql);
3042             $package = "none";
3043             $version = "none";
3044             $section = "none";
3045             $description = "none"; 
3046             next;
3047         }
3049         # Trigger for package name
3050         if ($line =~ /^Package:\s/){
3051             ($package)= ($line =~ /^Package: (.*)$/);
3052             next;
3053         }
3055         # Trigger for version
3056         if ($line =~ /^Version:\s/){
3057             ($version)= ($line =~ /^Version: (.*)$/);
3058             next;
3059         }
3061         # Trigger for description
3062         if ($line =~ /^Description:\s/){
3063             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3064             next;
3065         }
3067         # Trigger for section
3068         if ($line =~ /^Section:\s/){
3069             ($section)= ($line =~ /^Section: (.*)$/);
3070             next;
3071         }
3073         # Trigger for filename
3074         if ($line =~ /^Filename:\s/){
3075             my ($filename) = ($line =~ /^Filename: (.*)$/);
3076             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3077             next;
3078         }
3079     }
3081     close( $PACKAGES );
3082     unlink( "$path.in" );
3086 sub store_fileinfo {
3087     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3089     my %fileinfo = (
3090         'package' => $package,
3091         'dist' => $dist,
3092         'version' => $vers,
3093     );
3095     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3099 sub cleanup_and_extract {
3100         my $fileinfo = $repo_files{ $File::Find::name };
3102         if( defined $fileinfo ) {
3103                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3104                 my $sql;
3105                 my $package = $fileinfo->{ 'package' };
3106                 my $newver = $fileinfo->{ 'version' };
3108                 mkpath($dir);
3109                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3111                 if( -f "$dir/DEBIAN/templates" ) {
3113                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3115                         my $tmpl= ""; {
3116                                 local $/=undef;
3117                                 open FILE, "$dir/DEBIAN/templates";
3118                                 $tmpl = &encode_base64(<FILE>);
3119                                 close FILE;
3120                         }
3121                         rmtree("$dir/DEBIAN/templates");
3123                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3124                         push @packages_list_statements, $sql;
3125                 }
3126         }
3128         return;
3132 sub register_at_foreign_servers {   
3133     my ($kernel) = $_[KERNEL];
3135     # hole alle bekannten server aus known_server_db
3136     my $server_sql = "SELECT * FROM $known_server_tn";
3137     my $server_res = $known_server_db->exec_statement($server_sql);
3139     # no entries in known_server_db
3140     if (not ref(@$server_res[0]) eq "ARRAY") { 
3141         # TODO
3142     }
3144     # detect already connected clients
3145     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3146     my $client_res = $known_clients_db->exec_statement($client_sql);
3148     # send my server details to all other gosa-si-server within the network
3149     foreach my $hit (@$server_res) {
3150         my $hostname = @$hit[0];
3151         my $hostkey = &create_passwd;
3153         # add already connected clients to registration message 
3154         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3155         &add_content2xml_hash($myhash, 'key', $hostkey);
3156         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3158         # add locally loaded gosa-si modules to registration message
3159         my $loaded_modules = {};
3160         while (my ($package, $pck_info) = each %$known_modules) {
3161                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3162                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3163                                                         $loaded_modules->{$act_module} = ""; 
3164                                                 }
3165         }
3167         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3169         # add macaddress to registration message
3170         my ($host_ip, $host_port) = split(/:/, $hostname);
3171         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3172         my $network_interface= &get_interface_for_ip($local_ip);
3173         my $host_mac = &get_mac_for_interface($network_interface);
3174         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3175         
3176         # build registration message and send it
3177         my $foreign_server_msg = &create_xml_string($myhash);
3178         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3179     }
3180     
3181     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3182     return;
3186 #==== MAIN = main ==============================================================
3187 #  parse commandline options
3188 Getopt::Long::Configure( "bundling" );
3189 GetOptions("h|help" => \&usage,
3190         "c|config=s" => \$cfg_file,
3191         "f|foreground" => \$foreground,
3192         "v|verbose+" => \$verbose,
3193         "no-arp+" => \$no_arp,
3194            );
3196 #  read and set config parameters
3197 &check_cmdline_param ;
3198 &read_configfile($cfg_file, %cfg_defaults);
3199 &check_pid;
3201 $SIG{CHLD} = 'IGNORE';
3203 # forward error messages to logfile
3204 if( ! $foreground ) {
3205   open( STDIN,  '+>/dev/null' );
3206   open( STDOUT, '+>&STDIN'    );
3207   open( STDERR, '+>&STDIN'    );
3210 # Just fork, if we are not in foreground mode
3211 if( ! $foreground ) { 
3212     chdir '/'                 or die "Can't chdir to /: $!";
3213     $pid = fork;
3214     setsid                    or die "Can't start a new session: $!";
3215     umask 0;
3216 } else { 
3217     $pid = $$; 
3220 # Do something useful - put our PID into the pid_file
3221 if( 0 != $pid ) {
3222     open( LOCK_FILE, ">$pid_file" );
3223     print LOCK_FILE "$pid\n";
3224     close( LOCK_FILE );
3225     if( !$foreground ) { 
3226         exit( 0 ) 
3227     };
3230 # parse head url and revision from svn
3231 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3232 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3233 $server_headURL = defined $1 ? $1 : 'unknown' ;
3234 $server_revision = defined $2 ? $2 : 'unknown' ;
3235 if ($server_headURL =~ /\/tag\// || 
3236         $server_headURL =~ /\/branches\// ) {
3237     $server_status = "stable"; 
3238 } else {
3239     $server_status = "developmental" ;
3242 # Prepare log file
3243 $root_uid = getpwnam('root');
3244 $adm_gid = getgrnam('adm');
3245 chmod(0640, $log_file);
3246 chown($root_uid, $adm_gid, $log_file);
3247 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3249 daemon_log(" ", 1);
3250 daemon_log("$0 started!", 1);
3251 daemon_log("status: $server_status", 1);
3252 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3255     no strict "refs";
3257     if ($db_module eq "DBmysql") {
3258         # connect to incoming_db
3259         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3261         # connect to gosa-si job queue
3262         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3264         # connect to known_clients_db
3265         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3267         # connect to foreign_clients_db
3268         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3270         # connect to known_server_db
3271         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3273         # connect to login_usr_db
3274         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3276         # connect to fai_server_db 
3277         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3279         # connect to fai_release_db
3280         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3282         # connect to packages_list_db
3283         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3285         # connect to messaging_db
3286         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3288     } elsif ($db_module eq "DBsqlite") {
3289         # connect to incoming_db
3290         unlink($incoming_file_name);
3291         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3292         
3293         # connect to gosa-si job queue
3294         unlink($job_queue_file_name);  ## just for debugging
3295         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3296         chmod(0660, $job_queue_file_name);
3297         chown($root_uid, $adm_gid, $job_queue_file_name);
3298         
3299         # connect to known_clients_db
3300         unlink($known_clients_file_name);   ## just for debugging
3301         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3302         chmod(0660, $known_clients_file_name);
3303         chown($root_uid, $adm_gid, $known_clients_file_name);
3304         
3305         # connect to foreign_clients_db
3306         unlink($foreign_clients_file_name);
3307         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3308         chmod(0660, $foreign_clients_file_name);
3309         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3310         
3311         # connect to known_server_db
3312         unlink($known_server_file_name);
3313         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3314         chmod(0660, $known_server_file_name);
3315         chown($root_uid, $adm_gid, $known_server_file_name);
3316         
3317         # connect to login_usr_db
3318         unlink($login_users_file_name);
3319         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3320         chmod(0660, $login_users_file_name);
3321         chown($root_uid, $adm_gid, $login_users_file_name);
3322         
3323         # connect to fai_server_db
3324         unlink($fai_server_file_name);
3325         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3326         chmod(0660, $fai_server_file_name);
3327         chown($root_uid, $adm_gid, $fai_server_file_name);
3328         
3329         # connect to fai_release_db
3330         unlink($fai_release_file_name);
3331         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3332         chmod(0660, $fai_release_file_name);
3333         chown($root_uid, $adm_gid, $fai_release_file_name);
3334         
3335         # connect to packages_list_db
3336         #unlink($packages_list_file_name);
3337         unlink($packages_list_under_construction);
3338         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3339         chmod(0660, $packages_list_file_name);
3340         chown($root_uid, $adm_gid, $packages_list_file_name);
3341         
3342         # connect to messaging_db
3343         unlink($messaging_file_name);
3344         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3345         chmod(0660, $messaging_file_name);
3346         chown($root_uid, $adm_gid, $messaging_file_name);
3347     }
3351 # Creating tables
3352 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3353 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3354 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3355 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3356 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3357 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3358 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3359 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3360 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3361 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3363 # create xml object used for en/decrypting
3364 $xml = new XML::Simple();
3367 # foreign servers 
3368 my @foreign_server_list;
3370 # add foreign server from cfg file
3371 if ($foreign_server_string ne "") {
3372     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3373     foreach my $foreign_server (@cfg_foreign_server_list) {
3374         push(@foreign_server_list, $foreign_server);
3375     }
3377     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3380 # Perform a DNS lookup for server registration if flag is true
3381 if ($dns_lookup eq "true") {
3382     # Add foreign server from dns
3383     my @tmp_servers;
3384     if (not $server_domain) {
3385         # Try our DNS Searchlist
3386         for my $domain(get_dns_domains()) {
3387             chomp($domain);
3388             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3389             if(@$tmp_domains) {
3390                 for my $tmp_server(@$tmp_domains) {
3391                     push @tmp_servers, $tmp_server;
3392                 }
3393             }
3394         }
3395         if(@tmp_servers && length(@tmp_servers)==0) {
3396             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3397         }
3398     } else {
3399         @tmp_servers = &get_server_addresses($server_domain);
3400         if( 0 == @tmp_servers ) {
3401             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3402         }
3403     }
3405     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3407     foreach my $server (@tmp_servers) { 
3408         unshift(@foreign_server_list, $server); 
3409     }
3410 } else {
3411     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3415 # eliminate duplicate entries
3416 @foreign_server_list = &del_doubles(@foreign_server_list);
3417 my $all_foreign_server = join(", ", @foreign_server_list);
3418 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3420 # add all found foreign servers to known_server
3421 my $cur_timestamp = &get_time();
3422 foreach my $foreign_server (@foreign_server_list) {
3424         # do not add myself to known_server_db
3425         if (&is_local($foreign_server)) { next; }
3426         ######################################
3428     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3429             primkey=>['hostname'],
3430             hostname=>$foreign_server,
3431             macaddress=>"",
3432             status=>'not_yet_registered',
3433             hostkey=>"none",
3434             loaded_modules => "none", 
3435             timestamp=>$cur_timestamp,
3436             } );
3440 # Import all modules
3441 &import_modules;
3443 # Check wether all modules are gosa-si valid passwd check
3444 &password_check;
3446 # Prepare for using Opsi 
3447 if ($opsi_enabled eq "true") {
3448     use JSON::RPC::Client;
3449     use XML::Quote qw(:all);
3450     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3451     $opsi_client = new JSON::RPC::Client;
3455 POE::Component::Server::TCP->new(
3456         Alias => "TCP_SERVER",
3457         Port => $server_port,
3458         ClientInput => sub {
3459                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3460         my $session_id = $session->ID;
3461         my $remote_ip = $heap->{'remote_ip'};
3462                 push(@msgs_to_decrypt, $input);
3463         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3464                 $kernel->yield("msg_to_decrypt");
3465         },
3466         InlineStates => {
3467                 msg_to_decrypt => \&msg_to_decrypt,
3468                 next_task => \&next_task,
3469                 task_result => \&handle_task_result,
3470                 task_done   => \&handle_task_done,
3471                 task_debug  => \&handle_task_debug,
3472                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3473         }
3474 );
3476 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3478 # create session for repeatedly checking the job queue for jobs
3479 POE::Session->create(
3480         inline_states => {
3481                 _start => \&session_start,
3482         register_at_foreign_servers => \&register_at_foreign_servers,
3483         sig_handler => \&sig_handler,
3484         next_task => \&next_task,
3485         task_result => \&handle_task_result,
3486         task_done   => \&handle_task_done,
3487         task_debug  => \&handle_task_debug,
3488         watch_for_next_tasks => \&watch_for_next_tasks,
3489         watch_for_new_messages => \&watch_for_new_messages,
3490         watch_for_delivery_messages => \&watch_for_delivery_messages,
3491         watch_for_done_messages => \&watch_for_done_messages,
3492                 watch_for_new_jobs => \&watch_for_new_jobs,
3493         watch_for_modified_jobs => \&watch_for_modified_jobs,
3494         watch_for_done_jobs => \&watch_for_done_jobs,
3495         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3496         watch_for_old_known_clients => \&watch_for_old_known_clients,
3497         create_packages_list_db => \&run_create_packages_list_db,
3498         create_fai_server_db => \&run_create_fai_server_db,
3499         create_fai_release_db => \&run_create_fai_release_db,
3500                 recreate_packages_db => \&run_recreate_packages_db,
3501         session_run_result => \&session_run_result,
3502         session_run_debug => \&session_run_debug,
3503         session_run_done => \&session_run_done,
3504         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3505         }
3506 );
3509 POE::Kernel->run();
3510 exit;