Code

Updated setup, sambaMachineAccountRDN closes #621
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
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 auto_increment",
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 auto_increment",
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 $act_time = &get_time();
1380     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_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 $act_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 ($act_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->{'resultCode'} == 0 &&
2409                         $mesg->count != 0) {
2410                         # Walk through all possible FAI container ou's
2411                         my @sql_list;
2412                         my $timestamp= &get_time();
2413                         foreach my $ou (@{$mesg->{entries}}) {
2414                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2415                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2416                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2417                                         if(@tmp_array) {
2418                                                 foreach my $entry (@tmp_array) {
2419                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2420                                                                 my $sql= 
2421                                                                 "INSERT INTO $table_name "
2422                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2423                                                                 .$timestamp.","
2424                                                                 ."'".$entry->{'release'}."',"
2425                                                                 ."'".$entry->{'class'}."',"
2426                                                                 ."'".$entry->{'type'}."',"
2427                                                                 ."'".$entry->{'state'}."')";
2428                                                                 push @sql_list, $sql;
2429                                                         }
2430                                                 }
2431                                         }
2432                                 }
2433                         }
2435                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2436                         if(@sql_list) {
2437                                 unshift @sql_list, "DELETE FROM $table_name";
2438                                 $fai_release_db->exec_statementlist(\@sql_list);
2439                         }
2440                         daemon_log("$session_id DEBUG: Done with inserting",7);
2441                 }
2442                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2443         }
2444         $ldap_handle->disconnect;
2445         return $result;
2448 sub get_fai_types {
2449         my $tmp_classes = shift || return undef;
2450         my @result;
2452         foreach my $type(keys %{$tmp_classes}) {
2453                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2454                         my $entry = {
2455                                 type => $type,
2456                                 state => $tmp_classes->{$type}[0],
2457                         };
2458                         push @result, $entry;
2459                 }
2460         }
2462         return @result;
2465 sub get_fai_state {
2466         my $result = "";
2467         my $tmp_classes = shift || return $result;
2469         foreach my $type(keys %{$tmp_classes}) {
2470                 if(defined($tmp_classes->{$type}[0])) {
2471                         $result = $tmp_classes->{$type}[0];
2472                         
2473                 # State is equal for all types in class
2474                         last;
2475                 }
2476         }
2478         return $result;
2481 sub resolve_fai_classes {
2482         my ($fai_base, $ldap_handle, $session_id) = @_;
2483         if (not defined $session_id) { $session_id = 0; }
2484         my $result;
2485         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2486         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2487         my $fai_classes;
2489         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2490         my $mesg= $ldap_handle->search(
2491                 base   => $fai_base,
2492                 scope  => 'sub',
2493                 attrs  => ['cn','objectClass','FAIstate'],
2494                 filter => $fai_filter,
2495         );
2496         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2498         if($mesg->{'resultCode'} == 0 &&
2499                 $mesg->count != 0) {
2500                 foreach my $entry (@{$mesg->{entries}}) {
2501                         if($entry->exists('cn')) {
2502                                 my $tmp_dn= $entry->dn();
2504                                 # Skip classname and ou dn parts for class
2505                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2507                                 # Skip classes without releases
2508                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2509                                         next;
2510                                 }
2512                                 my $tmp_cn= $entry->get_value('cn');
2513                                 my $tmp_state= $entry->get_value('FAIstate');
2515                                 my $tmp_type;
2516                                 # Get FAI type
2517                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2518                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2519                                                 $tmp_type= $oclass;
2520                                                 last;
2521                                         }
2522                                 }
2524                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2525                                         # A Subrelease
2526                                         my @sub_releases = split(/,/, $tmp_release);
2528                                         # Walk through subreleases and build hash tree
2529                                         my $hash;
2530                                         while(my $tmp_sub_release = pop @sub_releases) {
2531                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2532                                         }
2533                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2534                                 } else {
2535                                         # A branch, no subrelease
2536                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2537                                 }
2538                         } elsif (!$entry->exists('cn')) {
2539                                 my $tmp_dn= $entry->dn();
2540                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2542                                 # Skip classes without releases
2543                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2544                                         next;
2545                                 }
2547                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2548                                         # A Subrelease
2549                                         my @sub_releases= split(/,/, $tmp_release);
2551                                         # Walk through subreleases and build hash tree
2552                                         my $hash;
2553                                         while(my $tmp_sub_release = pop @sub_releases) {
2554                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2555                                         }
2556                                         # Remove the last two characters
2557                                         chop($hash);
2558                                         chop($hash);
2560                                         eval('$fai_classes->'.$hash.'= {}');
2561                                 } else {
2562                                         # A branch, no subrelease
2563                                         if(!exists($fai_classes->{$tmp_release})) {
2564                                                 $fai_classes->{$tmp_release} = {};
2565                                         }
2566                                 }
2567                         }
2568                 }
2570                 # The hash is complete, now we can honor the copy-on-write based missing entries
2571                 foreach my $release (keys %$fai_classes) {
2572                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2573                 }
2574         }
2575         return $result;
2578 sub apply_fai_inheritance {
2579        my $fai_classes = shift || return {};
2580        my $tmp_classes;
2582        # Get the classes from the branch
2583        foreach my $class (keys %{$fai_classes}) {
2584                # Skip subreleases
2585                if($class =~ /^ou=.*$/) {
2586                        next;
2587                } else {
2588                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2589                }
2590        }
2592        # Apply to each subrelease
2593        foreach my $subrelease (keys %{$fai_classes}) {
2594                if($subrelease =~ /ou=/) {
2595                        foreach my $tmp_class (keys %{$tmp_classes}) {
2596                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2597                                        $fai_classes->{$subrelease}->{$tmp_class} =
2598                                        deep_copy($tmp_classes->{$tmp_class});
2599                                } else {
2600                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2601                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2602                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2603                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2604                                                }
2605                                        }
2606                                }
2607                        }
2608                }
2609        }
2611        # Find subreleases in deeper levels
2612        foreach my $subrelease (keys %{$fai_classes}) {
2613                if($subrelease =~ /ou=/) {
2614                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2615                                if($subsubrelease =~ /ou=/) {
2616                                        apply_fai_inheritance($fai_classes->{$subrelease});
2617                                }
2618                        }
2619                }
2620        }
2622        return $fai_classes;
2625 sub get_fai_release_entries {
2626         my $tmp_classes = shift || return;
2627         my $parent = shift || "";
2628         my @result = shift || ();
2630         foreach my $entry (keys %{$tmp_classes}) {
2631                 if(defined($entry)) {
2632                         if($entry =~ /^ou=.*$/) {
2633                                 my $release_name = $entry;
2634                                 $release_name =~ s/ou=//g;
2635                                 if(length($parent)>0) {
2636                                         $release_name = $parent."/".$release_name;
2637                                 }
2638                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2639                                 foreach my $bufentry(@bufentries) {
2640                                         push @result, $bufentry;
2641                                 }
2642                         } else {
2643                                 my @types = get_fai_types($tmp_classes->{$entry});
2644                                 foreach my $type (@types) {
2645                                         push @result, 
2646                                         {
2647                                                 'class' => $entry,
2648                                                 'type' => $type->{'type'},
2649                                                 'release' => $parent,
2650                                                 'state' => $type->{'state'},
2651                                         };
2652                                 }
2653                         }
2654                 }
2655         }
2657         return @result;
2660 sub deep_copy {
2661         my $this = shift;
2662         if (not ref $this) {
2663                 $this;
2664         } elsif (ref $this eq "ARRAY") {
2665                 [map deep_copy($_), @$this];
2666         } elsif (ref $this eq "HASH") {
2667                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2668         } else { die "what type is $_?" }
2672 sub session_run_result {
2673     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2674     $kernel->sig(CHLD => "child_reap");
2677 sub session_run_debug {
2678     my $result = $_[ARG0];
2679     print STDERR "$result\n";
2682 sub session_run_done {
2683     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2684     delete $heap->{task}->{$task_id};
2688 sub create_sources_list {
2689         my $session_id = shift;
2690         my $ldap_handle = &main::get_ldap_handle;
2691         my $result="/tmp/gosa_si_tmp_sources_list";
2693         # Remove old file
2694         if(stat($result)) {
2695                 unlink($result);
2696                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2697         }
2699         my $fh;
2700         open($fh, ">$result");
2701         if (not defined $fh) {
2702                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2703                 return undef;
2704         }
2705         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2706                 my $mesg=$ldap_handle->search(
2707                         base    => $main::ldap_server_dn,
2708                         scope   => 'base',
2709                         attrs   => 'FAIrepository',
2710                         filter  => 'objectClass=FAIrepositoryServer'
2711                 );
2712                 if($mesg->count) {
2713                         foreach my $entry(@{$mesg->{'entries'}}) {
2714                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2715                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2716                                         my $line = "deb $server $release";
2717                                         $sections =~ s/,/ /g;
2718                                         $line.= " $sections";
2719                                         print $fh $line."\n";
2720                                 }
2721                         }
2722                 }
2723         } else {
2724                 if (defined $main::ldap_server_dn){
2725                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2726                 } else {
2727                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2728                 }
2729         }
2730         close($fh);
2732         return $result;
2736 sub run_create_packages_list_db {
2737     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2738         my $session_id = $session->ID;
2740         my $task = POE::Wheel::Run->new(
2741                                         Priority => +20,
2742                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2743                                         StdoutEvent  => "session_run_result",
2744                                         StderrEvent  => "session_run_debug",
2745                                         CloseEvent   => "session_run_done",
2746                                         );
2747         $heap->{task}->{ $task->ID } = $task;
2751 sub create_packages_list_db {
2752         my ($ldap_handle, $sources_file, $session_id) = @_;
2753         
2754         # it should not be possible to trigger a recreation of packages_list_db
2755         # while packages_list_db is under construction, so set flag packages_list_under_construction
2756         # which is tested befor recreation can be started
2757         if (-r $packages_list_under_construction) {
2758                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2759                 return;
2760         } else {
2761                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2762                 # set packages_list_under_construction to true
2763                 system("touch $packages_list_under_construction");
2764                 @packages_list_statements=();
2765         }
2767         if (not defined $session_id) { $session_id = 0; }
2768         if (not defined $ldap_handle) { 
2769                 $ldap_handle= &get_ldap_handle();
2771                 if (not defined $ldap_handle) {
2772                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2773                         unlink($packages_list_under_construction);
2774                         return;
2775                 }
2776         }
2777         if (not defined $sources_file) { 
2778                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2779                 $sources_file = &create_sources_list($session_id);
2780         }
2782         if (not defined $sources_file) {
2783                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2784                 unlink($packages_list_under_construction);
2785                 return;
2786         }
2788         my $line;
2790         open(CONFIG, "<$sources_file") or do {
2791                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2792                 unlink($packages_list_under_construction);
2793                 return;
2794         };
2796         # Read lines
2797         while ($line = <CONFIG>){
2798                 # Unify
2799                 chop($line);
2800                 $line =~ s/^\s+//;
2801                 $line =~ s/^\s+/ /;
2803                 # Strip comments
2804                 $line =~ s/#.*$//g;
2806                 # Skip empty lines
2807                 if ($line =~ /^\s*$/){
2808                         next;
2809                 }
2811                 # Interpret deb line
2812                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2813                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2814                         my $section;
2815                         foreach $section (split(' ', $sections)){
2816                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2817                         }
2818                 }
2819         }
2821         close (CONFIG);
2823         if(keys(%repo_dirs)) {
2824                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2825                 &main::strip_packages_list_statements();
2826                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2827         }
2828         unlink($packages_list_under_construction);
2829         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2830         return;
2833 # This function should do some intensive task to minimize the db-traffic
2834 sub strip_packages_list_statements {
2835         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2836         my @new_statement_list=();
2837         my $hash;
2838         my $insert_hash;
2839         my $update_hash;
2840         my $delete_hash;
2841         my $known_packages_hash;
2842         my $local_timestamp=get_time();
2844         foreach my $existing_entry (@existing_entries) {
2845                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2846         }
2848         foreach my $statement (@packages_list_statements) {
2849                 if($statement =~ /^INSERT/i) {
2850                         # Assign the values from the insert statement
2851                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2852                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2853                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2854                                 # If section or description has changed, update the DB
2855                                 if( 
2856                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2857                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2858                                 ) {
2859                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2860                                 } else {
2861                                         # package is already present in database. cache this knowledge for later use
2862                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2863                                 }
2864                         } else {
2865                                 # Insert a non-existing entry to db
2866                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2867                         }
2868                 } elsif ($statement =~ /^UPDATE/i) {
2869                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2870                         /^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;
2871                         foreach my $distribution (keys %{$hash}) {
2872                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2873                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2874                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2875                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2876                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2877                                                 my $section;
2878                                                 my $description;
2879                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2880                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2881                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2882                                                 }
2883                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2884                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2885                                                 }
2886                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2887                                         }
2888                                 }
2889                         }
2890                 }
2891         }
2893         # Check for orphaned entries
2894         foreach my $existing_entry (@existing_entries) {
2895                 my $distribution= @{$existing_entry}[0];
2896                 my $package= @{$existing_entry}[1];
2897                 my $version= @{$existing_entry}[2];
2898                 my $section= @{$existing_entry}[3];
2900                 if(
2901                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2902                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2903                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2904                 ) {
2905                         next;
2906                 } else {
2907                         # Insert entry to delete hash
2908                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2909                 }
2910         }
2912         # unroll the insert hash
2913         foreach my $distribution (keys %{$insert_hash}) {
2914                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2915                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2916                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2917                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2918                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2919                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2920                                 ."'$local_timestamp')";
2921                         }
2922                 }
2923         }
2925         # unroll the update hash
2926         foreach my $distribution (keys %{$update_hash}) {
2927                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2928                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2929                                 my $set = "";
2930                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2931                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2932                                 }
2933                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2934                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2935                                 }
2936                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2937                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2938                                 }
2939                                 if(defined($set) and length($set) > 0) {
2940                                         $set .= "timestamp = '$local_timestamp'";
2941                                 } else {
2942                                         next;
2943                                 }
2944                                 push @new_statement_list, 
2945                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2946                                 ." distribution = '$distribution'"
2947                                 ." AND package = '$package'"
2948                                 ." AND version = '$version'";
2949                         }
2950                 }
2951         }
2952         
2953         # unroll the delete hash
2954         foreach my $distribution (keys %{$delete_hash}) {
2955                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2956                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2957                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2958                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2959                         }
2960                 }
2961         }
2963         @packages_list_statements = @new_statement_list;
2967 sub parse_package_info {
2968     my ($baseurl, $dist, $section, $session_id)= @_;
2969     my ($package);
2970     if (not defined $session_id) { $session_id = 0; }
2971     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2972     $repo_dirs{ "${repo_path}/pool" } = 1;
2974     foreach $package ("Packages.gz"){
2975         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2976         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2977         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2978     }
2979     
2983 sub get_package {
2984     my ($url, $dest, $session_id)= @_;
2985     if (not defined $session_id) { $session_id = 0; }
2987     my $tpath = dirname($dest);
2988     -d "$tpath" || mkpath "$tpath";
2990     # This is ugly, but I've no time to take a look at "how it works in perl"
2991     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2992         system("gunzip -cd '$dest' > '$dest.in'");
2993         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2994         unlink($dest);
2995         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2996     } else {
2997         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2998     }
2999     return 0;
3003 sub parse_package {
3004     my ($path, $dist, $srv_path, $session_id)= @_;
3005     if (not defined $session_id) { $session_id = 0;}
3006     my ($package, $version, $section, $description);
3007     my $PACKAGES;
3008     my $timestamp = &get_time();
3010     if(not stat("$path.in")) {
3011         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3012         return;
3013     }
3015     open($PACKAGES, "<$path.in");
3016     if(not defined($PACKAGES)) {
3017         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3018         return;
3019     }
3021     # Read lines
3022     while (<$PACKAGES>){
3023         my $line = $_;
3024         # Unify
3025         chop($line);
3027         # Use empty lines as a trigger
3028         if ($line =~ /^\s*$/){
3029             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3030             push(@packages_list_statements, $sql);
3031             $package = "none";
3032             $version = "none";
3033             $section = "none";
3034             $description = "none"; 
3035             next;
3036         }
3038         # Trigger for package name
3039         if ($line =~ /^Package:\s/){
3040             ($package)= ($line =~ /^Package: (.*)$/);
3041             next;
3042         }
3044         # Trigger for version
3045         if ($line =~ /^Version:\s/){
3046             ($version)= ($line =~ /^Version: (.*)$/);
3047             next;
3048         }
3050         # Trigger for description
3051         if ($line =~ /^Description:\s/){
3052             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3053             next;
3054         }
3056         # Trigger for section
3057         if ($line =~ /^Section:\s/){
3058             ($section)= ($line =~ /^Section: (.*)$/);
3059             next;
3060         }
3062         # Trigger for filename
3063         if ($line =~ /^Filename:\s/){
3064             my ($filename) = ($line =~ /^Filename: (.*)$/);
3065             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3066             next;
3067         }
3068     }
3070     close( $PACKAGES );
3071     unlink( "$path.in" );
3075 sub store_fileinfo {
3076     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3078     my %fileinfo = (
3079         'package' => $package,
3080         'dist' => $dist,
3081         'version' => $vers,
3082     );
3084     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3088 sub cleanup_and_extract {
3089         my $fileinfo = $repo_files{ $File::Find::name };
3091         if( defined $fileinfo ) {
3092                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3093                 my $sql;
3094                 my $package = $fileinfo->{ 'package' };
3095                 my $newver = $fileinfo->{ 'version' };
3097                 mkpath($dir);
3098                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3100                 if( -f "$dir/DEBIAN/templates" ) {
3102                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3104                         my $tmpl= ""; {
3105                                 local $/=undef;
3106                                 open FILE, "$dir/DEBIAN/templates";
3107                                 $tmpl = &encode_base64(<FILE>);
3108                                 close FILE;
3109                         }
3110                         rmtree("$dir/DEBIAN/templates");
3112                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3113                         push @packages_list_statements, $sql;
3114                 }
3115         }
3117         return;
3121 sub register_at_foreign_servers {   
3122     my ($kernel) = $_[KERNEL];
3124     # hole alle bekannten server aus known_server_db
3125     my $server_sql = "SELECT * FROM $known_server_tn";
3126     my $server_res = $known_server_db->exec_statement($server_sql);
3128     # no entries in known_server_db
3129     if (not ref(@$server_res[0]) eq "ARRAY") { 
3130         # TODO
3131     }
3133     # detect already connected clients
3134     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3135     my $client_res = $known_clients_db->exec_statement($client_sql);
3137     # send my server details to all other gosa-si-server within the network
3138     foreach my $hit (@$server_res) {
3139         my $hostname = @$hit[0];
3140         my $hostkey = &create_passwd;
3142         # add already connected clients to registration message 
3143         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3144         &add_content2xml_hash($myhash, 'key', $hostkey);
3145         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3147         # add locally loaded gosa-si modules to registration message
3148         my $loaded_modules = {};
3149         while (my ($package, $pck_info) = each %$known_modules) {
3150                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3151                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3152                                                         $loaded_modules->{$act_module} = ""; 
3153                                                 }
3154         }
3156         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3158         # add macaddress to registration message
3159         my ($host_ip, $host_port) = split(/:/, $hostname);
3160         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3161         my $network_interface= &get_interface_for_ip($local_ip);
3162         my $host_mac = &get_mac_for_interface($network_interface);
3163         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3164         
3165         # build registration message and send it
3166         my $foreign_server_msg = &create_xml_string($myhash);
3167         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3168     }
3169     
3170     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3171     return;
3175 #==== MAIN = main ==============================================================
3176 #  parse commandline options
3177 Getopt::Long::Configure( "bundling" );
3178 GetOptions("h|help" => \&usage,
3179         "c|config=s" => \$cfg_file,
3180         "f|foreground" => \$foreground,
3181         "v|verbose+" => \$verbose,
3182         "no-arp+" => \$no_arp,
3183            );
3185 #  read and set config parameters
3186 &check_cmdline_param ;
3187 &read_configfile($cfg_file, %cfg_defaults);
3188 &check_pid;
3190 $SIG{CHLD} = 'IGNORE';
3192 # forward error messages to logfile
3193 if( ! $foreground ) {
3194   open( STDIN,  '+>/dev/null' );
3195   open( STDOUT, '+>&STDIN'    );
3196   open( STDERR, '+>&STDIN'    );
3199 # Just fork, if we are not in foreground mode
3200 if( ! $foreground ) { 
3201     chdir '/'                 or die "Can't chdir to /: $!";
3202     $pid = fork;
3203     setsid                    or die "Can't start a new session: $!";
3204     umask 0;
3205 } else { 
3206     $pid = $$; 
3209 # Do something useful - put our PID into the pid_file
3210 if( 0 != $pid ) {
3211     open( LOCK_FILE, ">$pid_file" );
3212     print LOCK_FILE "$pid\n";
3213     close( LOCK_FILE );
3214     if( !$foreground ) { 
3215         exit( 0 ) 
3216     };
3219 # parse head url and revision from svn
3220 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3221 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3222 $server_headURL = defined $1 ? $1 : 'unknown' ;
3223 $server_revision = defined $2 ? $2 : 'unknown' ;
3224 if ($server_headURL =~ /\/tag\// || 
3225         $server_headURL =~ /\/branches\// ) {
3226     $server_status = "stable"; 
3227 } else {
3228     $server_status = "developmental" ;
3231 # Prepare log file
3232 $root_uid = getpwnam('root');
3233 $adm_gid = getgrnam('adm');
3234 chmod(0640, $log_file);
3235 chown($root_uid, $adm_gid, $log_file);
3236 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3238 daemon_log(" ", 1);
3239 daemon_log("$0 started!", 1);
3240 daemon_log("status: $server_status", 1);
3241 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3244     no strict "refs";
3246     if ($db_module eq "DBmysql") {
3247         # connect to incoming_db
3248         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3250         # connect to gosa-si job queue
3251         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3253         # connect to known_clients_db
3254         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3256         # connect to foreign_clients_db
3257         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3259         # connect to known_server_db
3260         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3262         # connect to login_usr_db
3263         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3265         # connect to fai_server_db 
3266         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3268         # connect to fai_release_db
3269         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3271         # connect to packages_list_db
3272         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3274         # connect to messaging_db
3275         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3277     } elsif ($db_module eq "DBsqlite") {
3278         # connect to incoming_db
3279         unlink($incoming_file_name);
3280         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3281         
3282         # connect to gosa-si job queue
3283         unlink($job_queue_file_name);  ## just for debugging
3284         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3285         chmod(0660, $job_queue_file_name);
3286         chown($root_uid, $adm_gid, $job_queue_file_name);
3287         
3288         # connect to known_clients_db
3289         unlink($known_clients_file_name);   ## just for debugging
3290         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3291         chmod(0660, $known_clients_file_name);
3292         chown($root_uid, $adm_gid, $known_clients_file_name);
3293         
3294         # connect to foreign_clients_db
3295         unlink($foreign_clients_file_name);
3296         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3297         chmod(0660, $foreign_clients_file_name);
3298         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3299         
3300         # connect to known_server_db
3301         unlink($known_server_file_name);
3302         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3303         chmod(0660, $known_server_file_name);
3304         chown($root_uid, $adm_gid, $known_server_file_name);
3305         
3306         # connect to login_usr_db
3307         unlink($login_users_file_name);
3308         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3309         chmod(0660, $login_users_file_name);
3310         chown($root_uid, $adm_gid, $login_users_file_name);
3311         
3312         # connect to fai_server_db
3313         unlink($fai_server_file_name);
3314         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3315         chmod(0660, $fai_server_file_name);
3316         chown($root_uid, $adm_gid, $fai_server_file_name);
3317         
3318         # connect to fai_release_db
3319         unlink($fai_release_file_name);
3320         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3321         chmod(0660, $fai_release_file_name);
3322         chown($root_uid, $adm_gid, $fai_release_file_name);
3323         
3324         # connect to packages_list_db
3325         #unlink($packages_list_file_name);
3326         unlink($packages_list_under_construction);
3327         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3328         chmod(0660, $packages_list_file_name);
3329         chown($root_uid, $adm_gid, $packages_list_file_name);
3330         
3331         # connect to messaging_db
3332         unlink($messaging_file_name);
3333         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3334         chmod(0660, $messaging_file_name);
3335         chown($root_uid, $adm_gid, $messaging_file_name);
3336     }
3339 # Creating tables
3340 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3341 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3342 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3343 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3344 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3345 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3346 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3347 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3348 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3349 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3352 # create xml object used for en/decrypting
3353 $xml = new XML::Simple();
3356 # foreign servers 
3357 my @foreign_server_list;
3359 # add foreign server from cfg file
3360 if ($foreign_server_string ne "") {
3361     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3362     foreach my $foreign_server (@cfg_foreign_server_list) {
3363         push(@foreign_server_list, $foreign_server);
3364     }
3366     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3369 # Perform a DNS lookup for server registration if flag is true
3370 if ($dns_lookup eq "true") {
3371     # Add foreign server from dns
3372     my @tmp_servers;
3373     if (not $server_domain) {
3374         # Try our DNS Searchlist
3375         for my $domain(get_dns_domains()) {
3376             chomp($domain);
3377             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3378             if(@$tmp_domains) {
3379                 for my $tmp_server(@$tmp_domains) {
3380                     push @tmp_servers, $tmp_server;
3381                 }
3382             }
3383         }
3384         if(@tmp_servers && length(@tmp_servers)==0) {
3385             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3386         }
3387     } else {
3388         @tmp_servers = &get_server_addresses($server_domain);
3389         if( 0 == @tmp_servers ) {
3390             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3391         }
3392     }
3394     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3396     foreach my $server (@tmp_servers) { 
3397         unshift(@foreign_server_list, $server); 
3398     }
3399 } else {
3400     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3404 # eliminate duplicate entries
3405 @foreign_server_list = &del_doubles(@foreign_server_list);
3406 my $all_foreign_server = join(", ", @foreign_server_list);
3407 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3409 # add all found foreign servers to known_server
3410 my $act_timestamp = &get_time();
3411 foreach my $foreign_server (@foreign_server_list) {
3413         # do not add myself to known_server_db
3414         if (&is_local($foreign_server)) { next; }
3415         ######################################
3417     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3418             primkey=>['hostname'],
3419             hostname=>$foreign_server,
3420             macaddress=>"",
3421             status=>'not_jet_registered',
3422             hostkey=>"none",
3423             loaded_modules => "none", 
3424             timestamp=>$act_timestamp,
3425             } );
3429 # Import all modules
3430 &import_modules;
3432 # Check wether all modules are gosa-si valid passwd check
3433 &password_check;
3435 # Prepare for using Opsi 
3436 if ($opsi_enabled eq "true") {
3437     use JSON::RPC::Client;
3438     use XML::Quote qw(:all);
3439     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3440     $opsi_client = new JSON::RPC::Client;
3444 POE::Component::Server::TCP->new(
3445         Alias => "TCP_SERVER",
3446         Port => $server_port,
3447         ClientInput => sub {
3448                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3449         my $session_id = $session->ID;
3450         my $remote_ip = $heap->{'remote_ip'};
3451                 push(@msgs_to_decrypt, $input);
3452         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3453                 $kernel->yield("msg_to_decrypt");
3454         },
3455         InlineStates => {
3456                 msg_to_decrypt => \&msg_to_decrypt,
3457                 next_task => \&next_task,
3458                 task_result => \&handle_task_result,
3459                 task_done   => \&handle_task_done,
3460                 task_debug  => \&handle_task_debug,
3461                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3462         }
3463 );
3465 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3467 # create session for repeatedly checking the job queue for jobs
3468 POE::Session->create(
3469         inline_states => {
3470                 _start => \&session_start,
3471         register_at_foreign_servers => \&register_at_foreign_servers,
3472         sig_handler => \&sig_handler,
3473         next_task => \&next_task,
3474         task_result => \&handle_task_result,
3475         task_done   => \&handle_task_done,
3476         task_debug  => \&handle_task_debug,
3477         watch_for_next_tasks => \&watch_for_next_tasks,
3478         watch_for_new_messages => \&watch_for_new_messages,
3479         watch_for_delivery_messages => \&watch_for_delivery_messages,
3480         watch_for_done_messages => \&watch_for_done_messages,
3481                 watch_for_new_jobs => \&watch_for_new_jobs,
3482         watch_for_modified_jobs => \&watch_for_modified_jobs,
3483         watch_for_done_jobs => \&watch_for_done_jobs,
3484         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3485         watch_for_old_known_clients => \&watch_for_old_known_clients,
3486         create_packages_list_db => \&run_create_packages_list_db,
3487         create_fai_server_db => \&run_create_fai_server_db,
3488         create_fai_release_db => \&run_create_fai_release_db,
3489                 recreate_packages_db => \&run_recreate_packages_db,
3490         session_run_result => \&session_run_result,
3491         session_run_debug => \&session_run_debug,
3492         session_run_done => \&session_run_done,
3493         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3494         }
3495 );
3498 POE::Kernel->run();
3499 exit;