Code

Added Translation String.
[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",
131         "timestamp VARCHAR(14) DEFAULT 'none'", 
132         "headertag VARCHAR(255) DEFAULT 'none'",
133         "targettag VARCHAR(255) DEFAULT 'none'",
134         "xmlmessage TEXT",
135         "module VARCHAR(255) DEFAULT 'none'",
136         "sessionid VARCHAR(255) DEFAULT '0'",
137 );
139 # holds all gosa jobs
140 our $job_db;
141 our $job_queue_tn = 'jobs';
142 my $job_queue_file_name;
143 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
144         "timestamp VARCHAR(14) DEFAULT 'none'", 
145         "status VARCHAR(255) DEFAULT 'none'", 
146         "result TEXT",
147         "progress VARCHAR(255) DEFAULT 'none'",
148         "headertag VARCHAR(255) DEFAULT 'none'",
149         "targettag VARCHAR(255) DEFAULT 'none'", 
150         "xmlmessage TEXT", 
151         "macaddress VARCHAR(17) DEFAULT 'none'",
152         "plainname VARCHAR(255) DEFAULT 'none'",
153         "siserver VARCHAR(255) DEFAULT 'none'",
154         "modified INTEGER DEFAULT '0'",
155 );
157 # holds all other gosa-si-server
158 our $known_server_db;
159 our $known_server_tn = "known_server";
160 my $known_server_file_name;
161 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
163 # holds all registrated clients
164 our $known_clients_db;
165 our $known_clients_tn = "known_clients";
166 my $known_clients_file_name;
167 my @known_clients_col_names = ("hostname VARCHAR(255)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "timestamp VARCHAR(14)", "macaddress VARCHAR(17)", "events TEXT", "keylifetime VARCHAR(255)");
169 # holds all registered clients at a foreign server
170 our $foreign_clients_db;
171 our $foreign_clients_tn = "foreign_clients"; 
172 my $foreign_clients_file_name;
173 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
175 # holds all logged in user at each client 
176 our $login_users_db;
177 our $login_users_tn = "login_users";
178 my $login_users_file_name;
179 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
181 # holds all fai server, the debian release and tag
182 our $fai_server_db;
183 our $fai_server_tn = "fai_server"; 
184 my $fai_server_file_name;
185 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
187 our $fai_release_db;
188 our $fai_release_tn = "fai_release"; 
189 my $fai_release_file_name;
190 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
192 # holds all packages available from different repositories
193 our $packages_list_db;
194 our $packages_list_tn = "packages_list";
195 my $packages_list_file_name;
196 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
197 my $outdir = "/tmp/packages_list_db";
198 my $arch = "i386"; 
200 # holds all messages which should be delivered to a user
201 our $messaging_db;
202 our $messaging_tn = "messaging"; 
203 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
204         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
205 my $messaging_file_name;
207 # path to directory to store client install log files
208 our $client_fai_log_dir = "/var/log/fai"; 
210 # queue which stores taskes until one of the $max_children children are ready to process the task
211 #my @tasks = qw();
212 my @msgs_to_decrypt = qw();
213 my $max_children = 2;
216 # loop delay for job queue to look for opsi jobs
217 my $job_queue_opsi_delay = 10;
218 our $opsi_client;
219 our $opsi_url;
220  
221 # Lifetime of logged in user information. If no update information comes after n seconds, 
222 # the user is expeceted to be no longer logged in or the host is no longer running. Because
223 # of this, the user is deleted from login_users_db
224 our $logged_in_user_date_of_expiry = 600;
227 %cfg_defaults = (
228 "general" => {
229     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
230     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
231     },
232 "server" => {
233     "ip"                    => [\$server_ip, "0.0.0.0"],
234     "port"                  => [\$server_port, "20081"],
235     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
236     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
237     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
238     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
239     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
240     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
241     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
242     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
243     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
244     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
245     "repo-path"             => [\$repo_path, '/srv/www/repository'],
246     "ldap-uri"              => [\$ldap_uri, ""],
247     "ldap-base"             => [\$ldap_base, ""],
248     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
249     "ldap-admin-password"   => [\$ldap_admin_password, ""],
250     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
251     "max-clients"           => [\$max_clients, 10],
252     "wol-password"          => [\$wake_on_lan_passwd, ""],
253                 "mysql-username"        => [\$mysql_username, "gosa_si"],
254                 "mysql-password"        => [\$mysql_password, ""],
255                 "mysql-database"        => [\$mysql_database, "gosa_si"],
256                 "mysql-host"            => [\$mysql_host, "127.0.0.1"],
257     },
258 "GOsaPackages" => {
259     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
260     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
261     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
262     "key" => [\$GosaPackages_key, "none"],
263                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
264     },
265 "ClientPackages" => {
266     "key" => [\$ClientPackages_key, "none"],
267     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
268     },
269 "ServerPackages"=> {
270     "address"      => [\$foreign_server_string, ""],
271     "dns-lookup"            => [\$dns_lookup, "true"],
272     "domain"  => [\$server_domain, ""],
273     "key"     => [\$ServerPackages_key, "none"],
274     "key-lifetime" => [\$foreign_servers_register_delay, 120],
275     "job-synchronization-enabled" => [\$job_synchronization, "true"],
276     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
277     },
278 "ArpHandler" => {
279     "enabled"   => [\$arp_enabled, "true"],
280     "interface" => [\$arp_interface, "all"],
281         },
282 "Opsi" => {
283     "enabled"  => [\$opsi_enabled, "false"], 
284     "server"   => [\$opsi_server, "localhost"],
285     "admin"    => [\$opsi_admin, "opsi-admin"],
286     "password" => [\$opsi_password, "secret"],
287    },
289 );
292 #===  FUNCTION  ================================================================
293 #         NAME:  usage
294 #   PARAMETERS:  nothing
295 #      RETURNS:  nothing
296 #  DESCRIPTION:  print out usage text to STDERR
297 #===============================================================================
298 sub usage {
299     print STDERR << "EOF" ;
300 usage: $prg [-hvf] [-c config]
302            -h        : this (help) message
303            -c <file> : config file
304            -f        : foreground, process will not be forked to background
305            -v        : be verbose (multiple to increase verbosity)
306            -no-arp   : starts $prg without connection to arp module
307  
308 EOF
309     print "\n" ;
313 #===  FUNCTION  ================================================================
314 #         NAME:  logging
315 #   PARAMETERS:  level - string - default 'info'
316 #                msg - string -
317 #                facility - string - default 'LOG_DAEMON'
318 #      RETURNS:  nothing
319 #  DESCRIPTION:  function for logging
320 #===============================================================================
321 sub daemon_log {
322     # log into log_file
323     my( $msg, $level ) = @_;
324     if(not defined $msg) { return }
325     if(not defined $level) { $level = 1 }
326     if(defined $log_file){
327         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
328         if(not $open_log_fh) {
329             print STDERR "cannot open $log_file: $!";
330             return;
331         }
332         # check owner and group of log_file and update settings if necessary
333         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
334         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
335             chown($root_uid, $adm_gid, $log_file);
336         }
338         chomp($msg);
339         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
340         if($level <= $verbose){
341             my ($seconds, $minutes, $hours, $monthday, $month,
342                     $year, $weekday, $yearday, $sommertime) = localtime(time);
343             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
344             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
345             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
346             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
347             $month = $monthnames[$month];
348             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
349             $year+=1900;
350             my $name = $prg;
352             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
353             print LOG_HANDLE $log_msg;
354             if( $foreground ) { 
355                 print STDERR $log_msg;
356             }
357         }
358         close( LOG_HANDLE );
359     }
363 #===  FUNCTION  ================================================================
364 #         NAME:  check_cmdline_param
365 #   PARAMETERS:  nothing
366 #      RETURNS:  nothing
367 #  DESCRIPTION:  validates commandline parameter
368 #===============================================================================
369 sub check_cmdline_param () {
370     my $err_config;
371     my $err_counter = 0;
372         if(not defined($cfg_file)) {
373                 $cfg_file = "/etc/gosa-si/server.conf";
374                 if(! -r $cfg_file) {
375                         $err_config = "please specify a config file";
376                         $err_counter += 1;
377                 }
378     }
379     if( $err_counter > 0 ) {
380         &usage( "", 1 );
381         if( defined( $err_config)) { print STDERR "$err_config\n"}
382         print STDERR "\n";
383         exit( -1 );
384     }
388 #===  FUNCTION  ================================================================
389 #         NAME:  check_pid
390 #   PARAMETERS:  nothing
391 #      RETURNS:  nothing
392 #  DESCRIPTION:  handels pid processing
393 #===============================================================================
394 sub check_pid {
395     $pid = -1;
396     # Check, if we are already running
397     if( open(LOCK_FILE, "<$pid_file") ) {
398         $pid = <LOCK_FILE>;
399         if( defined $pid ) {
400             chomp( $pid );
401             if( -f "/proc/$pid/stat" ) {
402                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
403                 if( $stat ) {
404                                         daemon_log("ERROR: Already running",1);
405                     close( LOCK_FILE );
406                     exit -1;
407                 }
408             }
409         }
410         close( LOCK_FILE );
411         unlink( $pid_file );
412     }
414     # create a syslog msg if it is not to possible to open PID file
415     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
416         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
417         if (open(LOCK_FILE, '<', $pid_file)
418                 && ($pid = <LOCK_FILE>))
419         {
420             chomp($pid);
421             $msg .= "(PID $pid)\n";
422         } else {
423             $msg .= "(unable to read PID)\n";
424         }
425         if( ! ($foreground) ) {
426             openlog( $0, "cons,pid", "daemon" );
427             syslog( "warning", $msg );
428             closelog();
429         }
430         else {
431             print( STDERR " $msg " );
432         }
433         exit( -1 );
434     }
437 #===  FUNCTION  ================================================================
438 #         NAME:  import_modules
439 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
440 #                are stored
441 #      RETURNS:  nothing
442 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
443 #                state is on is imported by "require 'file';"
444 #===============================================================================
445 sub import_modules {
446     daemon_log(" ", 1);
448     if (not -e $modules_path) {
449         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
450     }
452     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
453     while (defined (my $file = readdir (DIR))) {
454         if (not $file =~ /(\S*?).pm$/) {
455             next;
456         }
457                 my $mod_name = $1;
459         # ArpHandler switch
460         if( $file =~ /ArpHandler.pm/ ) {
461             if( $arp_enabled eq "false" ) { next; }
462         }
463         
464         eval { require $file; };
465         if ($@) {
466             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
467             daemon_log("$@", 1);
468             exit;
469                 } else {
470                         my $info = eval($mod_name.'::get_module_info()');
471                         # Only load module if get_module_info() returns a non-null object
472                         if( $info ) {
473                                 my ($input_address, $input_key, $event_hash) = @{$info};
474                                 $known_modules->{$mod_name} = $info;
475                                 daemon_log("0 INFO: module $mod_name loaded", 5);
476                         }
477                 }
478     }   
480     close (DIR);
483 #===  FUNCTION  ================================================================
484 #         NAME:  password_check
485 #   PARAMETERS:  nothing
486 #      RETURNS:  nothing
487 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
488 #                the same password
489 #===============================================================================
490 sub password_check {
491     my $passwd_hash = {};
492     while (my ($mod_name, $mod_info) = each %$known_modules) {
493         my $mod_passwd = @$mod_info[1];
494         if (not defined $mod_passwd) { next; }
495         if (not exists $passwd_hash->{$mod_passwd}) {
496             $passwd_hash->{$mod_passwd} = $mod_name;
498         # escalates critical error
499         } else {
500             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
501             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
502             exit( -1 );
503         }
504     }
509 #===  FUNCTION  ================================================================
510 #         NAME:  sig_int_handler
511 #   PARAMETERS:  signal - string - signal arose from system
512 #      RETURNS:  nothing
513 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
514 #===============================================================================
515 sub sig_int_handler {
516     my ($signal) = @_;
518 #       if (defined($ldap_handle)) {
519 #               $ldap_handle->disconnect;
520 #       }
521     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
522     
524     daemon_log("shutting down gosa-si-server", 1);
525     system("kill `ps -C gosa-si-server -o pid=`");
527 $SIG{INT} = \&sig_int_handler;
530 sub check_key_and_xml_validity {
531     my ($crypted_msg, $module_key, $session_id) = @_;
532     my $msg;
533     my $msg_hash;
534     my $error_string;
535     eval{
536         $msg = &decrypt_msg($crypted_msg, $module_key);
538         if ($msg =~ /<xml>/i){
539             $msg =~ s/\s+/ /g;  # just for better daemon_log
540             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
541             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
543             ##############
544             # check header
545             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
546             my $header_l = $msg_hash->{'header'};
547             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
548             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
549             my $header = @{$header_l}[0];
550             if( 0 == length $header) { die 'empty string in header tag'; }
552             ##############
553             # check source
554             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
555             my $source_l = $msg_hash->{'source'};
556             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
557             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
558             my $source = @{$source_l}[0];
559             if( 0 == length $source) { die 'source error'; }
561             ##############
562             # check target
563             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
564             my $target_l = $msg_hash->{'target'};
565             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
566         }
567     };
568     if($@) {
569         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
570         $msg = undef;
571         $msg_hash = undef;
572     }
574     return ($msg, $msg_hash);
578 sub check_outgoing_xml_validity {
579     my ($msg, $session_id) = @_;
581     my $msg_hash;
582     eval{
583         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
585         ##############
586         # check header
587         my $header_l = $msg_hash->{'header'};
588         if( 1 != @{$header_l} ) {
589             die 'no or more than one headers specified';
590         }
591         my $header = @{$header_l}[0];
592         if( 0 == length $header) {
593             die 'header has length 0';
594         }
596         ##############
597         # check source
598         my $source_l = $msg_hash->{'source'};
599         if( 1 != @{$source_l} ) {
600             die 'no or more than 1 sources specified';
601         }
602         my $source = @{$source_l}[0];
603         if( 0 == length $source) {
604             die 'source has length 0';
605         }
607                                 # Check if source contains hostname instead of ip address
608                                 if(not $source =~ /^[a-z0-9\.]+:\d+$/i) {
609                                                 my ($hostname,$port) = split(/:/, $source);
610                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
611                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
612                                                         # Write ip address to $source variable
613                                                         $source = "$ip_address:$port";
614                                                 }
615                                 }
616         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
617                 $source =~ /^GOSA$/i) {
618             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
619         }
620         
621         ##############
622         # check target  
623         my $target_l = $msg_hash->{'target'};
624         if( 0 == @{$target_l} ) {
625             die "no targets specified";
626         }
627         foreach my $target (@$target_l) {
628             if( 0 == length $target) {
629                 die "target has length 0";
630             }
631             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
632                     $target =~ /^GOSA$/i ||
633                     $target =~ /^\*$/ ||
634                     $target =~ /KNOWN_SERVER/i ||
635                     $target =~ /JOBDB/i ||
636                     $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 ){
637                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
638             }
639         }
640     };
641     if($@) {
642         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
643         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
644         $msg_hash = undef;
645     }
647     return ($msg_hash);
651 sub input_from_known_server {
652     my ($input, $remote_ip, $session_id) = @_ ;  
653     my ($msg, $msg_hash, $module);
655     my $sql_statement= "SELECT * FROM known_server";
656     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
658     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
659         my $host_name = $hit->{hostname};
660         if( not $host_name =~ "^$remote_ip") {
661             next;
662         }
663         my $host_key = $hit->{hostkey};
664         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
665         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
667         # check if module can open msg envelope with module key
668         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
669         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
670             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
671             daemon_log("$@", 8);
672             next;
673         }
674         else {
675             $msg = $tmp_msg;
676             $msg_hash = $tmp_msg_hash;
677             $module = "ServerPackages";
678             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
679             last;
680         }
681     }
683     if( (!$msg) || (!$msg_hash) || (!$module) ) {
684         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
685     }
686   
687     return ($msg, $msg_hash, $module);
691 sub input_from_known_client {
692     my ($input, $remote_ip, $session_id) = @_ ;  
693     my ($msg, $msg_hash, $module);
695     my $sql_statement= "SELECT * FROM known_clients";
696     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
697     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
698         my $host_name = $hit->{hostname};
699         if( not $host_name =~ /^$remote_ip:\d*$/) {
700                 next;
701                 }
702         my $host_key = $hit->{hostkey};
703         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
704         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
706         # check if module can open msg envelope with module key
707         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
709         if( (!$msg) || (!$msg_hash) ) {
710             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
711             &daemon_log("$@", 8);
712             next;
713         }
714         else {
715             $module = "ClientPackages";
716             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
717             last;
718         }
719     }
721     if( (!$msg) || (!$msg_hash) || (!$module) ) {
722         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
723     }
725     return ($msg, $msg_hash, $module);
729 sub input_from_unknown_host {
730         no strict "refs";
731         my ($input, $session_id) = @_ ;
732         my ($msg, $msg_hash, $module);
733         my $error_string;
735         my %act_modules = %$known_modules;
737         while( my ($mod, $info) = each(%act_modules)) {
739                 # check a key exists for this module
740                 my $module_key = ${$mod."_key"};
741                 if( not defined $module_key ) {
742                         if( $mod eq 'ArpHandler' ) {
743                                 next;
744                         }
745                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
746                         next;
747                 }
748                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
750                 # check if module can open msg envelope with module key
751                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
752                 if( (not defined $msg) || (not defined $msg_hash) ) {
753                         next;
754                 } else {
755                         $module = $mod;
756             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
757                         last;
758                 }
759         }
761         if( (!$msg) || (!$msg_hash) || (!$module)) {
762                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
763         }
765         return ($msg, $msg_hash, $module);
769 sub create_ciphering {
770     my ($passwd) = @_;
771         if((!defined($passwd)) || length($passwd)==0) {
772                 $passwd = "";
773         }
774     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
775     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
776     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
777     $my_cipher->set_iv($iv);
778     return $my_cipher;
782 sub encrypt_msg {
783     my ($msg, $key) = @_;
784     my $my_cipher = &create_ciphering($key);
785     my $len;
786     {
787             use bytes;
788             $len= 16-length($msg)%16;
789     }
790     $msg = "\0"x($len).$msg;
791     $msg = $my_cipher->encrypt($msg);
792     chomp($msg = &encode_base64($msg));
793     # there are no newlines allowed inside msg
794     $msg=~ s/\n//g;
795     return $msg;
799 sub decrypt_msg {
801     my ($msg, $key) = @_ ;
802     $msg = &decode_base64($msg);
803     my $my_cipher = &create_ciphering($key);
804     $msg = $my_cipher->decrypt($msg); 
805     $msg =~ s/\0*//g;
806     return $msg;
810 sub get_encrypt_key {
811     my ($target) = @_ ;
812     my $encrypt_key;
813     my $error = 0;
815     # target can be in known_server
816     if( not defined $encrypt_key ) {
817         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
818         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
819         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
820             my $host_name = $hit->{hostname};
821             if( $host_name ne $target ) {
822                 next;
823             }
824             $encrypt_key = $hit->{hostkey};
825             last;
826         }
827     }
829     # target can be in known_client
830     if( not defined $encrypt_key ) {
831         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
832         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
833         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
834             my $host_name = $hit->{hostname};
835             if( $host_name ne $target ) {
836                 next;
837             }
838             $encrypt_key = $hit->{hostkey};
839             last;
840         }
841     }
843     return $encrypt_key;
847 #===  FUNCTION  ================================================================
848 #         NAME:  open_socket
849 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
850 #                [PeerPort] string necessary if port not appended by PeerAddr
851 #      RETURNS:  socket IO::Socket::INET
852 #  DESCRIPTION:  open a socket to PeerAddr
853 #===============================================================================
854 sub open_socket {
855     my ($PeerAddr, $PeerPort) = @_ ;
856     if(defined($PeerPort)){
857         $PeerAddr = $PeerAddr.":".$PeerPort;
858     }
859     my $socket;
860     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
861             Porto => "tcp",
862             Type => SOCK_STREAM,
863             Timeout => 5,
864             );
865     if(not defined $socket) {
866         return;
867     }
868 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
869     return $socket;
873 sub send_msg_to_target {
874     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
875     my $error = 0;
876     my $header;
877     my $timestamp = &get_time();
878     my $new_status;
879     my $act_status;
880     my ($sql_statement, $res);
881   
882     if( $msg_header ) {
883         $header = "'$msg_header'-";
884     } else {
885         $header = "";
886     }
888         # Patch the source ip
889         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
890                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
891                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
892         }
894     # encrypt xml msg
895     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
897     # opensocket
898     my $socket = &open_socket($address);
899     if( !$socket ) {
900         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
901         $error++;
902     }
903     
904     if( $error == 0 ) {
905         # send xml msg
906         print $socket $crypted_msg."\n";
908         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
909         daemon_log("$session_id DEBUG: message:\n$msg", 9);
910         
911     }
913     # close socket in any case
914     if( $socket ) {
915         close $socket;
916     }
918     if( $error > 0 ) { $new_status = "down"; }
919     else { $new_status = $msg_header; }
922     # known_clients
923     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
924     $res = $known_clients_db->select_dbentry($sql_statement);
925     if( keys(%$res) == 1) {
926         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
927         if ($act_status eq "down" && $new_status eq "down") {
928             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
929             $res = $known_clients_db->del_dbentry($sql_statement);
930             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
931         } else { 
932             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
933             $res = $known_clients_db->update_dbentry($sql_statement);
934             if($new_status eq "down"){
935                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
936             } else {
937                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
938             }
939         }
940     }
942     # known_server
943     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
944     $res = $known_server_db->select_dbentry($sql_statement);
945     if( keys(%$res) == 1) {
946         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
947         if ($act_status eq "down" && $new_status eq "down") {
948             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
949             $res = $known_server_db->del_dbentry($sql_statement);
950             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
951         } 
952         else { 
953             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
954             $res = $known_server_db->update_dbentry($sql_statement);
955             if($new_status eq "down"){
956                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
957             } else {
958                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
959             }
960         }
961     }
962     return $error; 
966 sub update_jobdb_status_for_send_msgs {
967     my ($session_id, $answer, $error) = @_;
968     &daemon_log("$session_id DEBUG: try to update job status", 7); 
969     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
970         my $jobdb_id = $1;
971     
972         $answer =~ /<header>(.*)<\/header>/;
973         my $job_header = $1;
975         $answer =~ /<target>(.*)<\/target>/;
976         my $job_target = $1;
977             
978         # Sending msg failed
979         if( $error ) {
981             # Set jobs to done, jobs do not need to deliver their message in any case
982             if (($job_header eq "trigger_action_localboot")
983                     ||($job_header eq "trigger_action_lock")
984                     ||($job_header eq "trigger_action_halt") 
985                     ) {
986                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
987                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
988                 my $res = $job_db->update_dbentry($sql_statement);
989                 
990             # Reactivate jobs, jobs need to deliver their message
991             } elsif (($job_header eq "trigger_action_activate")
992                     ||($job_header eq "trigger_action_update")
993                     ||($job_header eq "trigger_action_reinstall") 
994                     ||($job_header eq "trigger_activate_new")
995                     ) {
996                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
998             # For all other messages
999             } else {
1000                 my $sql_statement = "UPDATE $job_queue_tn ".
1001                     "SET status='error', result='can not deliver msg, please consult log file' ".
1002                     "WHERE id=$jobdb_id";
1003                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1004                 my $res = $job_db->update_dbentry($sql_statement);
1005             }
1007         # Sending msg was successful
1008         } else {
1009             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1010             # jobs reinstall, update, inst_update do themself setting to done
1011             if (($job_header eq "trigger_action_localboot")
1012                     ||($job_header eq "trigger_action_lock")
1013                     ||($job_header eq "trigger_action_activate")
1014                     ||($job_header eq "trigger_action_halt") 
1015                     ||($job_header eq "trigger_action_reboot")
1016                     ||($job_header eq "trigger_action_wake")
1017                     ||($job_header eq "trigger_wake")
1018                     ) {
1020                 my $sql_statement = "UPDATE $job_queue_tn ".
1021                     "SET status='done' ".
1022                     "WHERE id=$jobdb_id AND status='processed'";
1023                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1024                 my $res = $job_db->update_dbentry($sql_statement);
1025             } else { 
1026                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1027             } 
1028         } 
1029     } else { 
1030         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1031     }
1034 sub reactivate_job_with_delay {
1035     my ($session_id, $target, $header, $delay) = @_ ;
1036     # 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
1037     
1038     if (not defined $delay) { $delay = 30 } ;
1039     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1041     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1042     my $res = $job_db->update_dbentry($sql);
1043     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1044             "cause client '$target' is currently not available", 5);
1045     daemon_log("$session_id $sql", 7);                             
1046     return;
1050 sub sig_handler {
1051         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1052         daemon_log("0 INFO got signal '$signal'", 1); 
1053         $kernel->sig_handled();
1054         return;
1058 sub msg_to_decrypt {
1059         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1060         my $session_id = $session->ID;
1061         my ($msg, $msg_hash, $module);
1062         my $error = 0;
1064         # fetch new msg out of @msgs_to_decrypt
1065         my $tmp_next_msg = shift @msgs_to_decrypt;
1066     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1068         # msg is from a new client or gosa
1069         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1071         # msg is from a gosa-si-server
1072         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1073                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1074         }
1075         # msg is from a gosa-si-client
1076         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1077                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1078         }
1079         # an error occurred
1080         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1081                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1082                 # could not understand a msg from its server the client cause a re-registering process
1083         my $remote_ip = $heap->{'remote_ip'};
1084         my $remote_port = $heap->{'remote_port'};
1085         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1086         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1088                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1089                         "' to cause a re-registering of the client if necessary", 3);
1090                 $error++;
1091         }
1094         my $header;
1095         my $target;
1096         my $source;
1097         my $done = 0;
1098         my $sql;
1099         my $res;
1101         # check whether this message should be processed here
1102         if ($error == 0) {
1103                 $header = @{$msg_hash->{'header'}}[0];
1104                 $target = @{$msg_hash->{'target'}}[0];
1105                 $source = @{$msg_hash->{'source'}}[0];
1106                 my $not_found_in_known_clients_db = 0;
1107                 my $not_found_in_known_server_db = 0;
1108                 my $not_found_in_foreign_clients_db = 0;
1109                 my $local_address;
1110                 my $local_mac;
1111                 my ($target_ip, $target_port) = split(':', $target);
1113                 # Determine the local ip address if target is an ip address
1114                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1115                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1116                 } else {
1117                         $local_address = $server_address;
1118                 }
1120                 # Determine the local mac address if target is a mac address
1121                 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) {
1122                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1123                         my $network_interface= &get_interface_for_ip($loc_ip);
1124                         $local_mac = &get_mac_for_interface($network_interface);
1125                 } else {
1126                         $local_mac = $server_mac_address;
1127                 }
1129                 # target and source is equal to GOSA -> process here
1130                 if (not $done) {
1131                         if ($target eq "GOSA" && $source eq "GOSA") {
1132                                 $done = 1;                    
1133                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1134                         }
1135                 }
1137                 # target is own address without forward_to_gosa-tag -> process here
1138                 if (not $done) {
1139                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1140                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1141                                 $done = 1;
1142                                 if ($source eq "GOSA") {
1143                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1144                                 }
1145                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1146                         }
1147                 }
1149                 # target is a client address in known_clients -> process here
1150                 if (not $done) {
1151                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1152                         $res = $known_clients_db->select_dbentry($sql);
1153                         if (keys(%$res) > 0) {
1154                                 $done = 1; 
1155                                 my $hostname = $res->{1}->{'hostname'};
1156                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1157                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1158                                 if ($source eq "GOSA") {
1159                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1160                                 }
1161                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1163                         } else {
1164                                 $not_found_in_known_clients_db = 1;
1165                         }
1166                 }
1168                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1169                 if (not $done) {
1170                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1171                         my $gosa_at;
1172                         my $gosa_session_id;
1173                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1174                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1175                                 if ($gosa_at ne $local_address) {
1176                                         $done = 1;
1177                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1178                                 }
1179                         }
1180                 }
1182                 # if message should be processed here -> add message to incoming_db
1183                 if ($done) {
1184                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1185                         # so gosa-si-server knows how to process this kind of messages
1186                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1187                                 $module = "GosaPackages";
1188                         }
1190                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1191                                         primkey=>[],
1192                                         headertag=>$header,
1193                                         targettag=>$target,
1194                                         xmlmessage=>&encode_base64($msg),
1195                                         timestamp=>&get_time,
1196                                         module=>$module,
1197                                         sessionid=>$session_id,
1198                                 } );
1200                 }
1202                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1203                 if (not $done) {
1204                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1205                         my $gosa_at;
1206                         my $gosa_session_id;
1207                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1208                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1209                                 if ($gosa_at eq $local_address) {
1210                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1211                                         if( defined $session_reference ) {
1212                                                 $heap = $session_reference->get_heap();
1213                                         }
1214                                         if(exists $heap->{'client'}) {
1215                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1216                                                 $heap->{'client'}->put($msg);
1217                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1218                                         }
1219                                         $done = 1;
1220                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1221                                 }
1222                         }
1224                 }
1226                 # target is a client address in foreign_clients -> forward to registration server
1227                 if (not $done) {
1228                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1229                         $res = $foreign_clients_db->select_dbentry($sql);
1230                         if (keys(%$res) > 0) {
1231                                 my $hostname = $res->{1}->{'hostname'};
1232                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1233                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1234                                 my $regserver = $res->{1}->{'regserver'};
1235                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1236                                 my $res = $known_server_db->select_dbentry($sql);
1237                                 if (keys(%$res) > 0) {
1238                                         my $regserver_key = $res->{1}->{'hostkey'};
1239                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1240                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1241                                         if ($source eq "GOSA") {
1242                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1243                                         }
1244                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1245                                 }
1246                                 $done = 1;
1247                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1248                         } else {
1249                                 $not_found_in_foreign_clients_db = 1;
1250                         }
1251                 }
1253                 # target is a server address -> forward to server
1254                 if (not $done) {
1255                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1256                         $res = $known_server_db->select_dbentry($sql);
1257                         if (keys(%$res) > 0) {
1258                                 my $hostkey = $res->{1}->{'hostkey'};
1260                                 if ($source eq "GOSA") {
1261                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1262                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1264                                 }
1266                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1267                                 $done = 1;
1268                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1269                         } else {
1270                                 $not_found_in_known_server_db = 1;
1271                         }
1272                 }
1275                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1276                 if ( $not_found_in_foreign_clients_db 
1277                         && $not_found_in_known_server_db
1278                         && $not_found_in_known_clients_db) {
1279                         &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);
1280             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1281                 $module = "GosaPackages"; 
1282             }
1283                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1284                                         primkey=>[],
1285                                         headertag=>$header,
1286                                         targettag=>$target,
1287                                         xmlmessage=>&encode_base64($msg),
1288                                         timestamp=>&get_time,
1289                                         module=>$module,
1290                                         sessionid=>$session_id,
1291                                 } );
1292                         $done = 1;
1293                 }
1296                 if (not $done) {
1297                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1298                         if ($source eq "GOSA") {
1299                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1300                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1302                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1303                                 if( defined $session_reference ) {
1304                                         $heap = $session_reference->get_heap();
1305                                 }
1306                                 if(exists $heap->{'client'}) {
1307                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1308                                         $heap->{'client'}->put($error_msg);
1309                                 }
1310                         }
1311                 }
1313         }
1315         return;
1319 sub next_task {
1320     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1321     my $running_task = POE::Wheel::Run->new(
1322             Program => sub { process_task($session, $heap, $task) },
1323             StdioFilter => POE::Filter::Reference->new(),
1324             StdoutEvent  => "task_result",
1325             StderrEvent  => "task_debug",
1326             CloseEvent   => "task_done",
1327             );
1328     $heap->{task}->{ $running_task->ID } = $running_task;
1331 sub handle_task_result {
1332     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1333     my $client_answer = $result->{'answer'};
1334     if( $client_answer =~ s/session_id=(\d+)$// ) {
1335         my $session_id = $1;
1336         if( defined $session_id ) {
1337             my $session_reference = $kernel->ID_id_to_session($session_id);
1338             if( defined $session_reference ) {
1339                 $heap = $session_reference->get_heap();
1340             }
1341         }
1343         if(exists $heap->{'client'}) {
1344             $heap->{'client'}->put($client_answer);
1345         }
1346     }
1347     $kernel->sig(CHLD => "child_reap");
1350 sub handle_task_debug {
1351     my $result = $_[ARG0];
1352     print STDERR "$result\n";
1355 sub handle_task_done {
1356     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1357     delete $heap->{task}->{$task_id};
1360 sub process_task {
1361     no strict "refs";
1362     #CHECK: Not @_[...]?
1363     my ($session, $heap, $task) = @_;
1364     my $error = 0;
1365     my $answer_l;
1366     my ($answer_header, @answer_target_l, $answer_source);
1367     my $client_answer = "";
1369     # prepare all variables needed to process message
1370     #my $msg = $task->{'xmlmessage'};
1371     my $msg = &decode_base64($task->{'xmlmessage'});
1372     my $incoming_id = $task->{'id'};
1373     my $module = $task->{'module'};
1374     my $header =  $task->{'headertag'};
1375     my $session_id = $task->{'sessionid'};
1376                 my $msg_hash;
1377                 eval {
1378         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1379                 }; 
1380                 daemon_log("ERROR: XML failure '$@'") if ($@);
1381     my $source = @{$msg_hash->{'source'}}[0];
1382     
1383     # set timestamp of incoming client uptodate, so client will not 
1384     # be deleted from known_clients because of expiration
1385     my $cur_time = &get_time();
1386     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1387     my $res = $known_clients_db->exec_statement($sql);
1389     ######################
1390     # process incoming msg
1391     if( $error == 0) {
1392         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1393         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1394         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1396         if ( 0 < @{$answer_l} ) {
1397             my $answer_str = join("\n", @{$answer_l});
1398             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1399                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1400             }
1401             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1402         } else {
1403             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1404         }
1406     }
1407     if( !$answer_l ) { $error++ };
1409     ########
1410     # answer
1411     if( $error == 0 ) {
1413         foreach my $answer ( @{$answer_l} ) {
1414             # check outgoing msg to xml validity
1415             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1416             if( not defined $answer_hash ) { next; }
1417             
1418             $answer_header = @{$answer_hash->{'header'}}[0];
1419             @answer_target_l = @{$answer_hash->{'target'}};
1420             $answer_source = @{$answer_hash->{'source'}}[0];
1422             # deliver msg to all targets 
1423             foreach my $answer_target ( @answer_target_l ) {
1425                 # targets of msg are all gosa-si-clients in known_clients_db
1426                 if( $answer_target eq "*" ) {
1427                     # answer is for all clients
1428                     my $sql_statement= "SELECT * FROM known_clients";
1429                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1430                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1431                         my $host_name = $hit->{hostname};
1432                         my $host_key = $hit->{hostkey};
1433                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1434                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1435                     }
1436                 }
1438                 # targets of msg are all gosa-si-server in known_server_db
1439                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1440                     # answer is for all server in known_server
1441                     my $sql_statement= "SELECT * FROM $known_server_tn";
1442                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1443                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1444                         my $host_name = $hit->{hostname};
1445                         my $host_key = $hit->{hostkey};
1446                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1447                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1448                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1449                     }
1450                 }
1452                 # target of msg is GOsa
1453                                 elsif( $answer_target eq "GOSA" ) {
1454                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1455                                         my $add_on = "";
1456                     if( defined $session_id ) {
1457                         $add_on = ".session_id=$session_id";
1458                     }
1459                     # answer is for GOSA and has to returned to connected client
1460                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1461                     $client_answer = $gosa_answer.$add_on;
1462                 }
1464                 # target of msg is job queue at this host
1465                 elsif( $answer_target eq "JOBDB") {
1466                     $answer =~ /<header>(\S+)<\/header>/;   
1467                     my $header;
1468                     if( defined $1 ) { $header = $1; }
1469                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1470                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1471                 }
1473                 # Target of msg is a mac address
1474                 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 ) {
1475                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1477                     # Looking for macaddress in known_clients
1478                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1479                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1480                     my $found_ip_flag = 0;
1481                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1482                         my $host_name = $hit->{hostname};
1483                         my $host_key = $hit->{hostkey};
1484                         $answer =~ s/$answer_target/$host_name/g;
1485                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1486                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1487                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1488                         $found_ip_flag++ ;
1489                     }   
1491                     # Looking for macaddress in foreign_clients
1492                     if ($found_ip_flag == 0) {
1493                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1494                         my $res = $foreign_clients_db->select_dbentry($sql);
1495                         while( my ($hit_num, $hit) = each %{ $res } ) {
1496                             my $host_name = $hit->{hostname};
1497                             my $reg_server = $hit->{regserver};
1498                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1499                             
1500                             # Fetch key for reg_server
1501                             my $reg_server_key;
1502                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1503                             my $res = $known_server_db->select_dbentry($sql);
1504                             if (exists $res->{1}) {
1505                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1506                             } else {
1507                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1508                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1509                                 $reg_server_key = undef;
1510                             }
1512                             # Send answer to server where client is registered
1513                             if (defined $reg_server_key) {
1514                                 $answer =~ s/$answer_target/$host_name/g;
1515                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1516                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1517                                 $found_ip_flag++ ;
1518                             }
1519                         }
1520                     }
1522                     # No mac to ip matching found
1523                     if( $found_ip_flag == 0) {
1524                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1525                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1526                     }
1528                 # Answer is for one specific host   
1529                 } else {
1530                     # get encrypt_key
1531                     my $encrypt_key = &get_encrypt_key($answer_target);
1532                     if( not defined $encrypt_key ) {
1533                         # unknown target
1534                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1535                         next;
1536                     }
1537                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1538                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1539                 }
1540             }
1541         }
1542     }
1544     my $filter = POE::Filter::Reference->new();
1545     my %result = ( 
1546             status => "seems ok to me",
1547             answer => $client_answer,
1548             );
1550     my $output = $filter->put( [ \%result ] );
1551     print @$output;
1556 sub session_start {
1557     my ($kernel) = $_[KERNEL];
1558     $global_kernel = $kernel;
1559     $kernel->yield('register_at_foreign_servers');
1560         $kernel->yield('create_fai_server_db', $fai_server_tn );
1561         $kernel->yield('create_fai_release_db', $fai_release_tn );
1562     $kernel->yield('watch_for_next_tasks');
1563         $kernel->sig(USR1 => "sig_handler");
1564         $kernel->sig(USR2 => "recreate_packages_db");
1565         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1566         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1567     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1568         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1569     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1570         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1571     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1573     # Start opsi check
1574     if ($opsi_enabled eq "true") {
1575         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1576     }
1581 sub watch_for_done_jobs {
1582         #CHECK: $heap for what?
1583         my ($kernel,$heap) = @_[KERNEL, HEAP];
1585         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1586         my $res = $job_db->select_dbentry( $sql_statement );
1588         while( my ($id, $hit) = each %{$res} ) {
1589                 my $jobdb_id = $hit->{id};
1590                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1591                 my $res = $job_db->del_dbentry($sql_statement); 
1592         }
1594         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1598 sub watch_for_opsi_jobs {
1599     my ($kernel) = $_[KERNEL];
1601     # 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 
1602     # opsi install job is to parse the xml message. There is still the correct header.
1603     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1604         my $res = $job_db->select_dbentry( $sql_statement );
1606     # Ask OPSI for an update of the running jobs
1607     while (my ($id, $hit) = each %$res ) {
1608         # Determine current parameters of the job
1609         my $hostId = $hit->{'plainname'};
1610         my $macaddress = $hit->{'macaddress'};
1611         my $progress = $hit->{'progress'};
1613         my $result= {};
1614         
1615         # For hosts, only return the products that are or get installed
1616         my $callobj;
1617         $callobj = {
1618             method  => 'getProductStates_hash',
1619             params  => [ $hostId ],
1620             id  => 1,
1621         };
1622         
1623         my $hres = $opsi_client->call($opsi_url, $callobj);
1624         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1625         if (not &check_opsi_res($hres)) {
1626             my $htmp= $hres->result->{$hostId};
1627         
1628             # Check state != not_installed or action == setup -> load and add
1629             my $products= 0;
1630             my $installed= 0;
1631             my $installing = 0;
1632             my $error= 0;  
1633             my @installed_list;
1634             my @error_list;
1635             my $act_status = "none";
1636             foreach my $product (@{$htmp}){
1638                 if ($product->{'installationStatus'} ne "not_installed" or
1639                         $product->{'actionRequest'} eq "setup"){
1641                     # Increase number of products for this host
1642                     $products++;
1643         
1644                     if ($product->{'installationStatus'} eq "failed"){
1645                         $result->{$product->{'productId'}}= "error";
1646                         unshift(@error_list, $product->{'productId'});
1647                         $error++;
1648                     }
1649                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1650                         $result->{$product->{'productId'}}= "installed";
1651                         unshift(@installed_list, $product->{'productId'});
1652                         $installed++;
1653                     }
1654                     if ($product->{'installationStatus'} eq "installing"){
1655                         $result->{$product->{'productId'}}= "installing";
1656                         $installing++;
1657                         $act_status = "installing - ".$product->{'productId'};
1658                     }
1659                 }
1660             }
1661         
1662             # Estimate "rough" progress, avoid division by zero
1663             if ($products == 0) {
1664                 $result->{'progress'}= 0;
1665             } else {
1666                 $result->{'progress'}= int($installed * 100 / $products);
1667             }
1669             # Set updates in job queue
1670             if ((not $error) && (not $installing) && ($installed)) {
1671                 $act_status = "installed - ".join(", ", @installed_list);
1672             }
1673             if ($error) {
1674                 $act_status = "error - ".join(", ", @error_list);
1675             }
1676             if ($progress ne $result->{'progress'} ) {
1677                 # Updating progress and result 
1678                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1679                 my $update_res = $job_db->update_dbentry($update_statement);
1680             }
1681             if ($progress eq 100) { 
1682                 # Updateing status
1683                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1684                 if ($error) {
1685                     $done_statement .= "status='error'";
1686                 } else {
1687                     $done_statement .= "status='done'";
1688                 }
1689                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1690                 my $done_res = $job_db->update_dbentry($done_statement);
1691             }
1694         }
1695     }
1697     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1701 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1702 sub watch_for_modified_jobs {
1703     my ($kernel,$heap) = @_[KERNEL, HEAP];
1705     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1706     my $res = $job_db->select_dbentry( $sql_statement );
1707     
1708     # if db contains no jobs which should be update, do nothing
1709     if (keys %$res != 0) {
1711         if ($job_synchronization  eq "true") {
1712             # make out of the db result a gosa-si message   
1713             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1714  
1715             # update all other SI-server
1716             &inform_all_other_si_server($update_msg);
1717         }
1719         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1720         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1721         $res = $job_db->update_dbentry($sql_statement);
1722     }
1724     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1728 sub watch_for_new_jobs {
1729         if($watch_for_new_jobs_in_progress == 0) {
1730                 $watch_for_new_jobs_in_progress = 1;
1731                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1733                 # check gosa job quaeue for jobs with executable timestamp
1734                 my $timestamp = &get_time();
1735                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1736                 my $res = $job_db->exec_statement( $sql_statement );
1738                 # Merge all new jobs that would do the same actions
1739                 my @drops;
1740                 my $hits;
1741                 foreach my $hit (reverse @{$res} ) {
1742                         my $macaddress= lc @{$hit}[8];
1743                         my $headertag= @{$hit}[5];
1744                         if(
1745                                 defined($hits->{$macaddress}) &&
1746                                 defined($hits->{$macaddress}->{$headertag}) &&
1747                                 defined($hits->{$macaddress}->{$headertag}[0])
1748                         ) {
1749                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1750                         }
1751                         $hits->{$macaddress}->{$headertag}= $hit;
1752                 }
1754                 # Delete new jobs with a matching job in state 'processing'
1755                 foreach my $macaddress (keys %{$hits}) {
1756                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1757                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1758                                 if(defined($jobdb_id)) {
1759                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1760                                         my $res = $job_db->exec_statement( $sql_statement );
1761                                         foreach my $hit (@{$res}) {
1762                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1763                                         }
1764                                 } else {
1765                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1766                                 }
1767                         }
1768                 }
1770                 # Commit deletion
1771                 $job_db->exec_statementlist(\@drops);
1773                 # Look for new jobs that could be executed
1774                 foreach my $macaddress (keys %{$hits}) {
1776                         # Look if there is an executing job
1777                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1778                         my $res = $job_db->exec_statement( $sql_statement );
1780                         # Skip new jobs for host if there is a processing job
1781                         if(defined($res) and defined @{$res}[0]) {
1782                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1783                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1784                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1785                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1786                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1787                                         if(defined($res_2) and defined @{$res_2}[0]) {
1788                                                 # Set status from goto-activation to 'waiting' and update timestamp
1789                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1790                                                 $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'");
1791                                         }
1792                                 }
1793                                 next;
1794                         }
1796                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1797                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1798                                 if(defined($jobdb_id)) {
1799                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1801                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1802                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1803                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1805                                         # expect macaddress is unique!!!!!!
1806                                         my $target = $res_hash->{1}->{hostname};
1808                                         # change header
1809                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1811                                         # add sqlite_id
1812                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1814                                         $job_msg =~ /<header>(\S+)<\/header>/;
1815                                         my $header = $1 ;
1816                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1818                                         # update status in job queue to ...
1819                     # ... 'processing', for jobs: 'reinstall', 'update'
1820                     if (($header =~ /gosa_trigger_action_reinstall/) 
1821                             || ($header =~ /gosa_trigger_activate_new/)
1822                             || ($header =~ /gosa_trigger_action_update/)) {
1823                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1824                         my $dbres = $job_db->update_dbentry($sql_statement);
1825                     }
1827                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1828                     else {
1829                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1830                         my $dbres = $job_db->update_dbentry($sql_statement);
1831                     }
1832                 
1834                                         # We don't want parallel processing
1835                                         last;
1836                                 }
1837                         }
1838                 }
1840                 $watch_for_new_jobs_in_progress = 0;
1841                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1842         }
1846 sub watch_for_new_messages {
1847     my ($kernel,$heap) = @_[KERNEL, HEAP];
1848     my @coll_user_msg;   # collection list of outgoing messages
1849     
1850     # check messaging_db for new incoming messages with executable timestamp
1851     my $timestamp = &get_time();
1852     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1853     my $res = $messaging_db->exec_statement( $sql_statement );
1854         foreach my $hit (@{$res}) {
1856         # create outgoing messages
1857         my $message_to = @{$hit}[3];
1858         # translate message_to to plain login name
1859         my @message_to_l = split(/,/, $message_to);  
1860                 my %receiver_h; 
1861                 foreach my $receiver (@message_to_l) {
1862                         if ($receiver =~ /^u_([\s\S]*)$/) {
1863                                 $receiver_h{$1} = 0;
1864                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1865                                 my $group_name = $1;
1866                                 # fetch all group members from ldap and add them to receiver hash
1867                                 my $ldap_handle = &get_ldap_handle();
1868                                 if (defined $ldap_handle) {
1869                                                 my $mesg = $ldap_handle->search(
1870                                                                                 base => $ldap_base,
1871                                                                                 scope => 'sub',
1872                                                                                 attrs => ['memberUid'],
1873                                                                                 filter => "cn=$group_name",
1874                                                                                 );
1875                                                 if ($mesg->count) {
1876                                                                 my @entries = $mesg->entries;
1877                                                                 foreach my $entry (@entries) {
1878                                                                                 my @receivers= $entry->get_value("memberUid");
1879                                                                                 foreach my $receiver (@receivers) { 
1880                                                                                                 $receiver_h{$receiver} = 0;
1881                                                                                 }
1882                                                                 }
1883                                                 } 
1884                                                 # translating errors ?
1885                                                 if ($mesg->code) {
1886                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1887                                                 }
1888                                 # ldap handle error ?           
1889                                 } else {
1890                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1891                                 }
1892                         } else {
1893                                 my $sbjct = &encode_base64(@{$hit}[1]);
1894                                 my $msg = &encode_base64(@{$hit}[7]);
1895                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1896                         }
1897                 }
1898                 my @receiver_l = keys(%receiver_h);
1900         my $message_id = @{$hit}[0];
1902         #add each outgoing msg to messaging_db
1903         my $receiver;
1904         foreach $receiver (@receiver_l) {
1905             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1906                 "VALUES ('".
1907                 $message_id."', '".    # id
1908                 @{$hit}[1]."', '".     # subject
1909                 @{$hit}[2]."', '".     # message_from
1910                 $receiver."', '".      # message_to
1911                 "none"."', '".         # flag
1912                 "out"."', '".          # direction
1913                 @{$hit}[6]."', '".     # delivery_time
1914                 @{$hit}[7]."', '".     # message
1915                 $timestamp."'".     # timestamp
1916                 ")";
1917             &daemon_log("M DEBUG: $sql_statement", 1);
1918             my $res = $messaging_db->exec_statement($sql_statement);
1919             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1920         }
1922         # set incoming message to flag d=deliverd
1923         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1924         &daemon_log("M DEBUG: $sql_statement", 7);
1925         $res = $messaging_db->update_dbentry($sql_statement);
1926         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1927     }
1929     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1930     return;
1933 sub watch_for_delivery_messages {
1934     my ($kernel, $heap) = @_[KERNEL, HEAP];
1936     # select outgoing messages
1937     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1938     #&daemon_log("0 DEBUG: $sql", 7);
1939     my $res = $messaging_db->exec_statement( $sql_statement );
1940     
1941     # build out msg for each    usr
1942     foreach my $hit (@{$res}) {
1943         my $receiver = @{$hit}[3];
1944         my $msg_id = @{$hit}[0];
1945         my $subject = @{$hit}[1];
1946         my $message = @{$hit}[7];
1948         # resolve usr -> host where usr is logged in
1949         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1950         #&daemon_log("0 DEBUG: $sql", 7);
1951         my $res = $login_users_db->exec_statement($sql);
1953         # receiver is logged in nowhere
1954         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1956         # receiver ist logged in at a client registered at local server
1957                 my $send_succeed = 0;
1958                 foreach my $hit (@$res) {
1959                                 my $receiver_host = @$hit[0];
1960                 my $delivered2host = 0;
1961                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1963                                 # Looking for host in know_clients_db 
1964                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1965                                 my $res = $known_clients_db->exec_statement($sql);
1967                 # Host is known in known_clients_db
1968                 if (ref(@$res[0]) eq "ARRAY") {
1969                     my $receiver_key = @{@{$res}[0]}[2];
1970                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1971                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1972                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1973                     if ($error == 0 ) {
1974                         $send_succeed++ ;
1975                         $delivered2host++ ;
1976                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1977                     } else {
1978                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1979                     }
1980                 }
1981                 
1982                 # Message already send, do not need to do anything more, otherwise ...
1983                 if ($delivered2host) { next;}
1984     
1985                 # ...looking for host in foreign_clients_db
1986                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
1987                 $res = $foreign_clients_db->exec_statement($sql);
1988   
1989                                 # Host is known in foreign_clients_db 
1990                                 if (ref(@$res[0]) eq "ARRAY") { 
1991                     my $registration_server = @{@{$res}[0]}[2];
1992                     
1993                     # Fetch encryption key for registration server
1994                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
1995                     my $res = $known_server_db->exec_statement($sql);
1996                     if (ref(@$res[0]) eq "ARRAY") { 
1997                         my $registration_server_key = @{@{$res}[0]}[3];
1998                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1999                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2000                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2001                         if ($error == 0 ) {
2002                             $send_succeed++ ;
2003                             $delivered2host++ ;
2004                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2005                         } else {
2006                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2007                         }
2009                     } else {
2010                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2011                                 "registrated at server '$registration_server', ".
2012                                 "but no data available in known_server_db ", 1); 
2013                     }
2014                 }
2015                 
2016                 if (not $delivered2host) {
2017                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2018                 }
2019                 }
2021                 if ($send_succeed) {
2022                                 # set outgoing msg at db to deliverd
2023                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2024                                 my $res = $messaging_db->exec_statement($sql); 
2025                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2026                 } else {
2027             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2028         }
2029         }
2031     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2032     return;
2036 sub watch_for_done_messages {
2037     my ($kernel,$heap) = @_[KERNEL, HEAP];
2039     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2040     #&daemon_log("0 DEBUG: $sql", 7);
2041     my $res = $messaging_db->exec_statement($sql); 
2043     foreach my $hit (@{$res}) {
2044         my $msg_id = @{$hit}[0];
2046         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2047         #&daemon_log("0 DEBUG: $sql", 7); 
2048         my $res = $messaging_db->exec_statement($sql);
2050         # not all usr msgs have been seen till now
2051         if ( ref(@$res[0]) eq "ARRAY") { next; }
2052         
2053         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2054         #&daemon_log("0 DEBUG: $sql", 7);
2055         $res = $messaging_db->exec_statement($sql);
2056     
2057     }
2059     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2060     return;
2064 sub watch_for_old_known_clients {
2065     my ($kernel,$heap) = @_[KERNEL, HEAP];
2067     my $sql_statement = "SELECT * FROM $known_clients_tn";
2068     my $res = $known_clients_db->select_dbentry( $sql_statement );
2070     my $cur_time = int(&get_time());
2072     while ( my ($hit_num, $hit) = each %$res) {
2073         my $expired_timestamp = int($hit->{'timestamp'});
2074         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2075         my $dt = DateTime->new( year   => $1,
2076                 month  => $2,
2077                 day    => $3,
2078                 hour   => $4,
2079                 minute => $5,
2080                 second => $6,
2081                 );
2083         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2084         $expired_timestamp = $dt->ymd('').$dt->hms('');
2085         if ($cur_time > $expired_timestamp) {
2086             my $hostname = $hit->{'hostname'};
2087             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2088             my $del_res = $known_clients_db->exec_statement($del_sql);
2090             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2091         }
2093     }
2095     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2099 sub watch_for_next_tasks {
2100     my ($kernel,$heap) = @_[KERNEL, HEAP];
2102     my $sql = "SELECT * FROM $incoming_tn";
2103     my $res = $incoming_db->select_dbentry($sql);
2104     
2105     while ( my ($hit_num, $hit) = each %$res) {
2106         my $headertag = $hit->{'headertag'};
2107         if ($headertag =~ /^answer_(\d+)/) {
2108             # do not start processing, this message is for a still running POE::Wheel
2109             next;
2110         }
2111         my $message_id = $hit->{'id'};
2112         my $session_id = $hit->{'sessionid'};
2113         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2114         $kernel->yield('next_task', $hit);
2116         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2117         my $res = $incoming_db->exec_statement($sql);
2118     }
2120     $kernel->delay_set('watch_for_next_tasks', 1); 
2124 sub get_ldap_handle {
2125         my ($session_id) = @_;
2126         my $heap;
2127         my $ldap_handle;
2129         if (not defined $session_id ) { $session_id = 0 };
2130         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2132         if ($session_id == 0) {
2133                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
2134                 $ldap_handle = Net::LDAP->new( $ldap_uri );
2135                 if (defined $ldap_handle) {
2136                         $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!"); 
2137                 } else {
2138                         daemon_log("$session_id ERROR: creation of a new LDAP handle failed (ldap_uri '$ldap_uri')");
2139                 }
2141         } else {
2142                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2143                 if( defined $session_reference ) {
2144                         $heap = $session_reference->get_heap();
2145                 }
2147                 if (not defined $heap) {
2148                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
2149                         return;
2150                 }
2152                 # TODO: This "if" is nonsense, because it doesn't prove that the
2153                 #       used handle is still valid - or if we've to reconnect...
2154                 #if (not exists $heap->{ldap_handle}) {
2155                         $ldap_handle = Net::LDAP->new( $ldap_uri );
2156                         $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!"); 
2157                         $heap->{ldap_handle} = $ldap_handle;
2158                 #}
2159         }
2160         return $ldap_handle;
2164 sub change_fai_state {
2165     my ($st, $targets, $session_id) = @_;
2166     $session_id = 0 if not defined $session_id;
2167     # Set FAI state to localboot
2168     my %mapActions= (
2169         reboot    => '',
2170         update    => 'softupdate',
2171         localboot => 'localboot',
2172         reinstall => 'install',
2173         rescan    => '',
2174         wake      => '',
2175         memcheck  => 'memcheck',
2176         sysinfo   => 'sysinfo',
2177         install   => 'install',
2178     );
2180     # Return if this is unknown
2181     if (!exists $mapActions{ $st }){
2182         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2183       return;
2184     }
2186     my $state= $mapActions{ $st };
2188     my $ldap_handle = &get_ldap_handle($session_id);
2189     if( defined($ldap_handle) ) {
2191       # Build search filter for hosts
2192         my $search= "(&(objectClass=GOhard)";
2193         foreach (@{$targets}){
2194             $search.= "(macAddress=$_)";
2195         }
2196         $search.= ")";
2198       # If there's any host inside of the search string, procress them
2199         if (!($search =~ /macAddress/)){
2200             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2201             return;
2202         }
2204       # Perform search for Unit Tag
2205       my $mesg = $ldap_handle->search(
2206           base   => $ldap_base,
2207           scope  => 'sub',
2208           attrs  => ['dn', 'FAIstate', 'objectClass'],
2209           filter => "$search"
2210           );
2212           if ($mesg->count) {
2213                   my @entries = $mesg->entries;
2214                   if (0 == @entries) {
2215                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2216                   }
2218                   foreach my $entry (@entries) {
2219                           # Only modify entry if it is not set to '$state'
2220                           if ($entry->get_value("FAIstate") ne "$state"){
2221                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2222                                   my $result;
2223                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2224                                   if (exists $tmp{'FAIobject'}){
2225                                           if ($state eq ''){
2226                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2227                                           } else {
2228                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2229                                           }
2230                                   } elsif ($state ne ''){
2231                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2232                                   }
2234                                   # Errors?
2235                                   if ($result->code){
2236                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2237                                   }
2238                           } else {
2239                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2240                           }  
2241                   }
2242           } else {
2243                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2244           }
2246     # if no ldap handle defined
2247     } else {
2248         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2249     }
2251         return;
2255 sub change_goto_state {
2256     my ($st, $targets, $session_id) = @_;
2257     $session_id = 0  if not defined $session_id;
2259     # Switch on or off?
2260     my $state= $st eq 'active' ? 'active': 'locked';
2262     my $ldap_handle = &get_ldap_handle($session_id);
2263     if( defined($ldap_handle) ) {
2265       # Build search filter for hosts
2266       my $search= "(&(objectClass=GOhard)";
2267       foreach (@{$targets}){
2268         $search.= "(macAddress=$_)";
2269       }
2270       $search.= ")";
2272       # If there's any host inside of the search string, procress them
2273       if (!($search =~ /macAddress/)){
2274         return;
2275       }
2277       # Perform search for Unit Tag
2278       my $mesg = $ldap_handle->search(
2279           base   => $ldap_base,
2280           scope  => 'sub',
2281           attrs  => ['dn', 'gotoMode'],
2282           filter => "$search"
2283           );
2285       if ($mesg->count) {
2286         my @entries = $mesg->entries;
2287         foreach my $entry (@entries) {
2289           # Only modify entry if it is not set to '$state'
2290           if ($entry->get_value("gotoMode") ne $state){
2292             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2293             my $result;
2294             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2296             # Errors?
2297             if ($result->code){
2298               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2299             }
2301           }
2302         }
2303       } else {
2304                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2305           }
2307     }
2311 sub run_recreate_packages_db {
2312     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2313     my $session_id = $session->ID;
2314         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2315         $kernel->yield('create_fai_release_db', $fai_release_tn);
2316         $kernel->yield('create_fai_server_db', $fai_server_tn);
2317         return;
2321 sub run_create_fai_server_db {
2322     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2323     my $session_id = $session->ID;
2324     my $task = POE::Wheel::Run->new(
2325             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2326             StdoutEvent  => "session_run_result",
2327             StderrEvent  => "session_run_debug",
2328             CloseEvent   => "session_run_done",
2329             );
2331     $heap->{task}->{ $task->ID } = $task;
2332     return;
2336 sub create_fai_server_db {
2337         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2338         my $result;
2340         if (not defined $session_id) { $session_id = 0; }
2341         my $ldap_handle = &get_ldap_handle();
2342         if(defined($ldap_handle)) {
2343                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2344                 my $mesg= $ldap_handle->search(
2345                         base   => $ldap_base,
2346                         scope  => 'sub',
2347                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2348                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2349                 );
2350                 if($mesg->{'resultCode'} == 0 &&
2351                         $mesg->count != 0) {
2352                         foreach my $entry (@{$mesg->{entries}}) {
2353                                 if($entry->exists('FAIrepository')) {
2354                                         # Add an entry for each Repository configured for server
2355                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2356                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2357                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2358                                                 $result= $fai_server_db->add_dbentry( { 
2359                                                                 table => $table_name,
2360                                                                 primkey => ['server', 'fai_release', 'tag'],
2361                                                                 server => $tmp_url,
2362                                                                 fai_release => $tmp_release,
2363                                                                 sections => $tmp_sections,
2364                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2365                                                         } );
2366                                         }
2367                                 }
2368                         }
2369                 }
2370                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2372                 # TODO: Find a way to post the 'create_packages_list_db' event
2373                 if(not defined($dont_create_packages_list)) {
2374                         &create_packages_list_db(undef, undef, $session_id);
2375                 }
2376         }       
2378         $ldap_handle->disconnect;
2379         return $result;
2383 sub run_create_fai_release_db {
2384         my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2385         my $session_id = $session->ID;
2386         my $task = POE::Wheel::Run->new(
2387                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2388                 StdoutEvent  => "session_run_result",
2389                 StderrEvent  => "session_run_debug",
2390                 CloseEvent   => "session_run_done",
2391         );
2393         $heap->{task}->{ $task->ID } = $task;
2394         return;
2398 sub create_fai_release_db {
2399         my ($table_name, $session_id) = @_;
2400         my $result;
2402         # used for logging
2403         if (not defined $session_id) { $session_id = 0; }
2405         my $ldap_handle = &get_ldap_handle();
2406         if(defined($ldap_handle)) {
2407                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2408                 my $mesg= $ldap_handle->search(
2409                         base   => $ldap_base,
2410                         scope  => 'sub',
2411                         attrs  => [],
2412                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2413                 );
2414                 if(($mesg->code == 0) && ($mesg->count != 0))
2415                 {
2416                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2418                         # Walk through all possible FAI container ou's
2419                         my @sql_list;
2420                         my $timestamp= &get_time();
2421                         foreach my $ou (@{$mesg->{entries}}) {
2422                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2423                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2424                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2425                                         if(@tmp_array) {
2426                                                 foreach my $entry (@tmp_array) {
2427                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2428                                                                 my $sql= 
2429                                                                 "INSERT INTO $table_name "
2430                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2431                                                                 .$timestamp.","
2432                                                                 ."'".$entry->{'release'}."',"
2433                                                                 ."'".$entry->{'class'}."',"
2434                                                                 ."'".$entry->{'type'}."',"
2435                                                                 ."'".$entry->{'state'}."')";
2436                                                                 push @sql_list, $sql;
2437                                                         }
2438                                                 }
2439                                         }
2440                                 }
2441                         }
2443                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2444                         if(@sql_list) {
2445                                 unshift @sql_list, "VACUUM";
2446                                 unshift @sql_list, "DELETE FROM $table_name";
2447                                 $fai_release_db->exec_statementlist(\@sql_list);
2448                         }
2449                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2450                 } else {
2451                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2452                 }
2453                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2454         }
2455         $ldap_handle->disconnect;
2456         return $result;
2459 sub get_fai_types {
2460         my $tmp_classes = shift || return undef;
2461         my @result;
2463         foreach my $type(keys %{$tmp_classes}) {
2464                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2465                         my $entry = {
2466                                 type => $type,
2467                                 state => $tmp_classes->{$type}[0],
2468                         };
2469                         push @result, $entry;
2470                 }
2471         }
2473         return @result;
2476 sub get_fai_state {
2477         my $result = "";
2478         my $tmp_classes = shift || return $result;
2480         foreach my $type(keys %{$tmp_classes}) {
2481                 if(defined($tmp_classes->{$type}[0])) {
2482                         $result = $tmp_classes->{$type}[0];
2483                         
2484                 # State is equal for all types in class
2485                         last;
2486                 }
2487         }
2489         return $result;
2492 sub resolve_fai_classes {
2493         my ($fai_base, $ldap_handle, $session_id) = @_;
2494         if (not defined $session_id) { $session_id = 0; }
2495         my $result;
2496         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2497         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2498         my $fai_classes;
2500         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2501         my $mesg= $ldap_handle->search(
2502                 base   => $fai_base,
2503                 scope  => 'sub',
2504                 attrs  => ['cn','objectClass','FAIstate'],
2505                 filter => $fai_filter,
2506         );
2507         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2509         if($mesg->{'resultCode'} == 0 &&
2510                 $mesg->count != 0) {
2511                 foreach my $entry (@{$mesg->{entries}}) {
2512                         if($entry->exists('cn')) {
2513                                 my $tmp_dn= $entry->dn();
2514                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2515                                         - length($fai_base) - 1 );
2517                                 # Skip classname and ou dn parts for class
2518                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2520                                 # Skip classes without releases
2521                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2522                                         next;
2523                                 }
2525                                 my $tmp_cn= $entry->get_value('cn');
2526                                 my $tmp_state= $entry->get_value('FAIstate');
2528                                 my $tmp_type;
2529                                 # Get FAI type
2530                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2531                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2532                                                 $tmp_type= $oclass;
2533                                                 last;
2534                                         }
2535                                 }
2537                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2538                                         # A Subrelease
2539                                         my @sub_releases = split(/,/, $tmp_release);
2541                                         # Walk through subreleases and build hash tree
2542                                         my $hash;
2543                                         while(my $tmp_sub_release = pop @sub_releases) {
2544                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2545                                         }
2546                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2547                                 } else {
2548                                         # A branch, no subrelease
2549                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2550                                 }
2551                         } elsif (!$entry->exists('cn')) {
2552                                 my $tmp_dn= $entry->dn();
2553                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2554                                         - length($fai_base) - 1 );
2555                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2557                                 # Skip classes without releases
2558                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2559                                         next;
2560                                 }
2562                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2563                                         # A Subrelease
2564                                         my @sub_releases= split(/,/, $tmp_release);
2566                                         # Walk through subreleases and build hash tree
2567                                         my $hash;
2568                                         while(my $tmp_sub_release = pop @sub_releases) {
2569                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2570                                         }
2571                                         # Remove the last two characters
2572                                         chop($hash);
2573                                         chop($hash);
2575                                         eval('$fai_classes->'.$hash.'= {}');
2576                                 } else {
2577                                         # A branch, no subrelease
2578                                         if(!exists($fai_classes->{$tmp_release})) {
2579                                                 $fai_classes->{$tmp_release} = {};
2580                                         }
2581                                 }
2582                         }
2583                 }
2585                 # The hash is complete, now we can honor the copy-on-write based missing entries
2586                 foreach my $release (keys %$fai_classes) {
2587                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2588                 }
2589         }
2590         return $result;
2593 sub apply_fai_inheritance {
2594        my $fai_classes = shift || return {};
2595        my $tmp_classes;
2597        # Get the classes from the branch
2598        foreach my $class (keys %{$fai_classes}) {
2599                # Skip subreleases
2600                if($class =~ /^ou=.*$/) {
2601                        next;
2602                } else {
2603                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2604                }
2605        }
2607        # Apply to each subrelease
2608        foreach my $subrelease (keys %{$fai_classes}) {
2609                if($subrelease =~ /ou=/) {
2610                        foreach my $tmp_class (keys %{$tmp_classes}) {
2611                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2612                                        $fai_classes->{$subrelease}->{$tmp_class} =
2613                                        deep_copy($tmp_classes->{$tmp_class});
2614                                } else {
2615                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2616                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2617                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2618                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2619                                                }
2620                                        }
2621                                }
2622                        }
2623                }
2624        }
2626        # Find subreleases in deeper levels
2627        foreach my $subrelease (keys %{$fai_classes}) {
2628                if($subrelease =~ /ou=/) {
2629                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2630                                if($subsubrelease =~ /ou=/) {
2631                                        apply_fai_inheritance($fai_classes->{$subrelease});
2632                                }
2633                        }
2634                }
2635        }
2637        return $fai_classes;
2640 sub get_fai_release_entries {
2641         my $tmp_classes = shift || return;
2642         my $parent = shift || "";
2643         my @result = shift || ();
2645         foreach my $entry (keys %{$tmp_classes}) {
2646                 if(defined($entry)) {
2647                         if($entry =~ /^ou=.*$/) {
2648                                 my $release_name = $entry;
2649                                 $release_name =~ s/ou=//g;
2650                                 if(length($parent)>0) {
2651                                         $release_name = $parent."/".$release_name;
2652                                 }
2653                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2654                                 foreach my $bufentry(@bufentries) {
2655                                         push @result, $bufentry;
2656                                 }
2657                         } else {
2658                                 my @types = get_fai_types($tmp_classes->{$entry});
2659                                 foreach my $type (@types) {
2660                                         push @result, 
2661                                         {
2662                                                 'class' => $entry,
2663                                                 'type' => $type->{'type'},
2664                                                 'release' => $parent,
2665                                                 'state' => $type->{'state'},
2666                                         };
2667                                 }
2668                         }
2669                 }
2670         }
2672         return @result;
2675 sub deep_copy {
2676         my $this = shift;
2677         if (not ref $this) {
2678                 $this;
2679         } elsif (ref $this eq "ARRAY") {
2680                 [map deep_copy($_), @$this];
2681         } elsif (ref $this eq "HASH") {
2682                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2683         } else { die "what type is $_?" }
2687 sub session_run_result {
2688     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2689     $kernel->sig(CHLD => "child_reap");
2692 sub session_run_debug {
2693     my $result = $_[ARG0];
2694     print STDERR "$result\n";
2697 sub session_run_done {
2698     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2699     delete $heap->{task}->{$task_id};
2703 sub create_sources_list {
2704         my $session_id = shift;
2705         my $ldap_handle = &main::get_ldap_handle;
2706         my $result="/tmp/gosa_si_tmp_sources_list";
2708         # Remove old file
2709         if(stat($result)) {
2710                 unlink($result);
2711                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2712         }
2714         my $fh;
2715         open($fh, ">$result");
2716         if (not defined $fh) {
2717                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2718                 return undef;
2719         }
2720         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2721                 my $mesg=$ldap_handle->search(
2722                         base    => $main::ldap_server_dn,
2723                         scope   => 'base',
2724                         attrs   => 'FAIrepository',
2725                         filter  => 'objectClass=FAIrepositoryServer'
2726                 );
2727                 if($mesg->count) {
2728                         foreach my $entry(@{$mesg->{'entries'}}) {
2729                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2730                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2731                                         my $line = "deb $server $release";
2732                                         $sections =~ s/,/ /g;
2733                                         $line.= " $sections";
2734                                         print $fh $line."\n";
2735                                 }
2736                         }
2737                 }
2738         } else {
2739                 if (defined $main::ldap_server_dn){
2740                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2741                 } else {
2742                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2743                 }
2744         }
2745         close($fh);
2747         return $result;
2751 sub run_create_packages_list_db {
2752     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2753         my $session_id = $session->ID;
2755         my $task = POE::Wheel::Run->new(
2756                                         Priority => +20,
2757                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2758                                         StdoutEvent  => "session_run_result",
2759                                         StderrEvent  => "session_run_debug",
2760                                         CloseEvent   => "session_run_done",
2761                                         );
2762         $heap->{task}->{ $task->ID } = $task;
2766 sub create_packages_list_db {
2767         my ($ldap_handle, $sources_file, $session_id) = @_;
2768         
2769         # it should not be possible to trigger a recreation of packages_list_db
2770         # while packages_list_db is under construction, so set flag packages_list_under_construction
2771         # which is tested befor recreation can be started
2772         if (-r $packages_list_under_construction) {
2773                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2774                 return;
2775         } else {
2776                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2777                 # set packages_list_under_construction to true
2778                 system("touch $packages_list_under_construction");
2779                 @packages_list_statements=();
2780         }
2782         if (not defined $session_id) { $session_id = 0; }
2783         if (not defined $ldap_handle) { 
2784                 $ldap_handle= &get_ldap_handle();
2786                 if (not defined $ldap_handle) {
2787                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2788                         unlink($packages_list_under_construction);
2789                         return;
2790                 }
2791         }
2792         if (not defined $sources_file) { 
2793                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2794                 $sources_file = &create_sources_list($session_id);
2795         }
2797         if (not defined $sources_file) {
2798                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2799                 unlink($packages_list_under_construction);
2800                 return;
2801         }
2803         my $line;
2805         open(CONFIG, "<$sources_file") or do {
2806                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2807                 unlink($packages_list_under_construction);
2808                 return;
2809         };
2811         # Read lines
2812         while ($line = <CONFIG>){
2813                 # Unify
2814                 chop($line);
2815                 $line =~ s/^\s+//;
2816                 $line =~ s/^\s+/ /;
2818                 # Strip comments
2819                 $line =~ s/#.*$//g;
2821                 # Skip empty lines
2822                 if ($line =~ /^\s*$/){
2823                         next;
2824                 }
2826                 # Interpret deb line
2827                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2828                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2829                         my $section;
2830                         foreach $section (split(' ', $sections)){
2831                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2832                         }
2833                 }
2834         }
2836         close (CONFIG);
2838         if(keys(%repo_dirs)) {
2839                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2840                 &main::strip_packages_list_statements();
2841                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2842         }
2843         unlink($packages_list_under_construction);
2844         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2845         return;
2848 # This function should do some intensive task to minimize the db-traffic
2849 sub strip_packages_list_statements {
2850         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2851         my @new_statement_list=();
2852         my $hash;
2853         my $insert_hash;
2854         my $update_hash;
2855         my $delete_hash;
2856         my $known_packages_hash;
2857         my $local_timestamp=get_time();
2859         foreach my $existing_entry (@existing_entries) {
2860                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2861         }
2863         foreach my $statement (@packages_list_statements) {
2864                 if($statement =~ /^INSERT/i) {
2865                         # Assign the values from the insert statement
2866                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2867                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2868                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2869                                 # If section or description has changed, update the DB
2870                                 if( 
2871                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2872                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2873                                 ) {
2874                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2875                                 } else {
2876                                         # package is already present in database. cache this knowledge for later use
2877                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2878                                 }
2879                         } else {
2880                                 # Insert a non-existing entry to db
2881                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2882                         }
2883                 } elsif ($statement =~ /^UPDATE/i) {
2884                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2885                         /^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;
2886                         foreach my $distribution (keys %{$hash}) {
2887                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2888                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2889                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2890                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2891                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2892                                                 my $section;
2893                                                 my $description;
2894                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2895                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2896                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2897                                                 }
2898                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2899                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2900                                                 }
2901                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2902                                         }
2903                                 }
2904                         }
2905                 }
2906         }
2908         # Check for orphaned entries
2909         foreach my $existing_entry (@existing_entries) {
2910                 my $distribution= @{$existing_entry}[0];
2911                 my $package= @{$existing_entry}[1];
2912                 my $version= @{$existing_entry}[2];
2913                 my $section= @{$existing_entry}[3];
2915                 if(
2916                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2917                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2918                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2919                 ) {
2920                         next;
2921                 } else {
2922                         # Insert entry to delete hash
2923                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2924                 }
2925         }
2927         # unroll the insert hash
2928         foreach my $distribution (keys %{$insert_hash}) {
2929                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2930                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2931                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2932                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2933                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2934                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2935                                 ."'$local_timestamp')";
2936                         }
2937                 }
2938         }
2940         # unroll the update hash
2941         foreach my $distribution (keys %{$update_hash}) {
2942                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2943                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2944                                 my $set = "";
2945                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2946                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2947                                 }
2948                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2949                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2950                                 }
2951                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2952                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2953                                 }
2954                                 if(defined($set) and length($set) > 0) {
2955                                         $set .= "timestamp = '$local_timestamp'";
2956                                 } else {
2957                                         next;
2958                                 }
2959                                 push @new_statement_list, 
2960                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2961                                 ." distribution = '$distribution'"
2962                                 ." AND package = '$package'"
2963                                 ." AND version = '$version'";
2964                         }
2965                 }
2966         }
2967         
2968         # unroll the delete hash
2969         foreach my $distribution (keys %{$delete_hash}) {
2970                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2971                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2972                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2973                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2974                         }
2975                 }
2976         }
2978         unshift(@new_statement_list, "VACUUM");
2980         @packages_list_statements = @new_statement_list;
2984 sub parse_package_info {
2985     my ($baseurl, $dist, $section, $session_id)= @_;
2986     my ($package);
2987     if (not defined $session_id) { $session_id = 0; }
2988     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2989     $repo_dirs{ "${repo_path}/pool" } = 1;
2991     foreach $package ("Packages.gz"){
2992         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2993         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2994         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2995     }
2996     
3000 sub get_package {
3001     my ($url, $dest, $session_id)= @_;
3002     if (not defined $session_id) { $session_id = 0; }
3004     my $tpath = dirname($dest);
3005     -d "$tpath" || mkpath "$tpath";
3007     # This is ugly, but I've no time to take a look at "how it works in perl"
3008     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3009         system("gunzip -cd '$dest' > '$dest.in'");
3010         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3011         unlink($dest);
3012         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3013     } else {
3014         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3015     }
3016     return 0;
3020 sub parse_package {
3021     my ($path, $dist, $srv_path, $session_id)= @_;
3022     if (not defined $session_id) { $session_id = 0;}
3023     my ($package, $version, $section, $description);
3024     my $PACKAGES;
3025     my $timestamp = &get_time();
3027     if(not stat("$path.in")) {
3028         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3029         return;
3030     }
3032     open($PACKAGES, "<$path.in");
3033     if(not defined($PACKAGES)) {
3034         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3035         return;
3036     }
3038     # Read lines
3039     while (<$PACKAGES>){
3040         my $line = $_;
3041         # Unify
3042         chop($line);
3044         # Use empty lines as a trigger
3045         if ($line =~ /^\s*$/){
3046             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3047             push(@packages_list_statements, $sql);
3048             $package = "none";
3049             $version = "none";
3050             $section = "none";
3051             $description = "none"; 
3052             next;
3053         }
3055         # Trigger for package name
3056         if ($line =~ /^Package:\s/){
3057             ($package)= ($line =~ /^Package: (.*)$/);
3058             next;
3059         }
3061         # Trigger for version
3062         if ($line =~ /^Version:\s/){
3063             ($version)= ($line =~ /^Version: (.*)$/);
3064             next;
3065         }
3067         # Trigger for description
3068         if ($line =~ /^Description:\s/){
3069             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3070             next;
3071         }
3073         # Trigger for section
3074         if ($line =~ /^Section:\s/){
3075             ($section)= ($line =~ /^Section: (.*)$/);
3076             next;
3077         }
3079         # Trigger for filename
3080         if ($line =~ /^Filename:\s/){
3081             my ($filename) = ($line =~ /^Filename: (.*)$/);
3082             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3083             next;
3084         }
3085     }
3087     close( $PACKAGES );
3088     unlink( "$path.in" );
3092 sub store_fileinfo {
3093     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3095     my %fileinfo = (
3096         'package' => $package,
3097         'dist' => $dist,
3098         'version' => $vers,
3099     );
3101     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3105 sub cleanup_and_extract {
3106         my $fileinfo = $repo_files{ $File::Find::name };
3108         if( defined $fileinfo ) {
3109                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3110                 my $sql;
3111                 my $package = $fileinfo->{ 'package' };
3112                 my $newver = $fileinfo->{ 'version' };
3114                 mkpath($dir);
3115                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3117                 if( -f "$dir/DEBIAN/templates" ) {
3119                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3121                         my $tmpl= ""; {
3122                                 local $/=undef;
3123                                 open FILE, "$dir/DEBIAN/templates";
3124                                 $tmpl = &encode_base64(<FILE>);
3125                                 close FILE;
3126                         }
3127                         rmtree("$dir/DEBIAN/templates");
3129                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3130                         push @packages_list_statements, $sql;
3131                 }
3132         }
3134         return;
3138 sub register_at_foreign_servers {   
3139     my ($kernel) = $_[KERNEL];
3141     # hole alle bekannten server aus known_server_db
3142     my $server_sql = "SELECT * FROM $known_server_tn";
3143     my $server_res = $known_server_db->exec_statement($server_sql);
3145     # no entries in known_server_db
3146     if (not ref(@$server_res[0]) eq "ARRAY") { 
3147         # TODO
3148     }
3150     # detect already connected clients
3151     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3152     my $client_res = $known_clients_db->exec_statement($client_sql);
3154     # send my server details to all other gosa-si-server within the network
3155     foreach my $hit (@$server_res) {
3156         my $hostname = @$hit[0];
3157         my $hostkey = &create_passwd;
3159         # add already connected clients to registration message 
3160         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3161         &add_content2xml_hash($myhash, 'key', $hostkey);
3162         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3164         # add locally loaded gosa-si modules to registration message
3165         my $loaded_modules = {};
3166         while (my ($package, $pck_info) = each %$known_modules) {
3167                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3168                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3169                                                         $loaded_modules->{$act_module} = ""; 
3170                                                 }
3171         }
3173         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3175         # add macaddress to registration message
3176         my ($host_ip, $host_port) = split(/:/, $hostname);
3177         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3178         my $network_interface= &get_interface_for_ip($local_ip);
3179         my $host_mac = &get_mac_for_interface($network_interface);
3180         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3181         
3182         # build registration message and send it
3183         my $foreign_server_msg = &create_xml_string($myhash);
3184         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3185     }
3186     
3187     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3188     return;
3192 #==== MAIN = main ==============================================================
3193 #  parse commandline options
3194 Getopt::Long::Configure( "bundling" );
3195 GetOptions("h|help" => \&usage,
3196         "c|config=s" => \$cfg_file,
3197         "f|foreground" => \$foreground,
3198         "v|verbose+" => \$verbose,
3199         "no-arp+" => \$no_arp,
3200            );
3202 #  read and set config parameters
3203 &check_cmdline_param ;
3204 &read_configfile($cfg_file, %cfg_defaults);
3205 &check_pid;
3207 $SIG{CHLD} = 'IGNORE';
3209 # forward error messages to logfile
3210 if( ! $foreground ) {
3211   open( STDIN,  '+>/dev/null' );
3212   open( STDOUT, '+>&STDIN'    );
3213   open( STDERR, '+>&STDIN'    );
3216 # Just fork, if we are not in foreground mode
3217 if( ! $foreground ) { 
3218     chdir '/'                 or die "Can't chdir to /: $!";
3219     $pid = fork;
3220     setsid                    or die "Can't start a new session: $!";
3221     umask 0;
3222 } else { 
3223     $pid = $$; 
3226 # Do something useful - put our PID into the pid_file
3227 if( 0 != $pid ) {
3228     open( LOCK_FILE, ">$pid_file" );
3229     print LOCK_FILE "$pid\n";
3230     close( LOCK_FILE );
3231     if( !$foreground ) { 
3232         exit( 0 ) 
3233     };
3236 # parse head url and revision from svn
3237 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3238 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3239 $server_headURL = defined $1 ? $1 : 'unknown' ;
3240 $server_revision = defined $2 ? $2 : 'unknown' ;
3241 if ($server_headURL =~ /\/tag\// || 
3242         $server_headURL =~ /\/branches\// ) {
3243     $server_status = "stable"; 
3244 } else {
3245     $server_status = "developmental" ;
3247 # Prepare log file and set permissions
3248 $root_uid = getpwnam('root');
3249 $adm_gid = getgrnam('adm');
3250 open(FH, ">>$log_file");
3251 close FH;
3252 chmod(0440, $log_file);
3253 chown($root_uid, $adm_gid, $log_file);
3254 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3256 daemon_log(" ", 1);
3257 daemon_log("$0 started!", 1);
3258 daemon_log("status: $server_status", 1);
3259 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3262     no strict "refs";
3264     if ($db_module eq "DBmysql") {
3265         # connect to incoming_db
3266         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3268         # connect to gosa-si job queue
3269         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3271         # connect to known_clients_db
3272         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3274         # connect to foreign_clients_db
3275         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3277         # connect to known_server_db
3278         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3280         # connect to login_usr_db
3281         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3283         # connect to fai_server_db 
3284         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3286         # connect to fai_release_db
3287         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3289         # connect to packages_list_db
3290         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3292         # connect to messaging_db
3293         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3295     } elsif ($db_module eq "DBsqlite") {
3296         # connect to incoming_db
3297         unlink($incoming_file_name);
3298         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3299         
3300         # connect to gosa-si job queue
3301         unlink($job_queue_file_name);  ## just for debugging
3302         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3303         chmod(0640, $job_queue_file_name);
3304         chown($root_uid, $adm_gid, $job_queue_file_name);
3305         
3306         # connect to known_clients_db
3307         unlink($known_clients_file_name);   ## just for debugging
3308         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3309         chmod(0640, $known_clients_file_name);
3310         chown($root_uid, $adm_gid, $known_clients_file_name);
3311         
3312         # connect to foreign_clients_db
3313         unlink($foreign_clients_file_name);
3314         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3315         chmod(0640, $foreign_clients_file_name);
3316         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3317         
3318         # connect to known_server_db
3319         unlink($known_server_file_name);
3320         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3321         chmod(0640, $known_server_file_name);
3322         chown($root_uid, $adm_gid, $known_server_file_name);
3323         
3324         # connect to login_usr_db
3325         unlink($login_users_file_name);
3326         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3327         chmod(0640, $login_users_file_name);
3328         chown($root_uid, $adm_gid, $login_users_file_name);
3329         
3330         # connect to fai_server_db
3331         unlink($fai_server_file_name);
3332         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3333         chmod(0640, $fai_server_file_name);
3334         chown($root_uid, $adm_gid, $fai_server_file_name);
3335         
3336         # connect to fai_release_db
3337         unlink($fai_release_file_name);
3338         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3339         chmod(0640, $fai_release_file_name);
3340         chown($root_uid, $adm_gid, $fai_release_file_name);
3341         
3342         # connect to packages_list_db
3343         #unlink($packages_list_file_name);
3344         unlink($packages_list_under_construction);
3345         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3346         chmod(0640, $packages_list_file_name);
3347         chown($root_uid, $adm_gid, $packages_list_file_name);
3348         
3349         # connect to messaging_db
3350         unlink($messaging_file_name);
3351         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3352         chmod(0640, $messaging_file_name);
3353         chown($root_uid, $adm_gid, $messaging_file_name);
3354     }
3358 # Creating tables
3359 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3360 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3361 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3362 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3363 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3364 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3365 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3366 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3367 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3368 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3370 # create xml object used for en/decrypting
3371 $xml = new XML::Simple();
3374 # foreign servers 
3375 my @foreign_server_list;
3377 # add foreign server from cfg file
3378 if ($foreign_server_string ne "") {
3379     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3380     foreach my $foreign_server (@cfg_foreign_server_list) {
3381         push(@foreign_server_list, $foreign_server);
3382     }
3384     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3387 # Perform a DNS lookup for server registration if flag is true
3388 if ($dns_lookup eq "true") {
3389     # Add foreign server from dns
3390     my @tmp_servers;
3391     if (not $server_domain) {
3392         # Try our DNS Searchlist
3393         for my $domain(get_dns_domains()) {
3394             chomp($domain);
3395             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3396             if(@$tmp_domains) {
3397                 for my $tmp_server(@$tmp_domains) {
3398                     push @tmp_servers, $tmp_server;
3399                 }
3400             }
3401         }
3402         if(@tmp_servers && length(@tmp_servers)==0) {
3403             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3404         }
3405     } else {
3406         @tmp_servers = &get_server_addresses($server_domain);
3407         if( 0 == @tmp_servers ) {
3408             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3409         }
3410     }
3412     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3414     foreach my $server (@tmp_servers) { 
3415         unshift(@foreign_server_list, $server); 
3416     }
3417 } else {
3418     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3422 # eliminate duplicate entries
3423 @foreign_server_list = &del_doubles(@foreign_server_list);
3424 my $all_foreign_server = join(", ", @foreign_server_list);
3425 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3427 # add all found foreign servers to known_server
3428 my $cur_timestamp = &get_time();
3429 foreach my $foreign_server (@foreign_server_list) {
3431         # do not add myself to known_server_db
3432         if (&is_local($foreign_server)) { next; }
3433         ######################################
3435     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3436             primkey=>['hostname'],
3437             hostname=>$foreign_server,
3438             macaddress=>"",
3439             status=>'not_yet_registered',
3440             hostkey=>"none",
3441             loaded_modules => "none", 
3442             timestamp=>$cur_timestamp,
3443             } );
3447 # Import all modules
3448 &import_modules;
3450 # Check wether all modules are gosa-si valid passwd check
3451 &password_check;
3453 # Prepare for using Opsi 
3454 if ($opsi_enabled eq "true") {
3455     use JSON::RPC::Client;
3456     use XML::Quote qw(:all);
3457     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3458     $opsi_client = new JSON::RPC::Client;
3462 POE::Component::Server::TCP->new(
3463         Alias => "TCP_SERVER",
3464         Port => $server_port,
3465         ClientInput => sub {
3466                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3467         my $session_id = $session->ID;
3468         my $remote_ip = $heap->{'remote_ip'};
3469                 push(@msgs_to_decrypt, $input);
3470         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3471                 $kernel->yield("msg_to_decrypt");
3472         },
3473         InlineStates => {
3474                 msg_to_decrypt => \&msg_to_decrypt,
3475                 next_task => \&next_task,
3476                 task_result => \&handle_task_result,
3477                 task_done   => \&handle_task_done,
3478                 task_debug  => \&handle_task_debug,
3479                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3480         }
3481 );
3483 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3485 # create session for repeatedly checking the job queue for jobs
3486 POE::Session->create(
3487         inline_states => {
3488                 _start => \&session_start,
3489         register_at_foreign_servers => \&register_at_foreign_servers,
3490         sig_handler => \&sig_handler,
3491         next_task => \&next_task,
3492         task_result => \&handle_task_result,
3493         task_done   => \&handle_task_done,
3494         task_debug  => \&handle_task_debug,
3495         watch_for_next_tasks => \&watch_for_next_tasks,
3496         watch_for_new_messages => \&watch_for_new_messages,
3497         watch_for_delivery_messages => \&watch_for_delivery_messages,
3498         watch_for_done_messages => \&watch_for_done_messages,
3499                 watch_for_new_jobs => \&watch_for_new_jobs,
3500         watch_for_modified_jobs => \&watch_for_modified_jobs,
3501         watch_for_done_jobs => \&watch_for_done_jobs,
3502         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3503         watch_for_old_known_clients => \&watch_for_old_known_clients,
3504         create_packages_list_db => \&run_create_packages_list_db,
3505         create_fai_server_db => \&run_create_fai_server_db,
3506         create_fai_release_db => \&run_create_fai_release_db,
3507                 recreate_packages_db => \&run_recreate_packages_db,
3508         session_run_result => \&session_run_result,
3509         session_run_debug => \&session_run_debug,
3510         session_run_done => \&session_run_done,
3511         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3512         }
3513 );
3516 POE::Kernel->run();
3517 exit;