Code

2b0c4c1a4b58ed042d8c7e41e44b6b2e01d87b86
[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 qw/:flock/;
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                         flock(LOG_HANDLE, LOCK_EX);
354                         seek(LOG_HANDLE, 0, 2);
355             print LOG_HANDLE $log_msg;
356                         flock(LOG_HANDLE, LOCK_UN);
357             if( $foreground ) { 
358                 print STDERR $log_msg;
359             }
360         }
361         close( LOG_HANDLE );
362     }
366 #===  FUNCTION  ================================================================
367 #         NAME:  check_cmdline_param
368 #   PARAMETERS:  nothing
369 #      RETURNS:  nothing
370 #  DESCRIPTION:  validates commandline parameter
371 #===============================================================================
372 sub check_cmdline_param () {
373     my $err_config;
374     my $err_counter = 0;
375         if(not defined($cfg_file)) {
376                 $cfg_file = "/etc/gosa-si/server.conf";
377                 if(! -r $cfg_file) {
378                         $err_config = "please specify a config file";
379                         $err_counter += 1;
380                 }
381     }
382     if( $err_counter > 0 ) {
383         &usage( "", 1 );
384         if( defined( $err_config)) { print STDERR "$err_config\n"}
385         print STDERR "\n";
386         exit( -1 );
387     }
391 #===  FUNCTION  ================================================================
392 #         NAME:  check_pid
393 #   PARAMETERS:  nothing
394 #      RETURNS:  nothing
395 #  DESCRIPTION:  handels pid processing
396 #===============================================================================
397 sub check_pid {
398     $pid = -1;
399     # Check, if we are already running
400     if( open(LOCK_FILE, "<$pid_file") ) {
401         $pid = <LOCK_FILE>;
402         if( defined $pid ) {
403             chomp( $pid );
404             if( -f "/proc/$pid/stat" ) {
405                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
406                 if( $stat ) {
407                                         daemon_log("ERROR: Already running",1);
408                     close( LOCK_FILE );
409                     exit -1;
410                 }
411             }
412         }
413         close( LOCK_FILE );
414         unlink( $pid_file );
415     }
417     # create a syslog msg if it is not to possible to open PID file
418     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
419         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
420         if (open(LOCK_FILE, '<', $pid_file)
421                 && ($pid = <LOCK_FILE>))
422         {
423             chomp($pid);
424             $msg .= "(PID $pid)\n";
425         } else {
426             $msg .= "(unable to read PID)\n";
427         }
428         if( ! ($foreground) ) {
429             openlog( $0, "cons,pid", "daemon" );
430             syslog( "warning", $msg );
431             closelog();
432         }
433         else {
434             print( STDERR " $msg " );
435         }
436         exit( -1 );
437     }
440 #===  FUNCTION  ================================================================
441 #         NAME:  import_modules
442 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
443 #                are stored
444 #      RETURNS:  nothing
445 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
446 #                state is on is imported by "require 'file';"
447 #===============================================================================
448 sub import_modules {
449     daemon_log(" ", 1);
451     if (not -e $modules_path) {
452         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
453     }
455     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
456     while (defined (my $file = readdir (DIR))) {
457         if (not $file =~ /(\S*?).pm$/) {
458             next;
459         }
460                 my $mod_name = $1;
462         # ArpHandler switch
463         if( $file =~ /ArpHandler.pm/ ) {
464             if( $arp_enabled eq "false" ) { next; }
465         }
466         
467         eval { require $file; };
468         if ($@) {
469             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
470             daemon_log("$@", 1);
471             exit;
472                 } else {
473                         my $info = eval($mod_name.'::get_module_info()');
474                         # Only load module if get_module_info() returns a non-null object
475                         if( $info ) {
476                                 my ($input_address, $input_key, $event_hash) = @{$info};
477                                 $known_modules->{$mod_name} = $info;
478                                 daemon_log("0 INFO: module $mod_name loaded", 5);
479                         }
480                 }
481     }   
483     close (DIR);
486 #===  FUNCTION  ================================================================
487 #         NAME:  password_check
488 #   PARAMETERS:  nothing
489 #      RETURNS:  nothing
490 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
491 #                the same password
492 #===============================================================================
493 sub password_check {
494     my $passwd_hash = {};
495     while (my ($mod_name, $mod_info) = each %$known_modules) {
496         my $mod_passwd = @$mod_info[1];
497         if (not defined $mod_passwd) { next; }
498         if (not exists $passwd_hash->{$mod_passwd}) {
499             $passwd_hash->{$mod_passwd} = $mod_name;
501         # escalates critical error
502         } else {
503             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
504             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
505             exit( -1 );
506         }
507     }
512 #===  FUNCTION  ================================================================
513 #         NAME:  sig_int_handler
514 #   PARAMETERS:  signal - string - signal arose from system
515 #      RETURNS:  nothing
516 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
517 #===============================================================================
518 sub sig_int_handler {
519     my ($signal) = @_;
521 #       if (defined($ldap_handle)) {
522 #               $ldap_handle->disconnect;
523 #       }
524     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
525     
527     daemon_log("shutting down gosa-si-server", 1);
528     system("kill `ps -C gosa-si-server -o pid=`");
530 $SIG{INT} = \&sig_int_handler;
533 sub check_key_and_xml_validity {
534     my ($crypted_msg, $module_key, $session_id) = @_;
535     my $msg;
536     my $msg_hash;
537     my $error_string;
538     eval{
539         $msg = &decrypt_msg($crypted_msg, $module_key);
541         if ($msg =~ /<xml>/i){
542             $msg =~ s/\s+/ /g;  # just for better daemon_log
543             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
544             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
546             ##############
547             # check header
548             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
549             my $header_l = $msg_hash->{'header'};
550             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
551             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
552             my $header = @{$header_l}[0];
553             if( 0 == length $header) { die 'empty string in header tag'; }
555             ##############
556             # check source
557             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
558             my $source_l = $msg_hash->{'source'};
559             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
560             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
561             my $source = @{$source_l}[0];
562             if( 0 == length $source) { die 'source error'; }
564             ##############
565             # check target
566             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
567             my $target_l = $msg_hash->{'target'};
568             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
569         }
570     };
571     if($@) {
572         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
573         $msg = undef;
574         $msg_hash = undef;
575     }
577     return ($msg, $msg_hash);
581 sub check_outgoing_xml_validity {
582     my ($msg, $session_id) = @_;
584     my $msg_hash;
585     eval{
586         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
588         ##############
589         # check header
590         my $header_l = $msg_hash->{'header'};
591         if( 1 != @{$header_l} ) {
592             die 'no or more than one headers specified';
593         }
594         my $header = @{$header_l}[0];
595         if( 0 == length $header) {
596             die 'header has length 0';
597         }
599         ##############
600         # check source
601         my $source_l = $msg_hash->{'source'};
602         if( 1 != @{$source_l} ) {
603             die 'no or more than 1 sources specified';
604         }
605         my $source = @{$source_l}[0];
606         if( 0 == length $source) {
607             die 'source has length 0';
608         }
610                                 # Check if source contains hostname instead of ip address
611                                 if(not $source =~ /^[a-z0-9\.]+:\d+$/i) {
612                                                 my ($hostname,$port) = split(/:/, $source);
613                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
614                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
615                                                         # Write ip address to $source variable
616                                                         $source = "$ip_address:$port";
617                                                 }
618                                 }
619         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
620                 $source =~ /^GOSA$/i) {
621             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
622         }
623         
624         ##############
625         # check target  
626         my $target_l = $msg_hash->{'target'};
627         if( 0 == @{$target_l} ) {
628             die "no targets specified";
629         }
630         foreach my $target (@$target_l) {
631             if( 0 == length $target) {
632                 die "target has length 0";
633             }
634             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
635                     $target =~ /^GOSA$/i ||
636                     $target =~ /^\*$/ ||
637                     $target =~ /KNOWN_SERVER/i ||
638                     $target =~ /JOBDB/i ||
639                     $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 ){
640                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
641             }
642         }
643     };
644     if($@) {
645         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
646         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
647         $msg_hash = undef;
648     }
650     return ($msg_hash);
654 sub input_from_known_server {
655     my ($input, $remote_ip, $session_id) = @_ ;  
656     my ($msg, $msg_hash, $module);
658     my $sql_statement= "SELECT * FROM known_server";
659     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
661     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
662         my $host_name = $hit->{hostname};
663         if( not $host_name =~ "^$remote_ip") {
664             next;
665         }
666         my $host_key = $hit->{hostkey};
667         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
668         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
670         # check if module can open msg envelope with module key
671         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
672         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
673             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
674             daemon_log("$@", 8);
675             next;
676         }
677         else {
678             $msg = $tmp_msg;
679             $msg_hash = $tmp_msg_hash;
680             $module = "ServerPackages";
681             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
682             last;
683         }
684     }
686     if( (!$msg) || (!$msg_hash) || (!$module) ) {
687         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
688     }
689   
690     return ($msg, $msg_hash, $module);
694 sub input_from_known_client {
695     my ($input, $remote_ip, $session_id) = @_ ;  
696     my ($msg, $msg_hash, $module);
698     my $sql_statement= "SELECT * FROM known_clients";
699     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
700     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
701         my $host_name = $hit->{hostname};
702         if( not $host_name =~ /^$remote_ip:\d*$/) {
703                 next;
704                 }
705         my $host_key = $hit->{hostkey};
706         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
707         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
709         # check if module can open msg envelope with module key
710         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
712         if( (!$msg) || (!$msg_hash) ) {
713             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
714             &daemon_log("$@", 8);
715             next;
716         }
717         else {
718             $module = "ClientPackages";
719             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
720             last;
721         }
722     }
724     if( (!$msg) || (!$msg_hash) || (!$module) ) {
725         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
726     }
728     return ($msg, $msg_hash, $module);
732 sub input_from_unknown_host {
733         no strict "refs";
734         my ($input, $session_id) = @_ ;
735         my ($msg, $msg_hash, $module);
736         my $error_string;
738         my %act_modules = %$known_modules;
740         while( my ($mod, $info) = each(%act_modules)) {
742                 # check a key exists for this module
743                 my $module_key = ${$mod."_key"};
744                 if( not defined $module_key ) {
745                         if( $mod eq 'ArpHandler' ) {
746                                 next;
747                         }
748                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
749                         next;
750                 }
751                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
753                 # check if module can open msg envelope with module key
754                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
755                 if( (not defined $msg) || (not defined $msg_hash) ) {
756                         next;
757                 } else {
758                         $module = $mod;
759             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
760                         last;
761                 }
762         }
764         if( (!$msg) || (!$msg_hash) || (!$module)) {
765                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
766         }
768         return ($msg, $msg_hash, $module);
772 sub create_ciphering {
773     my ($passwd) = @_;
774         if((!defined($passwd)) || length($passwd)==0) {
775                 $passwd = "";
776         }
777     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
778     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
779     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
780     $my_cipher->set_iv($iv);
781     return $my_cipher;
785 sub encrypt_msg {
786     my ($msg, $key) = @_;
787     my $my_cipher = &create_ciphering($key);
788     my $len;
789     {
790             use bytes;
791             $len= 16-length($msg)%16;
792     }
793     $msg = "\0"x($len).$msg;
794     $msg = $my_cipher->encrypt($msg);
795     chomp($msg = &encode_base64($msg));
796     # there are no newlines allowed inside msg
797     $msg=~ s/\n//g;
798     return $msg;
802 sub decrypt_msg {
804     my ($msg, $key) = @_ ;
805     $msg = &decode_base64($msg);
806     my $my_cipher = &create_ciphering($key);
807     $msg = $my_cipher->decrypt($msg); 
808     $msg =~ s/\0*//g;
809     return $msg;
813 sub get_encrypt_key {
814     my ($target) = @_ ;
815     my $encrypt_key;
816     my $error = 0;
818     # target can be in known_server
819     if( not defined $encrypt_key ) {
820         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
821         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
822         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
823             my $host_name = $hit->{hostname};
824             if( $host_name ne $target ) {
825                 next;
826             }
827             $encrypt_key = $hit->{hostkey};
828             last;
829         }
830     }
832     # target can be in known_client
833     if( not defined $encrypt_key ) {
834         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
835         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
836         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
837             my $host_name = $hit->{hostname};
838             if( $host_name ne $target ) {
839                 next;
840             }
841             $encrypt_key = $hit->{hostkey};
842             last;
843         }
844     }
846     return $encrypt_key;
850 #===  FUNCTION  ================================================================
851 #         NAME:  open_socket
852 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
853 #                [PeerPort] string necessary if port not appended by PeerAddr
854 #      RETURNS:  socket IO::Socket::INET
855 #  DESCRIPTION:  open a socket to PeerAddr
856 #===============================================================================
857 sub open_socket {
858     my ($PeerAddr, $PeerPort) = @_ ;
859     if(defined($PeerPort)){
860         $PeerAddr = $PeerAddr.":".$PeerPort;
861     }
862     my $socket;
863     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
864             Porto => "tcp",
865             Type => SOCK_STREAM,
866             Timeout => 5,
867             );
868     if(not defined $socket) {
869         return;
870     }
871 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
872     return $socket;
876 sub send_msg_to_target {
877     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
878     my $error = 0;
879     my $header;
880     my $timestamp = &get_time();
881     my $new_status;
882     my $act_status;
883     my ($sql_statement, $res);
884   
885     if( $msg_header ) {
886         $header = "'$msg_header'-";
887     } else {
888         $header = "";
889     }
891         # Patch the source ip
892         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
893                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
894                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
895         }
897     # encrypt xml msg
898     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
900     # opensocket
901     my $socket = &open_socket($address);
902     if( !$socket ) {
903         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
904         $error++;
905     }
906     
907     if( $error == 0 ) {
908         # send xml msg
909         print $socket $crypted_msg."\n";
911         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
912         daemon_log("$session_id DEBUG: message:\n$msg", 9);
913         
914     }
916     # close socket in any case
917     if( $socket ) {
918         close $socket;
919     }
921     if( $error > 0 ) { $new_status = "down"; }
922     else { $new_status = $msg_header; }
925     # known_clients
926     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
927     $res = $known_clients_db->select_dbentry($sql_statement);
928     if( keys(%$res) == 1) {
929         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
930         if ($act_status eq "down" && $new_status eq "down") {
931             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
932             $res = $known_clients_db->del_dbentry($sql_statement);
933             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
934         } else { 
935             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
936             $res = $known_clients_db->update_dbentry($sql_statement);
937             if($new_status eq "down"){
938                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
939             } else {
940                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
941             }
942         }
943     }
945     # known_server
946     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
947     $res = $known_server_db->select_dbentry($sql_statement);
948     if( keys(%$res) == 1) {
949         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
950         if ($act_status eq "down" && $new_status eq "down") {
951             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
952             $res = $known_server_db->del_dbentry($sql_statement);
953             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
954         } 
955         else { 
956             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
957             $res = $known_server_db->update_dbentry($sql_statement);
958             if($new_status eq "down"){
959                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
960             } else {
961                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
962             }
963         }
964     }
965     return $error; 
969 sub update_jobdb_status_for_send_msgs {
970     my ($session_id, $answer, $error) = @_;
971     &daemon_log("$session_id DEBUG: try to update job status", 7); 
972     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
973         my $jobdb_id = $1;
974     
975         $answer =~ /<header>(.*)<\/header>/;
976         my $job_header = $1;
978         $answer =~ /<target>(.*)<\/target>/;
979         my $job_target = $1;
980             
981         # Sending msg failed
982         if( $error ) {
984             # Set jobs to done, jobs do not need to deliver their message in any case
985             if (($job_header eq "trigger_action_localboot")
986                     ||($job_header eq "trigger_action_lock")
987                     ||($job_header eq "trigger_action_halt") 
988                     ) {
989                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
990                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
991                 my $res = $job_db->update_dbentry($sql_statement);
992                 
993             # Reactivate jobs, jobs need to deliver their message
994             } elsif (($job_header eq "trigger_action_activate")
995                     ||($job_header eq "trigger_action_update")
996                     ||($job_header eq "trigger_action_reinstall") 
997                     ||($job_header eq "trigger_activate_new")
998                     ) {
999                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1001             # For all other messages
1002             } else {
1003                 my $sql_statement = "UPDATE $job_queue_tn ".
1004                     "SET status='error', result='can not deliver msg, please consult log file' ".
1005                     "WHERE id=$jobdb_id";
1006                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1007                 my $res = $job_db->update_dbentry($sql_statement);
1008             }
1010         # Sending msg was successful
1011         } else {
1012             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1013             # jobs reinstall, update, inst_update do themself setting to done
1014             if (($job_header eq "trigger_action_localboot")
1015                     ||($job_header eq "trigger_action_lock")
1016                     ||($job_header eq "trigger_action_activate")
1017                     ||($job_header eq "trigger_action_halt") 
1018                     ||($job_header eq "trigger_action_reboot")
1019                     ||($job_header eq "trigger_action_wake")
1020                     ||($job_header eq "trigger_wake")
1021                     ) {
1023                 my $sql_statement = "UPDATE $job_queue_tn ".
1024                     "SET status='done' ".
1025                     "WHERE id=$jobdb_id AND status='processed'";
1026                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1027                 my $res = $job_db->update_dbentry($sql_statement);
1028             } else { 
1029                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1030             } 
1031         } 
1032     } else { 
1033         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1034     }
1037 sub reactivate_job_with_delay {
1038     my ($session_id, $target, $header, $delay) = @_ ;
1039     # 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
1040     
1041     if (not defined $delay) { $delay = 30 } ;
1042     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1044     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1045     my $res = $job_db->update_dbentry($sql);
1046     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1047             "cause client '$target' is currently not available", 5);
1048     daemon_log("$session_id $sql", 7);                             
1049     return;
1053 sub sig_handler {
1054         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1055         daemon_log("0 INFO got signal '$signal'", 1); 
1056         $kernel->sig_handled();
1057         return;
1061 sub msg_to_decrypt {
1062         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1063         my $session_id = $session->ID;
1064         my ($msg, $msg_hash, $module);
1065         my $error = 0;
1067         # fetch new msg out of @msgs_to_decrypt
1068         my $tmp_next_msg = shift @msgs_to_decrypt;
1069     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1071         # msg is from a new client or gosa
1072         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1074         # msg is from a gosa-si-server
1075         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1076                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1077         }
1078         # msg is from a gosa-si-client
1079         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1080                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1081         }
1082         # an error occurred
1083         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1084                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1085                 # could not understand a msg from its server the client cause a re-registering process
1086         my $remote_ip = $heap->{'remote_ip'};
1087         my $remote_port = $heap->{'remote_port'};
1088         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1089         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1091                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1092                         "' to cause a re-registering of the client if necessary", 3);
1093                 $error++;
1094         }
1097         my $header;
1098         my $target;
1099         my $source;
1100         my $done = 0;
1101         my $sql;
1102         my $res;
1104         # check whether this message should be processed here
1105         if ($error == 0) {
1106                 $header = @{$msg_hash->{'header'}}[0];
1107                 $target = @{$msg_hash->{'target'}}[0];
1108                 $source = @{$msg_hash->{'source'}}[0];
1109                 my $not_found_in_known_clients_db = 0;
1110                 my $not_found_in_known_server_db = 0;
1111                 my $not_found_in_foreign_clients_db = 0;
1112                 my $local_address;
1113                 my $local_mac;
1114                 my ($target_ip, $target_port) = split(':', $target);
1116                 # Determine the local ip address if target is an ip address
1117                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1118                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1119                 } else {
1120                         $local_address = $server_address;
1121                 }
1123                 # Determine the local mac address if target is a mac address
1124                 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) {
1125                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1126                         my $network_interface= &get_interface_for_ip($loc_ip);
1127                         $local_mac = &get_mac_for_interface($network_interface);
1128                 } else {
1129                         $local_mac = $server_mac_address;
1130                 }
1132                 # target and source is equal to GOSA -> process here
1133                 if (not $done) {
1134                         if ($target eq "GOSA" && $source eq "GOSA") {
1135                                 $done = 1;                    
1136                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1137                         }
1138                 }
1140                 # target is own address without forward_to_gosa-tag -> process here
1141                 if (not $done) {
1142                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1143                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1144                                 $done = 1;
1145                                 if ($source eq "GOSA") {
1146                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1147                                 }
1148                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1149                         }
1150                 }
1152                 # target is a client address in known_clients -> process here
1153                 if (not $done) {
1154                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1155                         $res = $known_clients_db->select_dbentry($sql);
1156                         if (keys(%$res) > 0) {
1157                                 $done = 1; 
1158                                 my $hostname = $res->{1}->{'hostname'};
1159                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1160                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1161                                 if ($source eq "GOSA") {
1162                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1163                                 }
1164                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1166                         } else {
1167                                 $not_found_in_known_clients_db = 1;
1168                         }
1169                 }
1171                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1172                 if (not $done) {
1173                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1174                         my $gosa_at;
1175                         my $gosa_session_id;
1176                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1177                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1178                                 if ($gosa_at ne $local_address) {
1179                                         $done = 1;
1180                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1181                                 }
1182                         }
1183                 }
1185                 # if message should be processed here -> add message to incoming_db
1186                 if ($done) {
1187                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1188                         # so gosa-si-server knows how to process this kind of messages
1189                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1190                                 $module = "GosaPackages";
1191                         }
1193                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1194                                         primkey=>[],
1195                                         headertag=>$header,
1196                                         targettag=>$target,
1197                                         xmlmessage=>&encode_base64($msg),
1198                                         timestamp=>&get_time,
1199                                         module=>$module,
1200                                         sessionid=>$session_id,
1201                                 } );
1203                 }
1205                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1206                 if (not $done) {
1207                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1208                         my $gosa_at;
1209                         my $gosa_session_id;
1210                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1211                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1212                                 if ($gosa_at eq $local_address) {
1213                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1214                                         if( defined $session_reference ) {
1215                                                 $heap = $session_reference->get_heap();
1216                                         }
1217                                         if(exists $heap->{'client'}) {
1218                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1219                                                 $heap->{'client'}->put($msg);
1220                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1221                                         }
1222                                         $done = 1;
1223                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1224                                 }
1225                         }
1227                 }
1229                 # target is a client address in foreign_clients -> forward to registration server
1230                 if (not $done) {
1231                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1232                         $res = $foreign_clients_db->select_dbentry($sql);
1233                         if (keys(%$res) > 0) {
1234                                 my $hostname = $res->{1}->{'hostname'};
1235                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1236                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1237                                 my $regserver = $res->{1}->{'regserver'};
1238                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1239                                 my $res = $known_server_db->select_dbentry($sql);
1240                                 if (keys(%$res) > 0) {
1241                                         my $regserver_key = $res->{1}->{'hostkey'};
1242                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1243                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1244                                         if ($source eq "GOSA") {
1245                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1246                                         }
1247                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1248                                 }
1249                                 $done = 1;
1250                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1251                         } else {
1252                                 $not_found_in_foreign_clients_db = 1;
1253                         }
1254                 }
1256                 # target is a server address -> forward to server
1257                 if (not $done) {
1258                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1259                         $res = $known_server_db->select_dbentry($sql);
1260                         if (keys(%$res) > 0) {
1261                                 my $hostkey = $res->{1}->{'hostkey'};
1263                                 if ($source eq "GOSA") {
1264                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1265                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1267                                 }
1269                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1270                                 $done = 1;
1271                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1272                         } else {
1273                                 $not_found_in_known_server_db = 1;
1274                         }
1275                 }
1278                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1279                 if ( $not_found_in_foreign_clients_db 
1280                         && $not_found_in_known_server_db
1281                         && $not_found_in_known_clients_db) {
1282                         &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);
1283             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1284                 $module = "GosaPackages"; 
1285             }
1286                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1287                                         primkey=>[],
1288                                         headertag=>$header,
1289                                         targettag=>$target,
1290                                         xmlmessage=>&encode_base64($msg),
1291                                         timestamp=>&get_time,
1292                                         module=>$module,
1293                                         sessionid=>$session_id,
1294                                 } );
1295                         $done = 1;
1296                 }
1299                 if (not $done) {
1300                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1301                         if ($source eq "GOSA") {
1302                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1303                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1305                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1306                                 if( defined $session_reference ) {
1307                                         $heap = $session_reference->get_heap();
1308                                 }
1309                                 if(exists $heap->{'client'}) {
1310                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1311                                         $heap->{'client'}->put($error_msg);
1312                                 }
1313                         }
1314                 }
1316         }
1318         return;
1322 sub next_task {
1323     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1324     my $running_task = POE::Wheel::Run->new(
1325             Program => sub { process_task($session, $heap, $task) },
1326             StdioFilter => POE::Filter::Reference->new(),
1327             StdoutEvent  => "task_result",
1328             StderrEvent  => "task_debug",
1329             CloseEvent   => "task_done",
1330             );
1331     $heap->{task}->{ $running_task->ID } = $running_task;
1334 sub handle_task_result {
1335     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1336     my $client_answer = $result->{'answer'};
1337     if( $client_answer =~ s/session_id=(\d+)$// ) {
1338         my $session_id = $1;
1339         if( defined $session_id ) {
1340             my $session_reference = $kernel->ID_id_to_session($session_id);
1341             if( defined $session_reference ) {
1342                 $heap = $session_reference->get_heap();
1343             }
1344         }
1346         if(exists $heap->{'client'}) {
1347             $heap->{'client'}->put($client_answer);
1348         }
1349     }
1350     $kernel->sig(CHLD => "child_reap");
1353 sub handle_task_debug {
1354     my $result = $_[ARG0];
1355     print STDERR "$result\n";
1358 sub handle_task_done {
1359     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1360     delete $heap->{task}->{$task_id};
1363 sub process_task {
1364     no strict "refs";
1365     #CHECK: Not @_[...]?
1366     my ($session, $heap, $task) = @_;
1367     my $error = 0;
1368     my $answer_l;
1369     my ($answer_header, @answer_target_l, $answer_source);
1370     my $client_answer = "";
1372     # prepare all variables needed to process message
1373     #my $msg = $task->{'xmlmessage'};
1374     my $msg = &decode_base64($task->{'xmlmessage'});
1375     my $incoming_id = $task->{'id'};
1376     my $module = $task->{'module'};
1377     my $header =  $task->{'headertag'};
1378     my $session_id = $task->{'sessionid'};
1379                 my $msg_hash;
1380                 eval {
1381         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1382                 }; 
1383                 daemon_log("ERROR: XML failure '$@'") if ($@);
1384     my $source = @{$msg_hash->{'source'}}[0];
1385     
1386     # set timestamp of incoming client uptodate, so client will not 
1387     # be deleted from known_clients because of expiration
1388     my $cur_time = &get_time();
1389     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1390     my $res = $known_clients_db->exec_statement($sql);
1392     ######################
1393     # process incoming msg
1394     if( $error == 0) {
1395         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1396         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1397         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1399         if ( 0 < @{$answer_l} ) {
1400             my $answer_str = join("\n", @{$answer_l});
1401             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1402                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1403             }
1404             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1405         } else {
1406             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1407         }
1409     }
1410     if( !$answer_l ) { $error++ };
1412     ########
1413     # answer
1414     if( $error == 0 ) {
1416         foreach my $answer ( @{$answer_l} ) {
1417             # check outgoing msg to xml validity
1418             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1419             if( not defined $answer_hash ) { next; }
1420             
1421             $answer_header = @{$answer_hash->{'header'}}[0];
1422             @answer_target_l = @{$answer_hash->{'target'}};
1423             $answer_source = @{$answer_hash->{'source'}}[0];
1425             # deliver msg to all targets 
1426             foreach my $answer_target ( @answer_target_l ) {
1428                 # targets of msg are all gosa-si-clients in known_clients_db
1429                 if( $answer_target eq "*" ) {
1430                     # answer is for all clients
1431                     my $sql_statement= "SELECT * FROM known_clients";
1432                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1433                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1434                         my $host_name = $hit->{hostname};
1435                         my $host_key = $hit->{hostkey};
1436                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1437                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1438                     }
1439                 }
1441                 # targets of msg are all gosa-si-server in known_server_db
1442                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1443                     # answer is for all server in known_server
1444                     my $sql_statement= "SELECT * FROM $known_server_tn";
1445                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1446                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1447                         my $host_name = $hit->{hostname};
1448                         my $host_key = $hit->{hostkey};
1449                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1450                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1451                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1452                     }
1453                 }
1455                 # target of msg is GOsa
1456                                 elsif( $answer_target eq "GOSA" ) {
1457                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1458                                         my $add_on = "";
1459                     if( defined $session_id ) {
1460                         $add_on = ".session_id=$session_id";
1461                     }
1462                     # answer is for GOSA and has to returned to connected client
1463                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1464                     $client_answer = $gosa_answer.$add_on;
1465                 }
1467                 # target of msg is job queue at this host
1468                 elsif( $answer_target eq "JOBDB") {
1469                     $answer =~ /<header>(\S+)<\/header>/;   
1470                     my $header;
1471                     if( defined $1 ) { $header = $1; }
1472                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1473                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1474                 }
1476                 # Target of msg is a mac address
1477                 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 ) {
1478                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1480                     # Looking for macaddress in known_clients
1481                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1482                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1483                     my $found_ip_flag = 0;
1484                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1485                         my $host_name = $hit->{hostname};
1486                         my $host_key = $hit->{hostkey};
1487                         $answer =~ s/$answer_target/$host_name/g;
1488                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1489                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1490                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1491                         $found_ip_flag++ ;
1492                     }   
1494                     # Looking for macaddress in foreign_clients
1495                     if ($found_ip_flag == 0) {
1496                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1497                         my $res = $foreign_clients_db->select_dbentry($sql);
1498                         while( my ($hit_num, $hit) = each %{ $res } ) {
1499                             my $host_name = $hit->{hostname};
1500                             my $reg_server = $hit->{regserver};
1501                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1502                             
1503                             # Fetch key for reg_server
1504                             my $reg_server_key;
1505                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1506                             my $res = $known_server_db->select_dbentry($sql);
1507                             if (exists $res->{1}) {
1508                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1509                             } else {
1510                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1511                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1512                                 $reg_server_key = undef;
1513                             }
1515                             # Send answer to server where client is registered
1516                             if (defined $reg_server_key) {
1517                                 $answer =~ s/$answer_target/$host_name/g;
1518                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1519                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1520                                 $found_ip_flag++ ;
1521                             }
1522                         }
1523                     }
1525                     # No mac to ip matching found
1526                     if( $found_ip_flag == 0) {
1527                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1528                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1529                     }
1531                 # Answer is for one specific host   
1532                 } else {
1533                     # get encrypt_key
1534                     my $encrypt_key = &get_encrypt_key($answer_target);
1535                     if( not defined $encrypt_key ) {
1536                         # unknown target
1537                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1538                         next;
1539                     }
1540                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1541                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1542                 }
1543             }
1544         }
1545     }
1547     my $filter = POE::Filter::Reference->new();
1548     my %result = ( 
1549             status => "seems ok to me",
1550             answer => $client_answer,
1551             );
1553     my $output = $filter->put( [ \%result ] );
1554     print @$output;
1559 sub session_start {
1560     my ($kernel) = $_[KERNEL];
1561     $global_kernel = $kernel;
1562     $kernel->yield('register_at_foreign_servers');
1563         $kernel->yield('create_fai_server_db', $fai_server_tn );
1564         $kernel->yield('create_fai_release_db', $fai_release_tn );
1565     $kernel->yield('watch_for_next_tasks');
1566         $kernel->sig(USR1 => "sig_handler");
1567         $kernel->sig(USR2 => "recreate_packages_db");
1568         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1569         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1570     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1571         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1572     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1573         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1574     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1576     # Start opsi check
1577     if ($opsi_enabled eq "true") {
1578         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1579     }
1584 sub watch_for_done_jobs {
1585         #CHECK: $heap for what?
1586         my ($kernel,$heap) = @_[KERNEL, HEAP];
1588         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1589         my $res = $job_db->select_dbentry( $sql_statement );
1591         while( my ($id, $hit) = each %{$res} ) {
1592                 my $jobdb_id = $hit->{id};
1593                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1594                 my $res = $job_db->del_dbentry($sql_statement); 
1595         }
1597         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1601 sub watch_for_opsi_jobs {
1602     my ($kernel) = $_[KERNEL];
1604     # 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 
1605     # opsi install job is to parse the xml message. There is still the correct header.
1606     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1607         my $res = $job_db->select_dbentry( $sql_statement );
1609     # Ask OPSI for an update of the running jobs
1610     while (my ($id, $hit) = each %$res ) {
1611         # Determine current parameters of the job
1612         my $hostId = $hit->{'plainname'};
1613         my $macaddress = $hit->{'macaddress'};
1614         my $progress = $hit->{'progress'};
1616         my $result= {};
1617         
1618         # For hosts, only return the products that are or get installed
1619         my $callobj;
1620         $callobj = {
1621             method  => 'getProductStates_hash',
1622             params  => [ $hostId ],
1623             id  => 1,
1624         };
1625         
1626         my $hres = $opsi_client->call($opsi_url, $callobj);
1627         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1628         if (not &check_opsi_res($hres)) {
1629             my $htmp= $hres->result->{$hostId};
1630         
1631             # Check state != not_installed or action == setup -> load and add
1632             my $products= 0;
1633             my $installed= 0;
1634             my $installing = 0;
1635             my $error= 0;  
1636             my @installed_list;
1637             my @error_list;
1638             my $act_status = "none";
1639             foreach my $product (@{$htmp}){
1641                 if ($product->{'installationStatus'} ne "not_installed" or
1642                         $product->{'actionRequest'} eq "setup"){
1644                     # Increase number of products for this host
1645                     $products++;
1646         
1647                     if ($product->{'installationStatus'} eq "failed"){
1648                         $result->{$product->{'productId'}}= "error";
1649                         unshift(@error_list, $product->{'productId'});
1650                         $error++;
1651                     }
1652                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1653                         $result->{$product->{'productId'}}= "installed";
1654                         unshift(@installed_list, $product->{'productId'});
1655                         $installed++;
1656                     }
1657                     if ($product->{'installationStatus'} eq "installing"){
1658                         $result->{$product->{'productId'}}= "installing";
1659                         $installing++;
1660                         $act_status = "installing - ".$product->{'productId'};
1661                     }
1662                 }
1663             }
1664         
1665             # Estimate "rough" progress, avoid division by zero
1666             if ($products == 0) {
1667                 $result->{'progress'}= 0;
1668             } else {
1669                 $result->{'progress'}= int($installed * 100 / $products);
1670             }
1672             # Set updates in job queue
1673             if ((not $error) && (not $installing) && ($installed)) {
1674                 $act_status = "installed - ".join(", ", @installed_list);
1675             }
1676             if ($error) {
1677                 $act_status = "error - ".join(", ", @error_list);
1678             }
1679             if ($progress ne $result->{'progress'} ) {
1680                 # Updating progress and result 
1681                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1682                 my $update_res = $job_db->update_dbentry($update_statement);
1683             }
1684             if ($progress eq 100) { 
1685                 # Updateing status
1686                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1687                 if ($error) {
1688                     $done_statement .= "status='error'";
1689                 } else {
1690                     $done_statement .= "status='done'";
1691                 }
1692                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1693                 my $done_res = $job_db->update_dbentry($done_statement);
1694             }
1697         }
1698     }
1700     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1704 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1705 sub watch_for_modified_jobs {
1706     my ($kernel,$heap) = @_[KERNEL, HEAP];
1708     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1709     my $res = $job_db->select_dbentry( $sql_statement );
1710     
1711     # if db contains no jobs which should be update, do nothing
1712     if (keys %$res != 0) {
1714         if ($job_synchronization  eq "true") {
1715             # make out of the db result a gosa-si message   
1716             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1717  
1718             # update all other SI-server
1719             &inform_all_other_si_server($update_msg);
1720         }
1722         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1723         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1724         $res = $job_db->update_dbentry($sql_statement);
1725     }
1727     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1731 sub watch_for_new_jobs {
1732         if($watch_for_new_jobs_in_progress == 0) {
1733                 $watch_for_new_jobs_in_progress = 1;
1734                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1736                 # check gosa job quaeue for jobs with executable timestamp
1737                 my $timestamp = &get_time();
1738                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1739                 my $res = $job_db->exec_statement( $sql_statement );
1741                 # Merge all new jobs that would do the same actions
1742                 my @drops;
1743                 my $hits;
1744                 foreach my $hit (reverse @{$res} ) {
1745                         my $macaddress= lc @{$hit}[8];
1746                         my $headertag= @{$hit}[5];
1747                         if(
1748                                 defined($hits->{$macaddress}) &&
1749                                 defined($hits->{$macaddress}->{$headertag}) &&
1750                                 defined($hits->{$macaddress}->{$headertag}[0])
1751                         ) {
1752                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1753                         }
1754                         $hits->{$macaddress}->{$headertag}= $hit;
1755                 }
1757                 # Delete new jobs with a matching job in state 'processing'
1758                 foreach my $macaddress (keys %{$hits}) {
1759                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1760                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1761                                 if(defined($jobdb_id)) {
1762                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1763                                         my $res = $job_db->exec_statement( $sql_statement );
1764                                         foreach my $hit (@{$res}) {
1765                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1766                                         }
1767                                 } else {
1768                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1769                                 }
1770                         }
1771                 }
1773                 # Commit deletion
1774                 $job_db->exec_statementlist(\@drops);
1776                 # Look for new jobs that could be executed
1777                 foreach my $macaddress (keys %{$hits}) {
1779                         # Look if there is an executing job
1780                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1781                         my $res = $job_db->exec_statement( $sql_statement );
1783                         # Skip new jobs for host if there is a processing job
1784                         if(defined($res) and defined @{$res}[0]) {
1785                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1786                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1787                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1788                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1789                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1790                                         if(defined($res_2) and defined @{$res_2}[0]) {
1791                                                 # Set status from goto-activation to 'waiting' and update timestamp
1792                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1793                                                 $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'");
1794                                         }
1795                                 }
1796                                 next;
1797                         }
1799                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1800                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1801                                 if(defined($jobdb_id)) {
1802                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1804                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1805                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1806                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1808                                         # expect macaddress is unique!!!!!!
1809                                         my $target = $res_hash->{1}->{hostname};
1811                                         # change header
1812                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1814                                         # add sqlite_id
1815                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1817                                         $job_msg =~ /<header>(\S+)<\/header>/;
1818                                         my $header = $1 ;
1819                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1821                                         # update status in job queue to ...
1822                     # ... 'processing', for jobs: 'reinstall', 'update'
1823                     if (($header =~ /gosa_trigger_action_reinstall/) 
1824                             || ($header =~ /gosa_trigger_activate_new/)
1825                             || ($header =~ /gosa_trigger_action_update/)) {
1826                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1827                         my $dbres = $job_db->update_dbentry($sql_statement);
1828                     }
1830                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1831                     else {
1832                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1833                         my $dbres = $job_db->update_dbentry($sql_statement);
1834                     }
1835                 
1837                                         # We don't want parallel processing
1838                                         last;
1839                                 }
1840                         }
1841                 }
1843                 $watch_for_new_jobs_in_progress = 0;
1844                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1845         }
1849 sub watch_for_new_messages {
1850     my ($kernel,$heap) = @_[KERNEL, HEAP];
1851     my @coll_user_msg;   # collection list of outgoing messages
1852     
1853     # check messaging_db for new incoming messages with executable timestamp
1854     my $timestamp = &get_time();
1855     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1856     my $res = $messaging_db->exec_statement( $sql_statement );
1857         foreach my $hit (@{$res}) {
1859         # create outgoing messages
1860         my $message_to = @{$hit}[3];
1861         # translate message_to to plain login name
1862         my @message_to_l = split(/,/, $message_to);  
1863                 my %receiver_h; 
1864                 foreach my $receiver (@message_to_l) {
1865                         if ($receiver =~ /^u_([\s\S]*)$/) {
1866                                 $receiver_h{$1} = 0;
1867                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1868                                 my $group_name = $1;
1869                                 # fetch all group members from ldap and add them to receiver hash
1870                                 my $ldap_handle = &get_ldap_handle();
1871                                 if (defined $ldap_handle) {
1872                                                 my $mesg = $ldap_handle->search(
1873                                                                                 base => $ldap_base,
1874                                                                                 scope => 'sub',
1875                                                                                 attrs => ['memberUid'],
1876                                                                                 filter => "cn=$group_name",
1877                                                                                 );
1878                                                 if ($mesg->count) {
1879                                                                 my @entries = $mesg->entries;
1880                                                                 foreach my $entry (@entries) {
1881                                                                                 my @receivers= $entry->get_value("memberUid");
1882                                                                                 foreach my $receiver (@receivers) { 
1883                                                                                                 $receiver_h{$receiver} = 0;
1884                                                                                 }
1885                                                                 }
1886                                                 } 
1887                                                 # translating errors ?
1888                                                 if ($mesg->code) {
1889                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1890                                                 }
1891                                 # ldap handle error ?           
1892                                 } else {
1893                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1894                                 }
1895                         } else {
1896                                 my $sbjct = &encode_base64(@{$hit}[1]);
1897                                 my $msg = &encode_base64(@{$hit}[7]);
1898                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1899                         }
1900                 }
1901                 my @receiver_l = keys(%receiver_h);
1903         my $message_id = @{$hit}[0];
1905         #add each outgoing msg to messaging_db
1906         my $receiver;
1907         foreach $receiver (@receiver_l) {
1908             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1909                 "VALUES ('".
1910                 $message_id."', '".    # id
1911                 @{$hit}[1]."', '".     # subject
1912                 @{$hit}[2]."', '".     # message_from
1913                 $receiver."', '".      # message_to
1914                 "none"."', '".         # flag
1915                 "out"."', '".          # direction
1916                 @{$hit}[6]."', '".     # delivery_time
1917                 @{$hit}[7]."', '".     # message
1918                 $timestamp."'".     # timestamp
1919                 ")";
1920             &daemon_log("M DEBUG: $sql_statement", 1);
1921             my $res = $messaging_db->exec_statement($sql_statement);
1922             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1923         }
1925         # set incoming message to flag d=deliverd
1926         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1927         &daemon_log("M DEBUG: $sql_statement", 7);
1928         $res = $messaging_db->update_dbentry($sql_statement);
1929         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1930     }
1932     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1933     return;
1936 sub watch_for_delivery_messages {
1937     my ($kernel, $heap) = @_[KERNEL, HEAP];
1939     # select outgoing messages
1940     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1941     #&daemon_log("0 DEBUG: $sql", 7);
1942     my $res = $messaging_db->exec_statement( $sql_statement );
1943     
1944     # build out msg for each    usr
1945     foreach my $hit (@{$res}) {
1946         my $receiver = @{$hit}[3];
1947         my $msg_id = @{$hit}[0];
1948         my $subject = @{$hit}[1];
1949         my $message = @{$hit}[7];
1951         # resolve usr -> host where usr is logged in
1952         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1953         #&daemon_log("0 DEBUG: $sql", 7);
1954         my $res = $login_users_db->exec_statement($sql);
1956         # receiver is logged in nowhere
1957         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1959         # receiver ist logged in at a client registered at local server
1960                 my $send_succeed = 0;
1961                 foreach my $hit (@$res) {
1962                                 my $receiver_host = @$hit[0];
1963                 my $delivered2host = 0;
1964                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1966                                 # Looking for host in know_clients_db 
1967                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1968                                 my $res = $known_clients_db->exec_statement($sql);
1970                 # Host is known in known_clients_db
1971                 if (ref(@$res[0]) eq "ARRAY") {
1972                     my $receiver_key = @{@{$res}[0]}[2];
1973                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1974                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1975                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1976                     if ($error == 0 ) {
1977                         $send_succeed++ ;
1978                         $delivered2host++ ;
1979                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1980                     } else {
1981                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1982                     }
1983                 }
1984                 
1985                 # Message already send, do not need to do anything more, otherwise ...
1986                 if ($delivered2host) { next;}
1987     
1988                 # ...looking for host in foreign_clients_db
1989                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
1990                 $res = $foreign_clients_db->exec_statement($sql);
1991   
1992                                 # Host is known in foreign_clients_db 
1993                                 if (ref(@$res[0]) eq "ARRAY") { 
1994                     my $registration_server = @{@{$res}[0]}[2];
1995                     
1996                     # Fetch encryption key for registration server
1997                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
1998                     my $res = $known_server_db->exec_statement($sql);
1999                     if (ref(@$res[0]) eq "ARRAY") { 
2000                         my $registration_server_key = @{@{$res}[0]}[3];
2001                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2002                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2003                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2004                         if ($error == 0 ) {
2005                             $send_succeed++ ;
2006                             $delivered2host++ ;
2007                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2008                         } else {
2009                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2010                         }
2012                     } else {
2013                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2014                                 "registrated at server '$registration_server', ".
2015                                 "but no data available in known_server_db ", 1); 
2016                     }
2017                 }
2018                 
2019                 if (not $delivered2host) {
2020                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2021                 }
2022                 }
2024                 if ($send_succeed) {
2025                                 # set outgoing msg at db to deliverd
2026                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2027                                 my $res = $messaging_db->exec_statement($sql); 
2028                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2029                 } else {
2030             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2031         }
2032         }
2034     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2035     return;
2039 sub watch_for_done_messages {
2040     my ($kernel,$heap) = @_[KERNEL, HEAP];
2042     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2043     #&daemon_log("0 DEBUG: $sql", 7);
2044     my $res = $messaging_db->exec_statement($sql); 
2046     foreach my $hit (@{$res}) {
2047         my $msg_id = @{$hit}[0];
2049         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2050         #&daemon_log("0 DEBUG: $sql", 7); 
2051         my $res = $messaging_db->exec_statement($sql);
2053         # not all usr msgs have been seen till now
2054         if ( ref(@$res[0]) eq "ARRAY") { next; }
2055         
2056         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2057         #&daemon_log("0 DEBUG: $sql", 7);
2058         $res = $messaging_db->exec_statement($sql);
2059     
2060     }
2062     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2063     return;
2067 sub watch_for_old_known_clients {
2068     my ($kernel,$heap) = @_[KERNEL, HEAP];
2070     my $sql_statement = "SELECT * FROM $known_clients_tn";
2071     my $res = $known_clients_db->select_dbentry( $sql_statement );
2073     my $cur_time = int(&get_time());
2075     while ( my ($hit_num, $hit) = each %$res) {
2076         my $expired_timestamp = int($hit->{'timestamp'});
2077         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2078         my $dt = DateTime->new( year   => $1,
2079                 month  => $2,
2080                 day    => $3,
2081                 hour   => $4,
2082                 minute => $5,
2083                 second => $6,
2084                 );
2086         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2087         $expired_timestamp = $dt->ymd('').$dt->hms('');
2088         if ($cur_time > $expired_timestamp) {
2089             my $hostname = $hit->{'hostname'};
2090             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2091             my $del_res = $known_clients_db->exec_statement($del_sql);
2093             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2094         }
2096     }
2098     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2102 sub watch_for_next_tasks {
2103     my ($kernel,$heap) = @_[KERNEL, HEAP];
2105     my $sql = "SELECT * FROM $incoming_tn";
2106     my $res = $incoming_db->select_dbentry($sql);
2107     
2108     while ( my ($hit_num, $hit) = each %$res) {
2109         my $headertag = $hit->{'headertag'};
2110         if ($headertag =~ /^answer_(\d+)/) {
2111             # do not start processing, this message is for a still running POE::Wheel
2112             next;
2113         }
2114         my $message_id = $hit->{'id'};
2115         my $session_id = $hit->{'sessionid'};
2116         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2117         $kernel->yield('next_task', $hit);
2119         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2120         my $res = $incoming_db->exec_statement($sql);
2121     }
2123     $kernel->delay_set('watch_for_next_tasks', 1); 
2127 sub get_ldap_handle {
2128         my ($session_id) = @_;
2129         my $heap;
2130         my $ldap_handle;
2132         if (not defined $session_id ) { $session_id = 0 };
2133         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2135         if ($session_id == 0) {
2136                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
2137                 $ldap_handle = Net::LDAP->new( $ldap_uri );
2138                 if (defined $ldap_handle) {
2139                         $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!"); 
2140                 } else {
2141                         daemon_log("$session_id ERROR: creation of a new LDAP handle failed (ldap_uri '$ldap_uri')");
2142                 }
2144         } else {
2145                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2146                 if( defined $session_reference ) {
2147                         $heap = $session_reference->get_heap();
2148                 }
2150                 if (not defined $heap) {
2151                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
2152                         return;
2153                 }
2155                 # TODO: This "if" is nonsense, because it doesn't prove that the
2156                 #       used handle is still valid - or if we've to reconnect...
2157                 #if (not exists $heap->{ldap_handle}) {
2158                         $ldap_handle = Net::LDAP->new( $ldap_uri );
2159                         $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!"); 
2160                         $heap->{ldap_handle} = $ldap_handle;
2161                 #}
2162         }
2163         return $ldap_handle;
2167 sub change_fai_state {
2168     my ($st, $targets, $session_id) = @_;
2169     $session_id = 0 if not defined $session_id;
2170     # Set FAI state to localboot
2171     my %mapActions= (
2172         reboot    => '',
2173         update    => 'softupdate',
2174         localboot => 'localboot',
2175         reinstall => 'install',
2176         rescan    => '',
2177         wake      => '',
2178         memcheck  => 'memcheck',
2179         sysinfo   => 'sysinfo',
2180         install   => 'install',
2181     );
2183     # Return if this is unknown
2184     if (!exists $mapActions{ $st }){
2185         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2186       return;
2187     }
2189     my $state= $mapActions{ $st };
2191     my $ldap_handle = &get_ldap_handle($session_id);
2192     if( defined($ldap_handle) ) {
2194       # Build search filter for hosts
2195         my $search= "(&(objectClass=GOhard)";
2196         foreach (@{$targets}){
2197             $search.= "(macAddress=$_)";
2198         }
2199         $search.= ")";
2201       # If there's any host inside of the search string, procress them
2202         if (!($search =~ /macAddress/)){
2203             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2204             return;
2205         }
2207       # Perform search for Unit Tag
2208       my $mesg = $ldap_handle->search(
2209           base   => $ldap_base,
2210           scope  => 'sub',
2211           attrs  => ['dn', 'FAIstate', 'objectClass'],
2212           filter => "$search"
2213           );
2215           if ($mesg->count) {
2216                   my @entries = $mesg->entries;
2217                   if (0 == @entries) {
2218                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2219                   }
2221                   foreach my $entry (@entries) {
2222                           # Only modify entry if it is not set to '$state'
2223                           if ($entry->get_value("FAIstate") ne "$state"){
2224                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2225                                   my $result;
2226                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2227                                   if (exists $tmp{'FAIobject'}){
2228                                           if ($state eq ''){
2229                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2230                                           } else {
2231                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2232                                           }
2233                                   } elsif ($state ne ''){
2234                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2235                                   }
2237                                   # Errors?
2238                                   if ($result->code){
2239                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2240                                   }
2241                           } else {
2242                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2243                           }  
2244                   }
2245           } else {
2246                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2247           }
2249     # if no ldap handle defined
2250     } else {
2251         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2252     }
2254         return;
2258 sub change_goto_state {
2259     my ($st, $targets, $session_id) = @_;
2260     $session_id = 0  if not defined $session_id;
2262     # Switch on or off?
2263     my $state= $st eq 'active' ? 'active': 'locked';
2265     my $ldap_handle = &get_ldap_handle($session_id);
2266     if( defined($ldap_handle) ) {
2268       # Build search filter for hosts
2269       my $search= "(&(objectClass=GOhard)";
2270       foreach (@{$targets}){
2271         $search.= "(macAddress=$_)";
2272       }
2273       $search.= ")";
2275       # If there's any host inside of the search string, procress them
2276       if (!($search =~ /macAddress/)){
2277         return;
2278       }
2280       # Perform search for Unit Tag
2281       my $mesg = $ldap_handle->search(
2282           base   => $ldap_base,
2283           scope  => 'sub',
2284           attrs  => ['dn', 'gotoMode'],
2285           filter => "$search"
2286           );
2288       if ($mesg->count) {
2289         my @entries = $mesg->entries;
2290         foreach my $entry (@entries) {
2292           # Only modify entry if it is not set to '$state'
2293           if ($entry->get_value("gotoMode") ne $state){
2295             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2296             my $result;
2297             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2299             # Errors?
2300             if ($result->code){
2301               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2302             }
2304           }
2305         }
2306       } else {
2307                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2308           }
2310     }
2314 sub run_recreate_packages_db {
2315     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2316     my $session_id = $session->ID;
2317         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2318         $kernel->yield('create_fai_release_db', $fai_release_tn);
2319         $kernel->yield('create_fai_server_db', $fai_server_tn);
2320         return;
2324 sub run_create_fai_server_db {
2325     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2326     my $session_id = $session->ID;
2327     my $task = POE::Wheel::Run->new(
2328             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2329             StdoutEvent  => "session_run_result",
2330             StderrEvent  => "session_run_debug",
2331             CloseEvent   => "session_run_done",
2332             );
2334     $heap->{task}->{ $task->ID } = $task;
2335     return;
2339 sub create_fai_server_db {
2340         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2341         my $result;
2343         if (not defined $session_id) { $session_id = 0; }
2344         my $ldap_handle = &get_ldap_handle();
2345         if(defined($ldap_handle)) {
2346                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2347                 my $mesg= $ldap_handle->search(
2348                         base   => $ldap_base,
2349                         scope  => 'sub',
2350                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2351                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2352                 );
2353                 if($mesg->{'resultCode'} == 0 &&
2354                         $mesg->count != 0) {
2355                         foreach my $entry (@{$mesg->{entries}}) {
2356                                 if($entry->exists('FAIrepository')) {
2357                                         # Add an entry for each Repository configured for server
2358                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2359                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2360                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2361                                                 $result= $fai_server_db->add_dbentry( { 
2362                                                                 table => $table_name,
2363                                                                 primkey => ['server', 'fai_release', 'tag'],
2364                                                                 server => $tmp_url,
2365                                                                 fai_release => $tmp_release,
2366                                                                 sections => $tmp_sections,
2367                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2368                                                         } );
2369                                         }
2370                                 }
2371                         }
2372                 }
2373                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2375                 # TODO: Find a way to post the 'create_packages_list_db' event
2376                 if(not defined($dont_create_packages_list)) {
2377                         &create_packages_list_db(undef, undef, $session_id);
2378                 }
2379         }       
2381         $ldap_handle->disconnect;
2382         return $result;
2386 sub run_create_fai_release_db {
2387         my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2388         my $session_id = $session->ID;
2389         my $task = POE::Wheel::Run->new(
2390                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2391                 StdoutEvent  => "session_run_result",
2392                 StderrEvent  => "session_run_debug",
2393                 CloseEvent   => "session_run_done",
2394         );
2396         $heap->{task}->{ $task->ID } = $task;
2397         return;
2401 sub create_fai_release_db {
2402         my ($table_name, $session_id) = @_;
2403         my $result;
2405         # used for logging
2406         if (not defined $session_id) { $session_id = 0; }
2408         my $ldap_handle = &get_ldap_handle();
2409         if(defined($ldap_handle)) {
2410                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2411                 my $mesg= $ldap_handle->search(
2412                         base   => $ldap_base,
2413                         scope  => 'sub',
2414                         attrs  => [],
2415                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2416                 );
2417                 if(($mesg->code == 0) && ($mesg->count != 0))
2418                 {
2419                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2421                         # Walk through all possible FAI container ou's
2422                         my @sql_list;
2423                         my $timestamp= &get_time();
2424                         foreach my $ou (@{$mesg->{entries}}) {
2425                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2426                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2427                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2428                                         if(@tmp_array) {
2429                                                 foreach my $entry (@tmp_array) {
2430                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2431                                                                 my $sql= 
2432                                                                 "INSERT INTO $table_name "
2433                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2434                                                                 .$timestamp.","
2435                                                                 ."'".$entry->{'release'}."',"
2436                                                                 ."'".$entry->{'class'}."',"
2437                                                                 ."'".$entry->{'type'}."',"
2438                                                                 ."'".$entry->{'state'}."')";
2439                                                                 push @sql_list, $sql;
2440                                                         }
2441                                                 }
2442                                         }
2443                                 }
2444                         }
2446                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2447                         if(@sql_list) {
2448                                 unshift @sql_list, "VACUUM";
2449                                 unshift @sql_list, "DELETE FROM $table_name";
2450                                 $fai_release_db->exec_statementlist(\@sql_list);
2451                         }
2452                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2453                 } else {
2454                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2455                 }
2456                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2457         }
2458         $ldap_handle->disconnect;
2459         return $result;
2462 sub get_fai_types {
2463         my $tmp_classes = shift || return undef;
2464         my @result;
2466         foreach my $type(keys %{$tmp_classes}) {
2467                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2468                         my $entry = {
2469                                 type => $type,
2470                                 state => $tmp_classes->{$type}[0],
2471                         };
2472                         push @result, $entry;
2473                 }
2474         }
2476         return @result;
2479 sub get_fai_state {
2480         my $result = "";
2481         my $tmp_classes = shift || return $result;
2483         foreach my $type(keys %{$tmp_classes}) {
2484                 if(defined($tmp_classes->{$type}[0])) {
2485                         $result = $tmp_classes->{$type}[0];
2486                         
2487                 # State is equal for all types in class
2488                         last;
2489                 }
2490         }
2492         return $result;
2495 sub resolve_fai_classes {
2496         my ($fai_base, $ldap_handle, $session_id) = @_;
2497         if (not defined $session_id) { $session_id = 0; }
2498         my $result;
2499         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2500         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2501         my $fai_classes;
2503         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2504         my $mesg= $ldap_handle->search(
2505                 base   => $fai_base,
2506                 scope  => 'sub',
2507                 attrs  => ['cn','objectClass','FAIstate'],
2508                 filter => $fai_filter,
2509         );
2510         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2512         if($mesg->{'resultCode'} == 0 &&
2513                 $mesg->count != 0) {
2514                 foreach my $entry (@{$mesg->{entries}}) {
2515                         if($entry->exists('cn')) {
2516                                 my $tmp_dn= $entry->dn();
2517                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2518                                         - length($fai_base) - 1 );
2520                                 # Skip classname and ou dn parts for class
2521                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2523                                 # Skip classes without releases
2524                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2525                                         next;
2526                                 }
2528                                 my $tmp_cn= $entry->get_value('cn');
2529                                 my $tmp_state= $entry->get_value('FAIstate');
2531                                 my $tmp_type;
2532                                 # Get FAI type
2533                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2534                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2535                                                 $tmp_type= $oclass;
2536                                                 last;
2537                                         }
2538                                 }
2540                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2541                                         # A Subrelease
2542                                         my @sub_releases = split(/,/, $tmp_release);
2544                                         # Walk through subreleases and build hash tree
2545                                         my $hash;
2546                                         while(my $tmp_sub_release = pop @sub_releases) {
2547                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2548                                         }
2549                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2550                                 } else {
2551                                         # A branch, no subrelease
2552                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2553                                 }
2554                         } elsif (!$entry->exists('cn')) {
2555                                 my $tmp_dn= $entry->dn();
2556                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2557                                         - length($fai_base) - 1 );
2558                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2560                                 # Skip classes without releases
2561                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2562                                         next;
2563                                 }
2565                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2566                                         # A Subrelease
2567                                         my @sub_releases= split(/,/, $tmp_release);
2569                                         # Walk through subreleases and build hash tree
2570                                         my $hash;
2571                                         while(my $tmp_sub_release = pop @sub_releases) {
2572                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2573                                         }
2574                                         # Remove the last two characters
2575                                         chop($hash);
2576                                         chop($hash);
2578                                         eval('$fai_classes->'.$hash.'= {}');
2579                                 } else {
2580                                         # A branch, no subrelease
2581                                         if(!exists($fai_classes->{$tmp_release})) {
2582                                                 $fai_classes->{$tmp_release} = {};
2583                                         }
2584                                 }
2585                         }
2586                 }
2588                 # The hash is complete, now we can honor the copy-on-write based missing entries
2589                 foreach my $release (keys %$fai_classes) {
2590                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2591                 }
2592         }
2593         return $result;
2596 sub apply_fai_inheritance {
2597        my $fai_classes = shift || return {};
2598        my $tmp_classes;
2600        # Get the classes from the branch
2601        foreach my $class (keys %{$fai_classes}) {
2602                # Skip subreleases
2603                if($class =~ /^ou=.*$/) {
2604                        next;
2605                } else {
2606                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2607                }
2608        }
2610        # Apply to each subrelease
2611        foreach my $subrelease (keys %{$fai_classes}) {
2612                if($subrelease =~ /ou=/) {
2613                        foreach my $tmp_class (keys %{$tmp_classes}) {
2614                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2615                                        $fai_classes->{$subrelease}->{$tmp_class} =
2616                                        deep_copy($tmp_classes->{$tmp_class});
2617                                } else {
2618                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2619                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2620                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2621                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2622                                                }
2623                                        }
2624                                }
2625                        }
2626                }
2627        }
2629        # Find subreleases in deeper levels
2630        foreach my $subrelease (keys %{$fai_classes}) {
2631                if($subrelease =~ /ou=/) {
2632                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2633                                if($subsubrelease =~ /ou=/) {
2634                                        apply_fai_inheritance($fai_classes->{$subrelease});
2635                                }
2636                        }
2637                }
2638        }
2640        return $fai_classes;
2643 sub get_fai_release_entries {
2644         my $tmp_classes = shift || return;
2645         my $parent = shift || "";
2646         my @result = shift || ();
2648         foreach my $entry (keys %{$tmp_classes}) {
2649                 if(defined($entry)) {
2650                         if($entry =~ /^ou=.*$/) {
2651                                 my $release_name = $entry;
2652                                 $release_name =~ s/ou=//g;
2653                                 if(length($parent)>0) {
2654                                         $release_name = $parent."/".$release_name;
2655                                 }
2656                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2657                                 foreach my $bufentry(@bufentries) {
2658                                         push @result, $bufentry;
2659                                 }
2660                         } else {
2661                                 my @types = get_fai_types($tmp_classes->{$entry});
2662                                 foreach my $type (@types) {
2663                                         push @result, 
2664                                         {
2665                                                 'class' => $entry,
2666                                                 'type' => $type->{'type'},
2667                                                 'release' => $parent,
2668                                                 'state' => $type->{'state'},
2669                                         };
2670                                 }
2671                         }
2672                 }
2673         }
2675         return @result;
2678 sub deep_copy {
2679         my $this = shift;
2680         if (not ref $this) {
2681                 $this;
2682         } elsif (ref $this eq "ARRAY") {
2683                 [map deep_copy($_), @$this];
2684         } elsif (ref $this eq "HASH") {
2685                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2686         } else { die "what type is $_?" }
2690 sub session_run_result {
2691     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2692     $kernel->sig(CHLD => "child_reap");
2695 sub session_run_debug {
2696     my $result = $_[ARG0];
2697     print STDERR "$result\n";
2700 sub session_run_done {
2701     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2702     delete $heap->{task}->{$task_id};
2706 sub create_sources_list {
2707         my $session_id = shift;
2708         my $ldap_handle = &main::get_ldap_handle;
2709         my $result="/tmp/gosa_si_tmp_sources_list";
2711         # Remove old file
2712         if(stat($result)) {
2713                 unlink($result);
2714                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2715         }
2717         my $fh;
2718         open($fh, ">$result");
2719         if (not defined $fh) {
2720                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2721                 return undef;
2722         }
2723         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2724                 my $mesg=$ldap_handle->search(
2725                         base    => $main::ldap_server_dn,
2726                         scope   => 'base',
2727                         attrs   => 'FAIrepository',
2728                         filter  => 'objectClass=FAIrepositoryServer'
2729                 );
2730                 if($mesg->count) {
2731                         foreach my $entry(@{$mesg->{'entries'}}) {
2732                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2733                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2734                                         my $line = "deb $server $release";
2735                                         $sections =~ s/,/ /g;
2736                                         $line.= " $sections";
2737                                         print $fh $line."\n";
2738                                 }
2739                         }
2740                 }
2741         } else {
2742                 if (defined $main::ldap_server_dn){
2743                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2744                 } else {
2745                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2746                 }
2747         }
2748         close($fh);
2750         return $result;
2754 sub run_create_packages_list_db {
2755     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2756         my $session_id = $session->ID;
2758         my $task = POE::Wheel::Run->new(
2759                                         Priority => +20,
2760                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2761                                         StdoutEvent  => "session_run_result",
2762                                         StderrEvent  => "session_run_debug",
2763                                         CloseEvent   => "session_run_done",
2764                                         );
2765         $heap->{task}->{ $task->ID } = $task;
2769 sub create_packages_list_db {
2770         my ($ldap_handle, $sources_file, $session_id) = @_;
2771         
2772         # it should not be possible to trigger a recreation of packages_list_db
2773         # while packages_list_db is under construction, so set flag packages_list_under_construction
2774         # which is tested befor recreation can be started
2775         if (-r $packages_list_under_construction) {
2776                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2777                 return;
2778         } else {
2779                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2780                 # set packages_list_under_construction to true
2781                 system("touch $packages_list_under_construction");
2782                 @packages_list_statements=();
2783         }
2785         if (not defined $session_id) { $session_id = 0; }
2786         if (not defined $ldap_handle) { 
2787                 $ldap_handle= &get_ldap_handle();
2789                 if (not defined $ldap_handle) {
2790                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2791                         unlink($packages_list_under_construction);
2792                         return;
2793                 }
2794         }
2795         if (not defined $sources_file) { 
2796                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2797                 $sources_file = &create_sources_list($session_id);
2798         }
2800         if (not defined $sources_file) {
2801                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2802                 unlink($packages_list_under_construction);
2803                 return;
2804         }
2806         my $line;
2808         open(CONFIG, "<$sources_file") or do {
2809                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2810                 unlink($packages_list_under_construction);
2811                 return;
2812         };
2814         # Read lines
2815         while ($line = <CONFIG>){
2816                 # Unify
2817                 chop($line);
2818                 $line =~ s/^\s+//;
2819                 $line =~ s/^\s+/ /;
2821                 # Strip comments
2822                 $line =~ s/#.*$//g;
2824                 # Skip empty lines
2825                 if ($line =~ /^\s*$/){
2826                         next;
2827                 }
2829                 # Interpret deb line
2830                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2831                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2832                         my $section;
2833                         foreach $section (split(' ', $sections)){
2834                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2835                         }
2836                 }
2837         }
2839         close (CONFIG);
2841         if(keys(%repo_dirs)) {
2842                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2843                 &main::strip_packages_list_statements();
2844                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2845         }
2846         unlink($packages_list_under_construction);
2847         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2848         return;
2851 # This function should do some intensive task to minimize the db-traffic
2852 sub strip_packages_list_statements {
2853         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2854         my @new_statement_list=();
2855         my $hash;
2856         my $insert_hash;
2857         my $update_hash;
2858         my $delete_hash;
2859         my $known_packages_hash;
2860         my $local_timestamp=get_time();
2862         foreach my $existing_entry (@existing_entries) {
2863                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2864         }
2866         foreach my $statement (@packages_list_statements) {
2867                 if($statement =~ /^INSERT/i) {
2868                         # Assign the values from the insert statement
2869                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2870                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2871                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2872                                 # If section or description has changed, update the DB
2873                                 if( 
2874                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2875                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2876                                 ) {
2877                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2878                                 } else {
2879                                         # package is already present in database. cache this knowledge for later use
2880                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2881                                 }
2882                         } else {
2883                                 # Insert a non-existing entry to db
2884                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2885                         }
2886                 } elsif ($statement =~ /^UPDATE/i) {
2887                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2888                         /^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;
2889                         foreach my $distribution (keys %{$hash}) {
2890                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2891                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2892                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2893                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2894                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2895                                                 my $section;
2896                                                 my $description;
2897                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2898                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2899                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2900                                                 }
2901                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2902                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2903                                                 }
2904                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2905                                         }
2906                                 }
2907                         }
2908                 }
2909         }
2911         # Check for orphaned entries
2912         foreach my $existing_entry (@existing_entries) {
2913                 my $distribution= @{$existing_entry}[0];
2914                 my $package= @{$existing_entry}[1];
2915                 my $version= @{$existing_entry}[2];
2916                 my $section= @{$existing_entry}[3];
2918                 if(
2919                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2920                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2921                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2922                 ) {
2923                         next;
2924                 } else {
2925                         # Insert entry to delete hash
2926                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2927                 }
2928         }
2930         # unroll the insert hash
2931         foreach my $distribution (keys %{$insert_hash}) {
2932                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2933                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2934                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2935                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2936                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2937                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2938                                 ."'$local_timestamp')";
2939                         }
2940                 }
2941         }
2943         # unroll the update hash
2944         foreach my $distribution (keys %{$update_hash}) {
2945                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2946                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2947                                 my $set = "";
2948                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2949                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2950                                 }
2951                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2952                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2953                                 }
2954                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2955                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2956                                 }
2957                                 if(defined($set) and length($set) > 0) {
2958                                         $set .= "timestamp = '$local_timestamp'";
2959                                 } else {
2960                                         next;
2961                                 }
2962                                 push @new_statement_list, 
2963                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2964                                 ." distribution = '$distribution'"
2965                                 ." AND package = '$package'"
2966                                 ." AND version = '$version'";
2967                         }
2968                 }
2969         }
2970         
2971         # unroll the delete hash
2972         foreach my $distribution (keys %{$delete_hash}) {
2973                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2974                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2975                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2976                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2977                         }
2978                 }
2979         }
2981         unshift(@new_statement_list, "VACUUM");
2983         @packages_list_statements = @new_statement_list;
2987 sub parse_package_info {
2988     my ($baseurl, $dist, $section, $session_id)= @_;
2989     my ($package);
2990     if (not defined $session_id) { $session_id = 0; }
2991     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2992     $repo_dirs{ "${repo_path}/pool" } = 1;
2994     foreach $package ("Packages.gz"){
2995         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2996         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2997         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2998     }
2999     
3003 sub get_package {
3004     my ($url, $dest, $session_id)= @_;
3005     if (not defined $session_id) { $session_id = 0; }
3007     my $tpath = dirname($dest);
3008     -d "$tpath" || mkpath "$tpath";
3010     # This is ugly, but I've no time to take a look at "how it works in perl"
3011     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3012         system("gunzip -cd '$dest' > '$dest.in'");
3013         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3014         unlink($dest);
3015         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3016     } else {
3017         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3018     }
3019     return 0;
3023 sub parse_package {
3024     my ($path, $dist, $srv_path, $session_id)= @_;
3025     if (not defined $session_id) { $session_id = 0;}
3026     my ($package, $version, $section, $description);
3027     my $PACKAGES;
3028     my $timestamp = &get_time();
3030     if(not stat("$path.in")) {
3031         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3032         return;
3033     }
3035     open($PACKAGES, "<$path.in");
3036     if(not defined($PACKAGES)) {
3037         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3038         return;
3039     }
3041     # Read lines
3042     while (<$PACKAGES>){
3043         my $line = $_;
3044         # Unify
3045         chop($line);
3047         # Use empty lines as a trigger
3048         if ($line =~ /^\s*$/){
3049             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3050             push(@packages_list_statements, $sql);
3051             $package = "none";
3052             $version = "none";
3053             $section = "none";
3054             $description = "none"; 
3055             next;
3056         }
3058         # Trigger for package name
3059         if ($line =~ /^Package:\s/){
3060             ($package)= ($line =~ /^Package: (.*)$/);
3061             next;
3062         }
3064         # Trigger for version
3065         if ($line =~ /^Version:\s/){
3066             ($version)= ($line =~ /^Version: (.*)$/);
3067             next;
3068         }
3070         # Trigger for description
3071         if ($line =~ /^Description:\s/){
3072             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3073             next;
3074         }
3076         # Trigger for section
3077         if ($line =~ /^Section:\s/){
3078             ($section)= ($line =~ /^Section: (.*)$/);
3079             next;
3080         }
3082         # Trigger for filename
3083         if ($line =~ /^Filename:\s/){
3084             my ($filename) = ($line =~ /^Filename: (.*)$/);
3085             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3086             next;
3087         }
3088     }
3090     close( $PACKAGES );
3091     unlink( "$path.in" );
3095 sub store_fileinfo {
3096     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3098     my %fileinfo = (
3099         'package' => $package,
3100         'dist' => $dist,
3101         'version' => $vers,
3102     );
3104     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3108 sub cleanup_and_extract {
3109         my $fileinfo = $repo_files{ $File::Find::name };
3111         if( defined $fileinfo ) {
3112                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3113                 my $sql;
3114                 my $package = $fileinfo->{ 'package' };
3115                 my $newver = $fileinfo->{ 'version' };
3117                 mkpath($dir);
3118                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3120                 if( -f "$dir/DEBIAN/templates" ) {
3122                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3124                         my $tmpl= ""; {
3125                                 local $/=undef;
3126                                 open FILE, "$dir/DEBIAN/templates";
3127                                 $tmpl = &encode_base64(<FILE>);
3128                                 close FILE;
3129                         }
3130                         rmtree("$dir/DEBIAN/templates");
3132                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3133                         push @packages_list_statements, $sql;
3134                 }
3135         }
3137         return;
3141 sub register_at_foreign_servers {   
3142     my ($kernel) = $_[KERNEL];
3144     # hole alle bekannten server aus known_server_db
3145     my $server_sql = "SELECT * FROM $known_server_tn";
3146     my $server_res = $known_server_db->exec_statement($server_sql);
3148     # no entries in known_server_db
3149     if (not ref(@$server_res[0]) eq "ARRAY") { 
3150         # TODO
3151     }
3153     # detect already connected clients
3154     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3155     my $client_res = $known_clients_db->exec_statement($client_sql);
3157     # send my server details to all other gosa-si-server within the network
3158     foreach my $hit (@$server_res) {
3159         my $hostname = @$hit[0];
3160         my $hostkey = &create_passwd;
3162         # add already connected clients to registration message 
3163         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3164         &add_content2xml_hash($myhash, 'key', $hostkey);
3165         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3167         # add locally loaded gosa-si modules to registration message
3168         my $loaded_modules = {};
3169         while (my ($package, $pck_info) = each %$known_modules) {
3170                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3171                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3172                                                         $loaded_modules->{$act_module} = ""; 
3173                                                 }
3174         }
3176         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3178         # add macaddress to registration message
3179         my ($host_ip, $host_port) = split(/:/, $hostname);
3180         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3181         my $network_interface= &get_interface_for_ip($local_ip);
3182         my $host_mac = &get_mac_for_interface($network_interface);
3183         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3184         
3185         # build registration message and send it
3186         my $foreign_server_msg = &create_xml_string($myhash);
3187         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3188     }
3189     
3190     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3191     return;
3195 #==== MAIN = main ==============================================================
3196 #  parse commandline options
3197 Getopt::Long::Configure( "bundling" );
3198 GetOptions("h|help" => \&usage,
3199         "c|config=s" => \$cfg_file,
3200         "f|foreground" => \$foreground,
3201         "v|verbose+" => \$verbose,
3202         "no-arp+" => \$no_arp,
3203            );
3205 #  read and set config parameters
3206 &check_cmdline_param ;
3207 &read_configfile($cfg_file, %cfg_defaults);
3208 &check_pid;
3210 $SIG{CHLD} = 'IGNORE';
3212 # forward error messages to logfile
3213 if( ! $foreground ) {
3214   open( STDIN,  '+>/dev/null' );
3215   open( STDOUT, '+>&STDIN'    );
3216   open( STDERR, '+>&STDIN'    );
3219 # Just fork, if we are not in foreground mode
3220 if( ! $foreground ) { 
3221     chdir '/'                 or die "Can't chdir to /: $!";
3222     $pid = fork;
3223     setsid                    or die "Can't start a new session: $!";
3224     umask 0;
3225 } else { 
3226     $pid = $$; 
3229 # Do something useful - put our PID into the pid_file
3230 if( 0 != $pid ) {
3231     open( LOCK_FILE, ">$pid_file" );
3232     print LOCK_FILE "$pid\n";
3233     close( LOCK_FILE );
3234     if( !$foreground ) { 
3235         exit( 0 ) 
3236     };
3239 # parse head url and revision from svn
3240 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3241 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3242 $server_headURL = defined $1 ? $1 : 'unknown' ;
3243 $server_revision = defined $2 ? $2 : 'unknown' ;
3244 if ($server_headURL =~ /\/tag\// || 
3245         $server_headURL =~ /\/branches\// ) {
3246     $server_status = "stable"; 
3247 } else {
3248     $server_status = "developmental" ;
3250 # Prepare log file and set permissions
3251 $root_uid = getpwnam('root');
3252 $adm_gid = getgrnam('adm');
3253 open(FH, ">>$log_file");
3254 close FH;
3255 chmod(0440, $log_file);
3256 chown($root_uid, $adm_gid, $log_file);
3257 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3259 daemon_log(" ", 1);
3260 daemon_log("$0 started!", 1);
3261 daemon_log("status: $server_status", 1);
3262 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3265     no strict "refs";
3267     if ($db_module eq "DBmysql") {
3268         # connect to incoming_db
3269         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3271         # connect to gosa-si job queue
3272         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3274         # connect to known_clients_db
3275         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3277         # connect to foreign_clients_db
3278         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3280         # connect to known_server_db
3281         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3283         # connect to login_usr_db
3284         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3286         # connect to fai_server_db 
3287         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3289         # connect to fai_release_db
3290         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3292         # connect to packages_list_db
3293         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3295         # connect to messaging_db
3296         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3298     } elsif ($db_module eq "DBsqlite") {
3299         # connect to incoming_db
3300         unlink($incoming_file_name);
3301         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3302         
3303         # connect to gosa-si job queue
3304         unlink($job_queue_file_name);  ## just for debugging
3305         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3306         chmod(0640, $job_queue_file_name);
3307         chown($root_uid, $adm_gid, $job_queue_file_name);
3308         
3309         # connect to known_clients_db
3310         unlink($known_clients_file_name);   ## just for debugging
3311         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3312         chmod(0640, $known_clients_file_name);
3313         chown($root_uid, $adm_gid, $known_clients_file_name);
3314         
3315         # connect to foreign_clients_db
3316         unlink($foreign_clients_file_name);
3317         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3318         chmod(0640, $foreign_clients_file_name);
3319         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3320         
3321         # connect to known_server_db
3322         unlink($known_server_file_name);
3323         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3324         chmod(0640, $known_server_file_name);
3325         chown($root_uid, $adm_gid, $known_server_file_name);
3326         
3327         # connect to login_usr_db
3328         unlink($login_users_file_name);
3329         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3330         chmod(0640, $login_users_file_name);
3331         chown($root_uid, $adm_gid, $login_users_file_name);
3332         
3333         # connect to fai_server_db
3334         unlink($fai_server_file_name);
3335         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3336         chmod(0640, $fai_server_file_name);
3337         chown($root_uid, $adm_gid, $fai_server_file_name);
3338         
3339         # connect to fai_release_db
3340         unlink($fai_release_file_name);
3341         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3342         chmod(0640, $fai_release_file_name);
3343         chown($root_uid, $adm_gid, $fai_release_file_name);
3344         
3345         # connect to packages_list_db
3346         #unlink($packages_list_file_name);
3347         unlink($packages_list_under_construction);
3348         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3349         chmod(0640, $packages_list_file_name);
3350         chown($root_uid, $adm_gid, $packages_list_file_name);
3351         
3352         # connect to messaging_db
3353         unlink($messaging_file_name);
3354         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3355         chmod(0640, $messaging_file_name);
3356         chown($root_uid, $adm_gid, $messaging_file_name);
3357     }
3361 # Creating tables
3362 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3363 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3364 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3365 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3366 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3367 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3368 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3369 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3370 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3371 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3373 # create xml object used for en/decrypting
3374 $xml = new XML::Simple();
3377 # foreign servers 
3378 my @foreign_server_list;
3380 # add foreign server from cfg file
3381 if ($foreign_server_string ne "") {
3382     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3383     foreach my $foreign_server (@cfg_foreign_server_list) {
3384         push(@foreign_server_list, $foreign_server);
3385     }
3387     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3390 # Perform a DNS lookup for server registration if flag is true
3391 if ($dns_lookup eq "true") {
3392     # Add foreign server from dns
3393     my @tmp_servers;
3394     if (not $server_domain) {
3395         # Try our DNS Searchlist
3396         for my $domain(get_dns_domains()) {
3397             chomp($domain);
3398             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3399             if(@$tmp_domains) {
3400                 for my $tmp_server(@$tmp_domains) {
3401                     push @tmp_servers, $tmp_server;
3402                 }
3403             }
3404         }
3405         if(@tmp_servers && length(@tmp_servers)==0) {
3406             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3407         }
3408     } else {
3409         @tmp_servers = &get_server_addresses($server_domain);
3410         if( 0 == @tmp_servers ) {
3411             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3412         }
3413     }
3415     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3417     foreach my $server (@tmp_servers) { 
3418         unshift(@foreign_server_list, $server); 
3419     }
3420 } else {
3421     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3425 # eliminate duplicate entries
3426 @foreign_server_list = &del_doubles(@foreign_server_list);
3427 my $all_foreign_server = join(", ", @foreign_server_list);
3428 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3430 # add all found foreign servers to known_server
3431 my $cur_timestamp = &get_time();
3432 foreach my $foreign_server (@foreign_server_list) {
3434         # do not add myself to known_server_db
3435         if (&is_local($foreign_server)) { next; }
3436         ######################################
3438     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3439             primkey=>['hostname'],
3440             hostname=>$foreign_server,
3441             macaddress=>"",
3442             status=>'not_yet_registered',
3443             hostkey=>"none",
3444             loaded_modules => "none", 
3445             timestamp=>$cur_timestamp,
3446             } );
3450 # Import all modules
3451 &import_modules;
3453 # Check wether all modules are gosa-si valid passwd check
3454 &password_check;
3456 # Prepare for using Opsi 
3457 if ($opsi_enabled eq "true") {
3458     use JSON::RPC::Client;
3459     use XML::Quote qw(:all);
3460     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3461     $opsi_client = new JSON::RPC::Client;
3465 POE::Component::Server::TCP->new(
3466         Alias => "TCP_SERVER",
3467         Port => $server_port,
3468         ClientInput => sub {
3469                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3470         my $session_id = $session->ID;
3471         my $remote_ip = $heap->{'remote_ip'};
3472                 push(@msgs_to_decrypt, $input);
3473         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3474                 $kernel->yield("msg_to_decrypt");
3475         },
3476         InlineStates => {
3477                 msg_to_decrypt => \&msg_to_decrypt,
3478                 next_task => \&next_task,
3479                 task_result => \&handle_task_result,
3480                 task_done   => \&handle_task_done,
3481                 task_debug  => \&handle_task_debug,
3482                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3483         }
3484 );
3486 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3488 # create session for repeatedly checking the job queue for jobs
3489 POE::Session->create(
3490         inline_states => {
3491                 _start => \&session_start,
3492         register_at_foreign_servers => \&register_at_foreign_servers,
3493         sig_handler => \&sig_handler,
3494         next_task => \&next_task,
3495         task_result => \&handle_task_result,
3496         task_done   => \&handle_task_done,
3497         task_debug  => \&handle_task_debug,
3498         watch_for_next_tasks => \&watch_for_next_tasks,
3499         watch_for_new_messages => \&watch_for_new_messages,
3500         watch_for_delivery_messages => \&watch_for_delivery_messages,
3501         watch_for_done_messages => \&watch_for_done_messages,
3502                 watch_for_new_jobs => \&watch_for_new_jobs,
3503         watch_for_modified_jobs => \&watch_for_modified_jobs,
3504         watch_for_done_jobs => \&watch_for_done_jobs,
3505         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3506         watch_for_old_known_clients => \&watch_for_old_known_clients,
3507         create_packages_list_db => \&run_create_packages_list_db,
3508         create_fai_server_db => \&run_create_fai_server_db,
3509         create_fai_release_db => \&run_create_fai_release_db,
3510                 recreate_packages_db => \&run_recreate_packages_db,
3511         session_run_result => \&session_run_result,
3512         session_run_debug => \&session_run_debug,
3513         session_run_done => \&session_run_done,
3514         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3515         }
3516 );
3519 POE::Kernel->run();
3520 exit;