Code

Apply fix for #6613
[gosa.git] / trunk / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
23 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
25 use strict;
26 use warnings;
27 use Getopt::Long;
28 use Config::IniFiles;
29 use POSIX;
31 use Fcntl 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_dirs=();
84 # Variables declared in config file are always set to 'our'
85 our (%cfg_defaults, $log_file, $pid_file, $repo_basepath,
86     $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
87     $arp_activ, $gosa_unit_tag,
88     $GosaPackages_key, $gosa_timeout,
89     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
90     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
91     $arp_enabled, $arp_interface,
92     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
93                 $new_systems_ou,
94 );
96 # additional variable which should be globaly accessable
97 our $server_address;
98 our $server_mac_address;
99 our $gosa_address;
100 our $no_arp;
101 our $verbose;
102 our $forground;
103 our $cfg_file;
104 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
105 our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
106 our $known_modules;
107 our $root_uid;
108 our $adm_gid;
111 # specifies the verbosity of the daemon_log
112 $verbose = 0 ;
114 # if foreground is not null, script will be not forked to background
115 $foreground = 0 ;
117 # specifies the timeout seconds while checking the online status of a registrating client
118 $ping_timeout = 5;
120 $no_arp = 0;
121 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
122 my @packages_list_statements;
123 my $watch_for_new_jobs_in_progress = 0;
125 # holds all incoming decrypted messages
126 our $incoming_db;
127 our $incoming_tn = 'incoming';
128 my $incoming_file_name;
129 my @incoming_col_names = ("id INTEGER PRIMARY KEY",
130         "timestamp VARCHAR(14) DEFAULT 'none'", 
131         "headertag VARCHAR(255) DEFAULT 'none'",
132         "targettag VARCHAR(255) DEFAULT 'none'",
133         "xmlmessage TEXT",
134         "module VARCHAR(255) DEFAULT 'none'",
135         "sessionid VARCHAR(255) DEFAULT '0'",
136 );
138 # holds all gosa jobs
139 our $job_db;
140 our $job_queue_tn = 'jobs';
141 my $job_queue_file_name;
142 my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
143         "timestamp VARCHAR(14) DEFAULT 'none'", 
144         "status VARCHAR(255) DEFAULT 'none'", 
145         "result TEXT",
146         "progress VARCHAR(255) DEFAULT 'none'",
147         "headertag VARCHAR(255) DEFAULT 'none'",
148         "targettag VARCHAR(255) DEFAULT 'none'", 
149         "xmlmessage TEXT", 
150         "macaddress VARCHAR(17) DEFAULT 'none'",
151         "plainname VARCHAR(255) DEFAULT 'none'",
152         "siserver VARCHAR(255) DEFAULT 'none'",
153         "modified INTEGER DEFAULT '0'",
154 );
156 # holds all other gosa-si-server
157 our $known_server_db;
158 our $known_server_tn = "known_server";
159 my $known_server_file_name;
160 my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)");
162 # holds all registrated clients
163 our $known_clients_db;
164 our $known_clients_tn = "known_clients";
165 my $known_clients_file_name;
166 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)");
168 # holds all registered clients at a foreign server
169 our $foreign_clients_db;
170 our $foreign_clients_tn = "foreign_clients"; 
171 my $foreign_clients_file_name;
172 my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
174 # holds all logged in user at each client 
175 our $login_users_db;
176 our $login_users_tn = "login_users";
177 my $login_users_file_name;
178 my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
180 # holds all fai server, the debian release and tag
181 our $fai_server_db;
182 our $fai_server_tn = "fai_server"; 
183 my $fai_server_file_name;
184 our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
186 our $fai_release_db;
187 our $fai_release_tn = "fai_release"; 
188 my $fai_release_file_name;
189 our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
191 # holds all packages available from different repositories
192 our $packages_list_db;
193 our $packages_list_tn = "packages_list";
194 my $packages_list_file_name;
195 our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
196 my $outdir = "/tmp/packages_list_db";
197 my $arch = "i386"; 
199 # holds all messages which should be delivered to a user
200 our $messaging_db;
201 our $messaging_tn = "messaging"; 
202 our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
203         "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
204 my $messaging_file_name;
206 # path to directory to store client install log files
207 our $client_fai_log_dir = "/var/log/fai"; 
209 # queue which stores taskes until one of the $max_children children are ready to process the task
210 #my @tasks = qw();
211 my @msgs_to_decrypt = qw();
212 my $max_children = 2;
214 # Allow 50 POE Childs
215 sub MAX_CONCURRENT_TASKS () { 50 }
217 # loop delay for job queue to look for opsi jobs
218 my $job_queue_opsi_delay = 10;
219 our $opsi_client;
220 our $opsi_url;
221  
222 # Lifetime of logged in user information. If no update information comes after n seconds, 
223 # the user is expeceted to be no longer logged in or the host is no longer running. Because
224 # of this, the user is deleted from login_users_db
225 our $logged_in_user_date_of_expiry = 600;
228 %cfg_defaults = (
229 "general" => {
230     "log-file"      => [\$log_file, "/var/run/".$prg.".log"],
231     "pid-file"      => [\$pid_file, "/var/run/".$prg.".pid"],
232     "repo-basepath" => [\$repo_basepath, "/srv/www"],
233     },
234 "server" => {
235     "ip"                    => [\$server_ip, "0.0.0.0"],
236     "port"                  => [\$server_port, "20081"],
237     "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
238     "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
239     "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
240     "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
241     "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
242     "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
243     "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
244     "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
245     "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
246     "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
247     "ldap-uri"              => [\$ldap_uri, ""],
248     "ldap-base"             => [\$ldap_base, ""],
249     "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
250     "ldap-admin-password"   => [\$ldap_admin_password, ""],
251     "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
252     "max-clients"           => [\$max_clients, 10],
253     "wol-password"          => [\$wake_on_lan_passwd, ""],
254                 "mysql-username"        => [\$mysql_username, "gosa_si"],
255                 "mysql-password"        => [\$mysql_password, ""],
256                 "mysql-database"        => [\$mysql_database, "gosa_si"],
257                 "mysql-host"            => [\$mysql_host, "127.0.0.1"],
258     },
259 "GOsaPackages" => {
260     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
261     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
262     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
263     "key" => [\$GosaPackages_key, "none"],
264                 "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
265     },
266 "ClientPackages" => {
267     "key" => [\$ClientPackages_key, "none"],
268     "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
269     },
270 "ServerPackages"=> {
271     "address"      => [\$foreign_server_string, ""],
272     "dns-lookup"            => [\$dns_lookup, "true"],
273     "domain"  => [\$server_domain, ""],
274     "key"     => [\$ServerPackages_key, "none"],
275     "key-lifetime" => [\$foreign_servers_register_delay, 120],
276     "job-synchronization-enabled" => [\$job_synchronization, "true"],
277     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
278     },
279 "ArpHandler" => {
280     "enabled"   => [\$arp_enabled, "true"],
281     "interface" => [\$arp_interface, "all"],
282         },
283 "Opsi" => {
284     "enabled"  => [\$opsi_enabled, "false"], 
285     "server"   => [\$opsi_server, "localhost"],
286     "admin"    => [\$opsi_admin, "opsi-admin"],
287     "password" => [\$opsi_password, "secret"],
288    },
290 );
293 #===  FUNCTION  ================================================================
294 #         NAME:  usage
295 #   PARAMETERS:  nothing
296 #      RETURNS:  nothing
297 #  DESCRIPTION:  print out usage text to STDERR
298 #===============================================================================
299 sub usage {
300     print STDERR << "EOF" ;
301 usage: $prg [-hvf] [-c config]
303            -h        : this (help) message
304            -c <file> : config file
305            -f        : foreground, process will not be forked to background
306            -v        : be verbose (multiple to increase verbosity)
307            -no-arp   : starts $prg without connection to arp module
308  
309 EOF
310     print "\n" ;
314 #===  FUNCTION  ================================================================
315 #         NAME:  logging
316 #   PARAMETERS:  level - string - default 'info'
317 #                msg - string -
318 #                facility - string - default 'LOG_DAEMON'
319 #      RETURNS:  nothing
320 #  DESCRIPTION:  function for logging
321 #===============================================================================
322 sub daemon_log {
323     # log into log_file
324     my( $msg, $level ) = @_;
325     if(not defined $msg) { return }
326     if(not defined $level) { $level = 1 }
327     if(defined $log_file){
328         my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
329         if(not $open_log_fh) {
330             print STDERR "cannot open $log_file: $!";
331             return;
332         }
333         # check owner and group of log_file and update settings if necessary
334         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
335         if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
336             chown($root_uid, $adm_gid, $log_file);
337         }
339         chomp($msg);
340         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
341         if($level <= $verbose){
342             my ($seconds, $minutes, $hours, $monthday, $month,
343                     $year, $weekday, $yearday, $sommertime) = localtime(time);
344             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
345             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
346             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
347             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
348             $month = $monthnames[$month];
349             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
350             $year+=1900;
351             my $name = $prg;
353             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
354                         flock(LOG_HANDLE, LOCK_EX);
355                         seek(LOG_HANDLE, 0, 2);
356             print LOG_HANDLE $log_msg;
357                         flock(LOG_HANDLE, LOCK_UN);
358             if( $foreground ) { 
359                 print STDERR $log_msg;
360             }
361         }
362         close( LOG_HANDLE );
363     }
367 #===  FUNCTION  ================================================================
368 #         NAME:  check_cmdline_param
369 #   PARAMETERS:  nothing
370 #      RETURNS:  nothing
371 #  DESCRIPTION:  validates commandline parameter
372 #===============================================================================
373 sub check_cmdline_param () {
374     my $err_config;
375     my $err_counter = 0;
376         if(not defined($cfg_file)) {
377                 $cfg_file = "/etc/gosa-si/server.conf";
378                 if(! -r $cfg_file) {
379                         $err_config = "please specify a config file";
380                         $err_counter += 1;
381                 }
382     }
383     if( $err_counter > 0 ) {
384         &usage( "", 1 );
385         if( defined( $err_config)) { print STDERR "$err_config\n"}
386         print STDERR "\n";
387         exit( -1 );
388     }
392 #===  FUNCTION  ================================================================
393 #         NAME:  check_pid
394 #   PARAMETERS:  nothing
395 #      RETURNS:  nothing
396 #  DESCRIPTION:  handels pid processing
397 #===============================================================================
398 sub check_pid {
399     $pid = -1;
400     # Check, if we are already running
401     if( open(LOCK_FILE, "<$pid_file") ) {
402         $pid = <LOCK_FILE>;
403         if( defined $pid ) {
404             chomp( $pid );
405             if( -f "/proc/$pid/stat" ) {
406                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
407                 if( $stat ) {
408                                         daemon_log("ERROR: Already running",1);
409                     close( LOCK_FILE );
410                     exit -1;
411                 }
412             }
413         }
414         close( LOCK_FILE );
415         unlink( $pid_file );
416     }
418     # create a syslog msg if it is not to possible to open PID file
419     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
420         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
421         if (open(LOCK_FILE, '<', $pid_file)
422                 && ($pid = <LOCK_FILE>))
423         {
424             chomp($pid);
425             $msg .= "(PID $pid)\n";
426         } else {
427             $msg .= "(unable to read PID)\n";
428         }
429         if( ! ($foreground) ) {
430             openlog( $0, "cons,pid", "daemon" );
431             syslog( "warning", $msg );
432             closelog();
433         }
434         else {
435             print( STDERR " $msg " );
436         }
437         exit( -1 );
438     }
441 #===  FUNCTION  ================================================================
442 #         NAME:  import_modules
443 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
444 #                are stored
445 #      RETURNS:  nothing
446 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
447 #                state is on is imported by "require 'file';"
448 #===============================================================================
449 sub import_modules {
450     daemon_log(" ", 1);
452     if (not -e $modules_path) {
453         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
454     }
456     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
457     while (defined (my $file = readdir (DIR))) {
458         if (not $file =~ /(\S*?).pm$/) {
459             next;
460         }
461                 my $mod_name = $1;
463         # ArpHandler switch
464         if( $file =~ /ArpHandler.pm/ ) {
465             if( $arp_enabled eq "false" ) { next; }
466         }
467         
468         eval { require $file; };
469         if ($@) {
470             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
471             daemon_log("$@", 1);
472             exit;
473                 } else {
474                         my $info = eval($mod_name.'::get_module_info()');
475                         # Only load module if get_module_info() returns a non-null object
476                         if( $info ) {
477                                 my ($input_address, $input_key, $event_hash) = @{$info};
478                                 $known_modules->{$mod_name} = $info;
479                                 daemon_log("0 INFO: module $mod_name loaded", 5);
480                         }
481                 }
482     }   
484     close (DIR);
487 #===  FUNCTION  ================================================================
488 #         NAME:  password_check
489 #   PARAMETERS:  nothing
490 #      RETURNS:  nothing
491 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
492 #                the same password
493 #===============================================================================
494 sub password_check {
495     my $passwd_hash = {};
496     while (my ($mod_name, $mod_info) = each %$known_modules) {
497         my $mod_passwd = @$mod_info[1];
498         if (not defined $mod_passwd) { next; }
499         if (not exists $passwd_hash->{$mod_passwd}) {
500             $passwd_hash->{$mod_passwd} = $mod_name;
502         # escalates critical error
503         } else {
504             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
505             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
506             exit( -1 );
507         }
508     }
512 sub clean_shutdown
514     unlink($pid_file) if (-w $pid_file);
515     unlink($packages_list_under_construction) if (-w $packages_list_under_construction);
518 sub sig_int_or_term_handler
520     my ($signal) = @_;
521     daemon_log("Got SIG${signal} - shutting down gosa-si-server", 1);
522     clean_shutdown();
523     POE::Kernel->signal('gosa-si_server_session', 'KILL');
524     POE::Kernel->signal('TCP_SERVER', 'KILL');
525     return 1;
528 sub sig_warn_handler
530     my @loc = caller(0);
531     daemon_log( "SIGWARN line " . $loc[2] . ": " . $_[0], 1 );
532     return 1;
535 $SIG{'INT'} = \&sig_int_or_term_handler;
536 $SIG{'TERM'} = \&sig_int_or_term_handler;
537 $SIG{'__WARN__'} = \&sig_warn_handler;
538 $SIG{'USR1'} = 'IGNORE';
540 sub check_key_and_xml_validity {
541     my ($crypted_msg, $module_key, $session_id) = @_;
542     my $msg;
543     my $msg_hash;
544     my $error_string;
545     eval{
546         $msg = &decrypt_msg($crypted_msg, $module_key);
548         if ($msg =~ /<xml>/i){
549             $msg =~ s/\s+/ /g;  # just for better daemon_log
550             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 9);
551             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
553             ##############
554             # check header
555             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
556             my $header_l = $msg_hash->{'header'};
557             if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
558             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
559             my $header = @{$header_l}[0];
560             if( 0 == length $header) { die 'empty string in header tag'; }
562             ##############
563             # check source
564             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
565             my $source_l = $msg_hash->{'source'};
566             if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
567             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
568             my $source = @{$source_l}[0];
569             if( 0 == length $source) { die 'source error'; }
571             ##############
572             # check target
573             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
574             my $target_l = $msg_hash->{'target'};
575             if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
576         }
577     };
578     if($@) {
579         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
580         $msg = undef;
581         $msg_hash = undef;
582     }
584     return ($msg, $msg_hash);
588 sub check_outgoing_xml_validity {
589     my ($msg, $session_id) = @_;
591     my $msg_hash;
592     eval{
593         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
595         ##############
596         # check header
597         my $header_l = $msg_hash->{'header'};
598         if( 1 != @{$header_l} ) {
599             die 'no or more than one headers specified';
600         }
601         my $header = @{$header_l}[0];
602         if( 0 == length $header) {
603             die 'header has length 0';
604         }
606         ##############
607         # check source
608         my $source_l = $msg_hash->{'source'};
609         if( 1 != @{$source_l} ) {
610             die 'no or more than 1 sources specified';
611         }
612         my $source = @{$source_l}[0];
613         if( 0 == length $source) {
614             die 'source has length 0';
615         }
617                                 # Check if source contains hostname instead of ip address
618                                 if($source =~ /^[a-z][a-z0-9\.]+:\d+$/i) {
619                                                 my ($hostname,$port) = split(/:/, $source);
620                                                 my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
621                                                 if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
622                                                         # Write ip address to $source variable
623                                                         $source = "$ip_address:$port";
624                                                 }
625                                 }
626         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
627                 $source =~ /^GOSA$/i) {
628             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
629         }
630         
631         ##############
632         # check target  
633         my $target_l = $msg_hash->{'target'};
634         if( 0 == @{$target_l} ) {
635             die "no targets specified";
636         }
637         foreach my $target (@$target_l) {
638             if( 0 == length $target) {
639                 die "target has length 0";
640             }
641             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
642                     $target =~ /^GOSA$/i ||
643                     $target =~ /^\*$/ ||
644                     $target =~ /KNOWN_SERVER/i ||
645                     $target =~ /JOBDB/i ||
646                     $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 ){
647                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
648             }
649         }
650     };
651     if($@) {
652         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
653         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
654         $msg_hash = undef;
655     }
657     return ($msg_hash);
661 sub input_from_known_server {
662     my ($input, $remote_ip, $session_id) = @_ ;  
663     my ($msg, $msg_hash, $module);
665     my $sql_statement= "SELECT * FROM known_server";
666     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
668     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
669         my $host_name = $hit->{hostname};
670         if( not $host_name =~ "^$remote_ip") {
671             next;
672         }
673         my $host_key = $hit->{hostkey};
674         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
675         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
677         # check if module can open msg envelope with module key
678         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
679         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
680             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
681             daemon_log("$@", 8);
682             next;
683         }
684         else {
685             $msg = $tmp_msg;
686             $msg_hash = $tmp_msg_hash;
687             $module = "ServerPackages";
688             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
689             last;
690         }
691     }
693     if( (!$msg) || (!$msg_hash) || (!$module) ) {
694         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
695     }
696   
697     return ($msg, $msg_hash, $module);
701 sub input_from_known_client {
702     my ($input, $remote_ip, $session_id) = @_ ;  
703     my ($msg, $msg_hash, $module);
705     my $sql_statement= "SELECT * FROM known_clients";
706     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
707     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
708         my $host_name = $hit->{hostname};
709         if( not $host_name =~ /^$remote_ip:\d*$/) {
710                 next;
711                 }
712         my $host_key = $hit->{hostkey};
713         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
714         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
716         # check if module can open msg envelope with module key
717         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
719         if( (!$msg) || (!$msg_hash) ) {
720             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
721             &daemon_log("$@", 8);
722             next;
723         }
724         else {
725             $module = "ClientPackages";
726             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
727             last;
728         }
729     }
731     if( (!$msg) || (!$msg_hash) || (!$module) ) {
732         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
733     }
735     return ($msg, $msg_hash, $module);
739 sub input_from_unknown_host {
740         no strict "refs";
741         my ($input, $session_id) = @_ ;
742         my ($msg, $msg_hash, $module);
743         my $error_string;
745         my %act_modules = %$known_modules;
747         while( my ($mod, $info) = each(%act_modules)) {
749                 # check a key exists for this module
750                 my $module_key = ${$mod."_key"};
751                 if( not defined $module_key ) {
752                         if( $mod eq 'ArpHandler' ) {
753                                 next;
754                         }
755                         daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
756                         next;
757                 }
758                 daemon_log("$session_id DEBUG: $mod: $module_key", 7);
760                 # check if module can open msg envelope with module key
761                 ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
762                 if( (not defined $msg) || (not defined $msg_hash) ) {
763                         next;
764                 } else {
765                         $module = $mod;
766             daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 7);
767                         last;
768                 }
769         }
771         if( (!$msg) || (!$msg_hash) || (!$module)) {
772                 daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
773         }
775         return ($msg, $msg_hash, $module);
779 sub create_ciphering {
780     my ($passwd) = @_;
781         if((!defined($passwd)) || length($passwd)==0) {
782                 $passwd = "";
783         }
784     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
785     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
786     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
787     $my_cipher->set_iv($iv);
788     return $my_cipher;
792 sub encrypt_msg {
793     my ($msg, $key) = @_;
794     my $my_cipher = &create_ciphering($key);
795     my $len;
796     {
797             use bytes;
798             $len= 16-length($msg)%16;
799     }
800     $msg = "\0"x($len).$msg;
801     $msg = $my_cipher->encrypt($msg);
802     chomp($msg = &encode_base64($msg));
803     # there are no newlines allowed inside msg
804     $msg=~ s/\n//g;
805     return $msg;
809 sub decrypt_msg {
811     my ($msg, $key) = @_ ;
812     $msg = &decode_base64($msg);
813     my $my_cipher = &create_ciphering($key);
814     $msg = $my_cipher->decrypt($msg); 
815     $msg =~ s/\0*//g;
816     return $msg;
820 sub get_encrypt_key {
821     my ($target) = @_ ;
822     my $encrypt_key;
823     my $error = 0;
825     # target can be in known_server
826     if( not defined $encrypt_key ) {
827         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
828         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
829         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
830             my $host_name = $hit->{hostname};
831             if( $host_name ne $target ) {
832                 next;
833             }
834             $encrypt_key = $hit->{hostkey};
835             last;
836         }
837     }
839     # target can be in known_client
840     if( not defined $encrypt_key ) {
841         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
842         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
843         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
844             my $host_name = $hit->{hostname};
845             if( $host_name ne $target ) {
846                 next;
847             }
848             $encrypt_key = $hit->{hostkey};
849             last;
850         }
851     }
853     return $encrypt_key;
857 #===  FUNCTION  ================================================================
858 #         NAME:  open_socket
859 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
860 #                [PeerPort] string necessary if port not appended by PeerAddr
861 #      RETURNS:  socket IO::Socket::INET
862 #  DESCRIPTION:  open a socket to PeerAddr
863 #===============================================================================
864 sub open_socket {
865     my ($PeerAddr, $PeerPort) = @_ ;
866     if(defined($PeerPort)){
867         $PeerAddr = $PeerAddr.":".$PeerPort;
868     }
869     my $socket;
870     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
871             Porto => "tcp",
872             Type => SOCK_STREAM,
873             Timeout => 5,
874             );
875     if(not defined $socket) {
876         return;
877     }
878 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
879     return $socket;
883 sub send_msg_to_target {
884     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
885     my $error = 0;
886     my $header;
887     my $timestamp = &get_time();
888     my $new_status;
889     my $act_status;
890     my ($sql_statement, $res);
891   
892     if( $msg_header ) {
893         $header = "'$msg_header'-";
894     } else {
895         $header = "";
896     }
898         # Patch the source ip
899         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
900                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
901                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
902         }
904     # encrypt xml msg
905     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
907     # opensocket
908     my $socket = &open_socket($address);
909     if( !$socket ) {
910         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
911         $error++;
912     }
913     
914     if( $error == 0 ) {
915         # send xml msg
916         print $socket $crypted_msg."\n";
918         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
919         daemon_log("$session_id DEBUG: message:\n$msg", 9);
920         
921     }
923     # close socket in any case
924     if( $socket ) {
925         close $socket;
926     }
928     if( $error > 0 ) { $new_status = "down"; }
929     else { $new_status = $msg_header; }
932     # known_clients
933     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
934     $res = $known_clients_db->select_dbentry($sql_statement);
935     if( keys(%$res) == 1) {
936         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
937         if ($act_status eq "down" && $new_status eq "down") {
938             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
939             $res = $known_clients_db->del_dbentry($sql_statement);
940             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
941         } else { 
942             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
943             $res = $known_clients_db->update_dbentry($sql_statement);
944             if($new_status eq "down"){
945                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
946             } else {
947                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
948             }
949         }
950     }
952     # known_server
953     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
954     $res = $known_server_db->select_dbentry($sql_statement);
955     if( keys(%$res) == 1) {
956         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
957         if ($act_status eq "down" && $new_status eq "down") {
958             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
959             $res = $known_server_db->del_dbentry($sql_statement);
960             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
962             # Remove the registered clients of the server as well
963             $sql_statement = "DELETE FROM foreign_clients WHERE regserver='$address'";
964             $res = $foreign_clients_db->del_dbentry($sql_statement);
965         } 
966         else { 
967             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
968             $res = $known_server_db->update_dbentry($sql_statement);
969             if($new_status eq "down"){
970                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
971             } else {
972                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
973             }
974         }
975     }
976     return $error; 
980 sub update_jobdb_status_for_send_msgs {
981     my ($session_id, $answer, $error) = @_;
982     &daemon_log("$session_id DEBUG: try to update job status", 7); 
983     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
984         my $jobdb_id = $1;
985     
986         $answer =~ /<header>(.*)<\/header>/;
987         my $job_header = $1;
989         $answer =~ /<target>(.*)<\/target>/;
990         my $job_target = $1;
991             
992         # Sending msg failed
993         if( $error ) {
995             # Set jobs to done, jobs do not need to deliver their message in any case
996             if (($job_header eq "trigger_action_localboot")
997                     ||($job_header eq "trigger_action_lock")
998                     ||($job_header eq "trigger_action_halt") 
999                     ) {
1000                 my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1001                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1002                 my $res = $job_db->update_dbentry($sql_statement);
1003                 
1004             # Reactivate jobs, jobs need to deliver their message
1005             } elsif (($job_header eq "trigger_action_activate")
1006                     ||($job_header eq "trigger_action_update")
1007                     ||($job_header eq "trigger_action_reinstall") 
1008                     ||($job_header eq "trigger_activate_new")
1009                     ) {
1010                 &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
1012             # For all other messages
1013             } else {
1014                 my $sql_statement = "UPDATE $job_queue_tn ".
1015                     "SET status='error', result='can not deliver msg, please consult log file' ".
1016                     "WHERE id=$jobdb_id";
1017                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1018                 my $res = $job_db->update_dbentry($sql_statement);
1019             }
1021         # Sending msg was successful
1022         } else {
1023             # Set jobs localboot, lock, activate, halt, reboot and wake to done
1024             # jobs reinstall, update, inst_update do themself setting to done
1025             if (($job_header eq "trigger_action_localboot")
1026                     ||($job_header eq "trigger_action_lock")
1027                     ||($job_header eq "trigger_action_activate")
1028                     ||($job_header eq "trigger_action_halt") 
1029                     ||($job_header eq "trigger_action_reboot")
1030                     ||($job_header eq "trigger_action_wake")
1031                     ||($job_header eq "trigger_wake")
1032                     ) {
1034                 my $sql_statement = "UPDATE $job_queue_tn ".
1035                     "SET status='done' ".
1036                     "WHERE id=$jobdb_id AND status='processed'";
1037                 &daemon_log("$session_id DEBUG: $sql_statement", 7); 
1038                 my $res = $job_db->update_dbentry($sql_statement);
1039             } else { 
1040                 &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 7); 
1041             } 
1042         } 
1043     } else { 
1044         &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag: $answer", 7); 
1045     }
1048 sub reactivate_job_with_delay {
1049     my ($session_id, $target, $header, $delay) = @_ ;
1050     # 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
1051     
1052     if (not defined $delay) { $delay = 30 } ;
1053     my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
1055     my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress='$target' AND headertag='$header')"; 
1056     my $res = $job_db->update_dbentry($sql);
1057     daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
1058             "cause client '$target' is currently not available", 5);
1059     daemon_log("$session_id $sql", 7);                             
1060     return;
1064 sub msg_to_decrypt {
1065         my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1066         my $session_id = $session->ID;
1067         my ($msg, $msg_hash, $module);
1068         my $error = 0;
1070         # fetch new msg out of @msgs_to_decrypt
1071         my $tmp_next_msg = shift @msgs_to_decrypt;
1072     my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
1074         # msg is from a new client or gosa
1075         ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1077         # msg is from a gosa-si-server
1078         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1079                 ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1080         }
1081         # msg is from a gosa-si-client
1082         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1083                 ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1084         }
1085         # an error occurred
1086         if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1087                 # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1088                 # could not understand a msg from its server the client cause a re-registering process
1089         my $remote_ip = $heap->{'remote_ip'};
1090         my $remote_port = $heap->{'remote_port'};
1091         my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
1092         my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
1094                 daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1095                         "' to cause a re-registering of the client if necessary", 3);
1096                 $error++;
1097         }
1100         my $header;
1101         my $target;
1102         my $source;
1103         my $done = 0;
1104         my $sql;
1105         my $res;
1107         # check whether this message should be processed here
1108         if ($error == 0) {
1109                 $header = @{$msg_hash->{'header'}}[0];
1110                 $target = @{$msg_hash->{'target'}}[0];
1111                 $source = @{$msg_hash->{'source'}}[0];
1112                 my $not_found_in_known_clients_db = 0;
1113                 my $not_found_in_known_server_db = 0;
1114                 my $not_found_in_foreign_clients_db = 0;
1115                 my $local_address;
1116                 my $local_mac;
1117                 my ($target_ip, $target_port) = split(':', $target);
1119                 # Determine the local ip address if target is an ip address
1120                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1121                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1122                 } else {
1123                         $local_address = $server_address;
1124                 }
1126                 # Determine the local mac address if target is a mac address
1127                 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) {
1128                         my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
1129                         my $network_interface= &get_interface_for_ip($loc_ip);
1130                         $local_mac = &get_mac_for_interface($network_interface);
1131                 } else {
1132                         $local_mac = $server_mac_address;
1133                 }
1135                 # target and source is equal to GOSA -> process here
1136                 if (not $done) {
1137                         if ($target eq "GOSA" && $source eq "GOSA") {
1138                                 $done = 1;                    
1139                                 &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process here", 7);
1140                         }
1141                 }
1143                 # target is own address without forward_to_gosa-tag -> process here
1144                 if (not $done) {
1145                         #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
1146                         if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1147                                 $done = 1;
1148                                 if ($source eq "GOSA") {
1149                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1150                                 }
1151                                 &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process here", 7);
1152                         }
1153                 }
1155                 # target is a client address in known_clients -> process here
1156                 if (not $done) {
1157                         $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1158                         $res = $known_clients_db->select_dbentry($sql);
1159                         if (keys(%$res) > 0) {
1160                                 $done = 1; 
1161                                 my $hostname = $res->{1}->{'hostname'};
1162                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1163                                 my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1164                                 if ($source eq "GOSA") {
1165                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1166                                 }
1167                                 &daemon_log("$session_id DEBUG: target is a client address in known_clients -> process here", 7);
1169                         } else {
1170                                 $not_found_in_known_clients_db = 1;
1171                         }
1172                 }
1174                 # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1175                 if (not $done) {
1176                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1177                         my $gosa_at;
1178                         my $gosa_session_id;
1179                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1180                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1181                                 if ($gosa_at ne $local_address) {
1182                                         $done = 1;
1183                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process here", 7); 
1184                                 }
1185                         }
1186                 }
1188                 # if message should be processed here -> add message to incoming_db
1189                 if ($done) {
1190                         # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1191                         # so gosa-si-server knows how to process this kind of messages
1192                         if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1193                                 $module = "GosaPackages";
1194                         }
1196                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1197                                         primkey=>[],
1198                                         headertag=>$header,
1199                                         targettag=>$target,
1200                                         xmlmessage=>&encode_base64($msg),
1201                                         timestamp=>&get_time,
1202                                         module=>$module,
1203                                         sessionid=>$session_id,
1204                                 } );
1206                 }
1208                 # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1209                 if (not $done) {
1210                         my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1211                         my $gosa_at;
1212                         my $gosa_session_id;
1213                         if (($target eq $local_address) && (defined $forward_to_gosa)){
1214                                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1215                                 if ($gosa_at eq $local_address) {
1216                                         my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1217                                         if( defined $session_reference ) {
1218                                                 $heap = $session_reference->get_heap();
1219                                         }
1220                                         if(exists $heap->{'client'}) {
1221                                                 $msg = &encrypt_msg($msg, $GosaPackages_key);
1222                                                 $heap->{'client'}->put($msg);
1223                                                 &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1224                                         }
1225                                         $done = 1;
1226                                         &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa", 7);
1227                                 }
1228                         }
1230                 }
1232                 # target is a client address in foreign_clients -> forward to registration server
1233                 if (not $done) {
1234                         $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1235                         $res = $foreign_clients_db->select_dbentry($sql);
1236                         if (keys(%$res) > 0) {
1237                                 my $hostname = $res->{1}->{'hostname'};
1238                                 my ($host_ip, $host_port) = split(/:/, $hostname);
1239                                 my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1240                                 my $regserver = $res->{1}->{'regserver'};
1241                                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1242                                 my $res = $known_server_db->select_dbentry($sql);
1243                                 if (keys(%$res) > 0) {
1244                                         my $regserver_key = $res->{1}->{'hostkey'};
1245                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1246                                         $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1247                                         if ($source eq "GOSA") {
1248                                                 $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1249                                         }
1250                                         &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1251                                 }
1252                                 $done = 1;
1253                                 &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward to registration server", 7);
1254                         } else {
1255                                 $not_found_in_foreign_clients_db = 1;
1256                         }
1257                 }
1259                 # target is a server address -> forward to server
1260                 if (not $done) {
1261                         $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1262                         $res = $known_server_db->select_dbentry($sql);
1263                         if (keys(%$res) > 0) {
1264                                 my $hostkey = $res->{1}->{'hostkey'};
1266                                 if ($source eq "GOSA") {
1267                                         $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1268                                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1270                                 }
1272                                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1273                                 $done = 1;
1274                                 &daemon_log("$session_id DEBUG: target is a server address -> forward to server", 7);
1275                         } else {
1276                                 $not_found_in_known_server_db = 1;
1277                         }
1278                 }
1281                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1282                 if ( $not_found_in_foreign_clients_db 
1283                         && $not_found_in_known_server_db
1284                         && $not_found_in_known_clients_db) {
1285                         &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);
1286             if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
1287                 $module = "GosaPackages"; 
1288             }
1289                         my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1290                                         primkey=>[],
1291                                         headertag=>$header,
1292                                         targettag=>$target,
1293                                         xmlmessage=>&encode_base64($msg),
1294                                         timestamp=>&get_time,
1295                                         module=>$module,
1296                                         sessionid=>$session_id,
1297                                 } );
1298                         $done = 1;
1299                 }
1302                 if (not $done) {
1303                         daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1304                         if ($source eq "GOSA") {
1305                                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1306                                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1308                                 my $session_reference = $kernel->ID_id_to_session($session_id);
1309                                 if( defined $session_reference ) {
1310                                         $heap = $session_reference->get_heap();
1311                                 }
1312                                 if(exists $heap->{'client'}) {
1313                                         $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1314                                         $heap->{'client'}->put($error_msg);
1315                                 }
1316                         }
1317                 }
1319         }
1321         return;
1325 sub next_task {
1326     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1327     my $running_task = POE::Wheel::Run->new(
1328             Program => sub { process_task($session, $heap, $task) },
1329             StdioFilter => POE::Filter::Reference->new(),
1330             StdoutEvent  => "task_result",
1331             StderrEvent  => "task_debug",
1332             CloseEvent   => "task_done",
1333             );
1334     $heap->{task}->{ $running_task->ID } = $running_task;
1337 sub handle_task_result {
1338     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1339     my $client_answer = $result->{'answer'};
1340     if( $client_answer =~ s/session_id=(\d+)$// ) {
1341         my $session_id = $1;
1342         if( defined $session_id ) {
1343             my $session_reference = $kernel->ID_id_to_session($session_id);
1344             if( defined $session_reference ) {
1345                 $heap = $session_reference->get_heap();
1346             }
1347         }
1349         if(exists $heap->{'client'}) {
1350             $heap->{'client'}->put($client_answer);
1351         }
1352     }
1353     $kernel->sig(CHLD => "child_reap");
1356 sub handle_task_debug {
1357     my $result = $_[ARG0];
1358     print STDERR "$result\n";
1361 sub handle_task_done {
1362     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1363     delete $heap->{task}->{$task_id};
1366 sub process_task {
1367     no strict "refs";
1368     #CHECK: Not @_[...]?
1369     my ($session, $heap, $task) = @_;
1370     my $error = 0;
1371     my $answer_l;
1372     my ($answer_header, @answer_target_l, $answer_source);
1373     my $client_answer = "";
1375     # prepare all variables needed to process message
1376     #my $msg = $task->{'xmlmessage'};
1377     my $msg = &decode_base64($task->{'xmlmessage'});
1378     my $incoming_id = $task->{'id'};
1379     my $module = $task->{'module'};
1380     my $header =  $task->{'headertag'};
1381     my $session_id = $task->{'sessionid'};
1382                 my $msg_hash;
1383                 eval {
1384         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1385                 }; 
1386                 daemon_log("ERROR: XML failure '$@'") if ($@);
1387     my $source = @{$msg_hash->{'source'}}[0];
1388     
1389     # set timestamp of incoming client uptodate, so client will not 
1390     # be deleted from known_clients because of expiration
1391     my $cur_time = &get_time();
1392     my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
1393     my $res = $known_clients_db->exec_statement($sql);
1395     ######################
1396     # process incoming msg
1397     if( $error == 0) {
1398         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1399         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1400         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1402         if ( 0 < @{$answer_l} ) {
1403             my $answer_str = join("\n", @{$answer_l});
1404             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1405                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1406             }
1407             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,9);
1408         } else {
1409             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,7);
1410         }
1412     }
1413     if( !$answer_l ) { $error++ };
1415     ########
1416     # answer
1417     if( $error == 0 ) {
1419         foreach my $answer ( @{$answer_l} ) {
1420             # check outgoing msg to xml validity
1421             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1422             if( not defined $answer_hash ) { next; }
1423             
1424             $answer_header = @{$answer_hash->{'header'}}[0];
1425             @answer_target_l = @{$answer_hash->{'target'}};
1426             $answer_source = @{$answer_hash->{'source'}}[0];
1428             # deliver msg to all targets 
1429             foreach my $answer_target ( @answer_target_l ) {
1431                 # targets of msg are all gosa-si-clients in known_clients_db
1432                 if( $answer_target eq "*" ) {
1433                     # answer is for all clients
1434                     my $sql_statement= "SELECT * FROM known_clients";
1435                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1436                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1437                         my $host_name = $hit->{hostname};
1438                         my $host_key = $hit->{hostkey};
1439                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1440                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1441                     }
1442                 }
1444                 # targets of msg are all gosa-si-server in known_server_db
1445                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1446                     # answer is for all server in known_server
1447                     my $sql_statement= "SELECT * FROM $known_server_tn";
1448                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1449                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1450                         my $host_name = $hit->{hostname};
1451                         my $host_key = $hit->{hostkey};
1452                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1453                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1454                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1455                     }
1456                 }
1458                 # target of msg is GOsa
1459                                 elsif( $answer_target eq "GOSA" ) {
1460                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1461                                         my $add_on = "";
1462                     if( defined $session_id ) {
1463                         $add_on = ".session_id=$session_id";
1464                     }
1465                     # answer is for GOSA and has to returned to connected client
1466                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1467                     $client_answer = $gosa_answer.$add_on;
1468                 }
1470                 # target of msg is job queue at this host
1471                 elsif( $answer_target eq "JOBDB") {
1472                     $answer =~ /<header>(\S+)<\/header>/;   
1473                     my $header;
1474                     if( defined $1 ) { $header = $1; }
1475                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1476                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1477                 }
1479                 # Target of msg is a mac address
1480                 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 ) {
1481                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 5);
1483                     # Looking for macaddress in known_clients
1484                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1485                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1486                     my $found_ip_flag = 0;
1487                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1488                         my $host_name = $hit->{hostname};
1489                         my $host_key = $hit->{hostkey};
1490                         $answer =~ s/$answer_target/$host_name/g;
1491                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1492                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1493                         &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1494                         $found_ip_flag++ ;
1495                     }   
1497                     # Looking for macaddress in foreign_clients
1498                     if ($found_ip_flag == 0) {
1499                         my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
1500                         my $res = $foreign_clients_db->select_dbentry($sql);
1501                         while( my ($hit_num, $hit) = each %{ $res } ) {
1502                             my $host_name = $hit->{hostname};
1503                             my $reg_server = $hit->{regserver};
1504                             daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
1505                             
1506                             # Fetch key for reg_server
1507                             my $reg_server_key;
1508                             my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
1509                             my $res = $known_server_db->select_dbentry($sql);
1510                             if (exists $res->{1}) {
1511                                 $reg_server_key = $res->{1}->{'hostkey'}; 
1512                             } else {
1513                                 daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
1514                                 daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
1515                                 $reg_server_key = undef;
1516                             }
1518                             # Send answer to server where client is registered
1519                             if (defined $reg_server_key) {
1520                                 $answer =~ s/$answer_target/$host_name/g;
1521                                 my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
1522                                 &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1523                                 $found_ip_flag++ ;
1524                             }
1525                         }
1526                     }
1528                     # No mac to ip matching found
1529                     if( $found_ip_flag == 0) {
1530                         daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
1531                         &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
1532                     }
1534                 # Answer is for one specific host   
1535                 } else {
1536                     # get encrypt_key
1537                     my $encrypt_key = &get_encrypt_key($answer_target);
1538                     if( not defined $encrypt_key ) {
1539                         # unknown target
1540                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1541                         next;
1542                     }
1543                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1544                     &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
1545                 }
1546             }
1547         }
1548     }
1550     my $filter = POE::Filter::Reference->new();
1551     my %result = ( 
1552             status => "seems ok to me",
1553             answer => $client_answer,
1554             );
1556     my $output = $filter->put( [ \%result ] );
1557     print @$output;
1562 sub session_start {
1563     my ($kernel) = $_[KERNEL];
1564     $kernel->alias_set('gosa-si_server_session');
1565     $global_kernel = $kernel;
1566     $kernel->yield('register_at_foreign_servers');
1567         $kernel->yield('create_fai_server_db', $fai_server_tn );
1568         $kernel->yield('create_fai_release_db', $fai_release_tn );
1569     $kernel->yield('watch_for_next_tasks');
1570         $kernel->sig(USR2 => "recreate_packages_db");
1571         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1572         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1573     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1574         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1575     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1576         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1577     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1579     # Start opsi check
1580     if ($opsi_enabled eq "true") {
1581         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1582     }
1585 sub session_stop {
1586     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1587     $kernel->alias_remove($heap->{alias});
1588     $kernel->alarm_remove_all();
1589     $kernel->post($heap->{child_session}, '_stop');
1592 sub watch_for_done_jobs {
1593         #CHECK: $heap for what?
1594         my ($kernel,$heap) = @_[KERNEL, HEAP];
1596         my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1597         my $res = $job_db->select_dbentry( $sql_statement );
1599         while( my ($id, $hit) = each %{$res} ) {
1600                 my $jobdb_id = $hit->{id};
1601                 my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1602                 my $res = $job_db->del_dbentry($sql_statement); 
1603         }
1605         $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1609 sub watch_for_opsi_jobs {
1610     my ($kernel) = $_[KERNEL];
1612     # 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 
1613     # opsi install job is to parse the xml message. There is still the correct header.
1614     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1615         my $res = $job_db->select_dbentry( $sql_statement );
1617     # Ask OPSI for an update of the running jobs
1618     while (my ($id, $hit) = each %$res ) {
1619         # Determine current parameters of the job
1620         my $hostId = $hit->{'plainname'};
1621         my $macaddress = $hit->{'macaddress'};
1622         my $progress = $hit->{'progress'};
1624         my $result= {};
1625         
1626         # For hosts, only return the products that are or get installed
1627         my $callobj;
1628         $callobj = {
1629             method  => 'getProductStates_hash',
1630             params  => [ $hostId ],
1631             id  => 1,
1632         };
1633         
1634         my $hres = $opsi_client->call($opsi_url, $callobj);
1635         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1636         if (not &check_opsi_res($hres)) {
1637             my $htmp= $hres->result->{$hostId};
1638         
1639             # Check state != not_installed or action == setup -> load and add
1640             my $products= 0;
1641             my $installed= 0;
1642             my $installing = 0;
1643             my $error= 0;  
1644             my @installed_list;
1645             my @error_list;
1646             my $act_status = "none";
1647             foreach my $product (@{$htmp}){
1649                 if ($product->{'installationStatus'} ne "not_installed" or
1650                         $product->{'actionRequest'} eq "setup"){
1652                     # Increase number of products for this host
1653                     $products++;
1654         
1655                     if ($product->{'installationStatus'} eq "failed"){
1656                         $result->{$product->{'productId'}}= "error";
1657                         unshift(@error_list, $product->{'productId'});
1658                         $error++;
1659                     }
1660                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1661                         $result->{$product->{'productId'}}= "installed";
1662                         unshift(@installed_list, $product->{'productId'});
1663                         $installed++;
1664                     }
1665                     if ($product->{'installationStatus'} eq "installing"){
1666                         $result->{$product->{'productId'}}= "installing";
1667                         $installing++;
1668                         $act_status = "installing - ".$product->{'productId'};
1669                     }
1670                 }
1671             }
1672         
1673             # Estimate "rough" progress, avoid division by zero
1674             if ($products == 0) {
1675                 $result->{'progress'}= 0;
1676             } else {
1677                 $result->{'progress'}= int($installed * 100 / $products);
1678             }
1680             # Set updates in job queue
1681             if ((not $error) && (not $installing) && ($installed)) {
1682                 $act_status = "installed - ".join(", ", @installed_list);
1683             }
1684             if ($error) {
1685                 $act_status = "error - ".join(", ", @error_list);
1686             }
1687             if ($progress ne $result->{'progress'} ) {
1688                 # Updating progress and result 
1689                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1690                 my $update_res = $job_db->update_dbentry($update_statement);
1691             }
1692             if ($progress eq 100) { 
1693                 # Updateing status
1694                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1695                 if ($error) {
1696                     $done_statement .= "status='error'";
1697                 } else {
1698                     $done_statement .= "status='done'";
1699                 }
1700                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1701                 my $done_res = $job_db->update_dbentry($done_statement);
1702             }
1705         }
1706     }
1708     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1712 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1713 sub watch_for_modified_jobs {
1714     my ($kernel,$heap) = @_[KERNEL, HEAP];
1716     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
1717     my $res = $job_db->select_dbentry( $sql_statement );
1718     
1719     # if db contains no jobs which should be update, do nothing
1720     if (keys %$res != 0) {
1722         if ($job_synchronization  eq "true") {
1723             # make out of the db result a gosa-si message   
1724             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1725  
1726             # update all other SI-server
1727             &inform_all_other_si_server($update_msg);
1728         }
1730         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1731         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1732         $res = $job_db->update_dbentry($sql_statement);
1733     }
1735     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1739 sub watch_for_new_jobs {
1740         if($watch_for_new_jobs_in_progress == 0) {
1741                 $watch_for_new_jobs_in_progress = 1;
1742                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1744                 # check gosa job quaeue for jobs with executable timestamp
1745                 my $timestamp = &get_time();
1746                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
1747                 my $res = $job_db->exec_statement( $sql_statement );
1749                 # Merge all new jobs that would do the same actions
1750                 my @drops;
1751                 my $hits;
1752                 foreach my $hit (reverse @{$res} ) {
1753                         my $macaddress= lc @{$hit}[8];
1754                         my $headertag= @{$hit}[5];
1755                         if(
1756                                 defined($hits->{$macaddress}) &&
1757                                 defined($hits->{$macaddress}->{$headertag}) &&
1758                                 defined($hits->{$macaddress}->{$headertag}[0])
1759                         ) {
1760                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1761                         }
1762                         $hits->{$macaddress}->{$headertag}= $hit;
1763                 }
1765                 # Delete new jobs with a matching job in state 'processing'
1766                 foreach my $macaddress (keys %{$hits}) {
1767                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1768                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1769                                 if(defined($jobdb_id)) {
1770                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1771                                         my $res = $job_db->exec_statement( $sql_statement );
1772                                         foreach my $hit (@{$res}) {
1773                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1774                                         }
1775                                 } else {
1776                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1777                                 }
1778                         }
1779                 }
1781                 # Commit deletion
1782                 $job_db->exec_statementlist(\@drops);
1784                 # Look for new jobs that could be executed
1785                 foreach my $macaddress (keys %{$hits}) {
1787                         # Look if there is an executing job
1788                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1789                         my $res = $job_db->exec_statement( $sql_statement );
1791                         # Skip new jobs for host if there is a processing job
1792                         if(defined($res) and defined @{$res}[0]) {
1793                                 # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
1794                                 my $row = @{$res}[0] if (ref $res eq 'ARRAY');
1795                                 if(@{$row}[5] eq 'trigger_action_reinstall') {
1796                                         my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
1797                                         my $res_2 = $job_db->exec_statement( $sql_statement_2 );
1798                                         if(defined($res_2) and defined @{$res_2}[0]) {
1799                                                 # Set status from goto-activation to 'waiting' and update timestamp
1800                                                 $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting', timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
1801                                         }
1802                                 }
1803                                 next;
1804                         }
1806                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1807                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1808                                 if(defined($jobdb_id)) {
1809                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1811                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1812                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1813                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1815                                         # expect macaddress is unique!!!!!!
1816                                         my $target = $res_hash->{1}->{hostname};
1818                                         # change header
1819                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1821                                         # add sqlite_id
1822                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1824                                         $job_msg =~ /<header>(\S+)<\/header>/;
1825                                         my $header = $1 ;
1826                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
1828                                         # update status in job queue to ...
1829                     # ... 'processing', for jobs: 'reinstall', 'update'
1830                     if (($header =~ /gosa_trigger_action_reinstall/) 
1831                             || ($header =~ /gosa_trigger_activate_new/)
1832                             || ($header =~ /gosa_trigger_action_update/)) {
1833                         my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1834                         my $dbres = $job_db->update_dbentry($sql_statement);
1835                     }
1837                     # ... 'done', for all other jobs, they are no longer needed in the jobqueue
1838                     else {
1839                         my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
1840                         my $dbres = $job_db->update_dbentry($sql_statement);
1841                     }
1842                 
1844                                         # We don't want parallel processing
1845                                         last;
1846                                 }
1847                         }
1848                 }
1850                 $watch_for_new_jobs_in_progress = 0;
1851                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1852         }
1856 sub watch_for_new_messages {
1857     my ($kernel,$heap) = @_[KERNEL, HEAP];
1858     my @coll_user_msg;   # collection list of outgoing messages
1859     
1860     # check messaging_db for new incoming messages with executable timestamp
1861     my $timestamp = &get_time();
1862     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
1863     my $res = $messaging_db->exec_statement( $sql_statement );
1864         foreach my $hit (@{$res}) {
1866         # create outgoing messages
1867         my $message_to = @{$hit}[3];
1868         # translate message_to to plain login name
1869         my @message_to_l = split(/,/, $message_to);  
1870                 my %receiver_h; 
1871                 foreach my $receiver (@message_to_l) {
1872                         if ($receiver =~ /^u_([\s\S]*)$/) {
1873                                 $receiver_h{$1} = 0;
1874                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1875                                 my $group_name = $1;
1876                                 # fetch all group members from ldap and add them to receiver hash
1877                                 my $ldap_handle = &get_ldap_handle();
1878                                 if (defined $ldap_handle) {
1879                                                 my $mesg = $ldap_handle->search(
1880                                                                                 base => $ldap_base,
1881                                                                                 scope => 'sub',
1882                                                                                 attrs => ['memberUid'],
1883                                                                                 filter => "cn=$group_name",
1884                                                                                 );
1885                                                 if ($mesg->count) {
1886                                                                 my @entries = $mesg->entries;
1887                                                                 foreach my $entry (@entries) {
1888                                                                                 my @receivers= $entry->get_value("memberUid");
1889                                                                                 foreach my $receiver (@receivers) { 
1890                                                                                                 $receiver_h{$receiver} = 0;
1891                                                                                 }
1892                                                                 }
1893                                                 } 
1894                                                 # translating errors ?
1895                                                 if ($mesg->code) {
1896                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1897                                                 }
1898                                 # ldap handle error ?           
1899                                 } else {
1900                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1901                                 }
1902                         } else {
1903                                 my $sbjct = &encode_base64(@{$hit}[1]);
1904                                 my $msg = &encode_base64(@{$hit}[7]);
1905                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1906                         }
1907                 }
1908                 my @receiver_l = keys(%receiver_h);
1910         my $message_id = @{$hit}[0];
1912         #add each outgoing msg to messaging_db
1913         my $receiver;
1914         foreach $receiver (@receiver_l) {
1915             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1916                 "VALUES ('".
1917                 $message_id."', '".    # id
1918                 @{$hit}[1]."', '".     # subject
1919                 @{$hit}[2]."', '".     # message_from
1920                 $receiver."', '".      # message_to
1921                 "none"."', '".         # flag
1922                 "out"."', '".          # direction
1923                 @{$hit}[6]."', '".     # delivery_time
1924                 @{$hit}[7]."', '".     # message
1925                 $timestamp."'".     # timestamp
1926                 ")";
1927             &daemon_log("M DEBUG: $sql_statement", 1);
1928             my $res = $messaging_db->exec_statement($sql_statement);
1929             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1930         }
1932         # set incoming message to flag d=deliverd
1933         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1934         &daemon_log("M DEBUG: $sql_statement", 7);
1935         $res = $messaging_db->update_dbentry($sql_statement);
1936         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1937     }
1939     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1940     return;
1943 sub watch_for_delivery_messages {
1944     my ($kernel, $heap) = @_[KERNEL, HEAP];
1946     # select outgoing messages
1947     my $timestamp= &get_time();
1948     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' AND delivery_time<$timestamp)";
1949     #&daemon_log("0 DEBUG: $sql", 7);
1950     my $res = $messaging_db->exec_statement( $sql_statement );
1951     
1952     # build out msg for each    usr
1953     foreach my $hit (@{$res}) {
1954         my $receiver = @{$hit}[3];
1955         my $msg_id = @{$hit}[0];
1956         my $subject = @{$hit}[1];
1957         my $message = @{$hit}[7];
1959         # resolve usr -> host where usr is logged in
1960         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1961         #&daemon_log("0 DEBUG: $sql", 7);
1962         my $res = $login_users_db->exec_statement($sql);
1964         # receiver is logged in nowhere
1965         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1967         # receiver ist logged in at a client registered at local server
1968                 my $send_succeed = 0;
1969                 foreach my $hit (@$res) {
1970                                 my $receiver_host = @$hit[0];
1971                 my $delivered2host = 0;
1972                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1974                                 # Looking for host in know_clients_db 
1975                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1976                                 my $res = $known_clients_db->exec_statement($sql);
1978                 # Host is known in known_clients_db
1979                 if (ref(@$res[0]) eq "ARRAY") {
1980                     my $receiver_key = @{@{$res}[0]}[2];
1981                     my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1982                     my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1983                     my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1984                     if ($error == 0 ) {
1985                         $send_succeed++ ;
1986                         $delivered2host++ ;
1987                         &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
1988                     } else {
1989                         &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
1990                     }
1991                 }
1992                 
1993                 # Message already send, do not need to do anything more, otherwise ...
1994                 if ($delivered2host) { next;}
1995     
1996                 # ...looking for host in foreign_clients_db
1997                 $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
1998                 $res = $foreign_clients_db->exec_statement($sql);
1999   
2000                                 # Host is known in foreign_clients_db 
2001                                 if (ref(@$res[0]) eq "ARRAY") { 
2002                     my $registration_server = @{@{$res}[0]}[2];
2003                     
2004                     # Fetch encryption key for registration server
2005                     my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
2006                     my $res = $known_server_db->exec_statement($sql);
2007                     if (ref(@$res[0]) eq "ARRAY") { 
2008                         my $registration_server_key = @{@{$res}[0]}[3];
2009                         my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
2010                         my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
2011                         my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
2012                         if ($error == 0 ) {
2013                             $send_succeed++ ;
2014                             $delivered2host++ ;
2015                             &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
2016                         } else {
2017                             &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
2018                         }
2020                     } else {
2021                         &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
2022                                 "registrated at server '$registration_server', ".
2023                                 "but no data available in known_server_db ", 1); 
2024                     }
2025                 }
2026                 
2027                 if (not $delivered2host) {
2028                     &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
2029                 }
2030                 }
2032                 if ($send_succeed) {
2033                                 # set outgoing msg at db to deliverd
2034                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
2035                                 my $res = $messaging_db->exec_statement($sql); 
2036                 &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
2037                 } else {
2038             &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
2039         }
2040         }
2042     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
2043     return;
2047 sub watch_for_done_messages {
2048     my ($kernel,$heap) = @_[KERNEL, HEAP];
2050     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
2051     #&daemon_log("0 DEBUG: $sql", 7);
2052     my $res = $messaging_db->exec_statement($sql); 
2054     foreach my $hit (@{$res}) {
2055         my $msg_id = @{$hit}[0];
2057         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
2058         #&daemon_log("0 DEBUG: $sql", 7); 
2059         my $res = $messaging_db->exec_statement($sql);
2061         # not all usr msgs have been seen till now
2062         if ( ref(@$res[0]) eq "ARRAY") { next; }
2063         
2064         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
2065         #&daemon_log("0 DEBUG: $sql", 7);
2066         $res = $messaging_db->exec_statement($sql);
2067     
2068     }
2070     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
2071     return;
2075 sub watch_for_old_known_clients {
2076     my ($kernel,$heap) = @_[KERNEL, HEAP];
2078     my $sql_statement = "SELECT * FROM $known_clients_tn";
2079     my $res = $known_clients_db->select_dbentry( $sql_statement );
2081     my $cur_time = int(&get_time());
2083     while ( my ($hit_num, $hit) = each %$res) {
2084         my $expired_timestamp = int($hit->{'timestamp'});
2085         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
2086         my $dt = DateTime->new( year   => $1,
2087                 month  => $2,
2088                 day    => $3,
2089                 hour   => $4,
2090                 minute => $5,
2091                 second => $6,
2092                 );
2094         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
2095         $expired_timestamp = $dt->ymd('').$dt->hms('');
2096         if ($cur_time > $expired_timestamp) {
2097             my $hostname = $hit->{'hostname'};
2098             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
2099             my $del_res = $known_clients_db->exec_statement($del_sql);
2101             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
2102         }
2104     }
2106     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
2110 sub watch_for_next_tasks {
2111     my ($kernel,$heap) = @_[KERNEL, HEAP];
2113     my $sql = "SELECT * FROM $incoming_tn";
2114     my $res = $incoming_db->select_dbentry($sql);
2115     
2116     while ( my ($hit_num, $hit) = each %$res) {
2117         my $headertag = $hit->{'headertag'};
2118         if ($headertag =~ /^answer_(\d+)/) {
2119             # do not start processing, this message is for a still running POE::Wheel
2120             next;
2121         }
2122         my $message_id = $hit->{'id'};
2123         my $session_id = $hit->{'sessionid'};
2124         &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 7);
2125         $kernel->yield('next_task', $hit);
2127         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
2128         my $res = $incoming_db->exec_statement($sql);
2129     }
2131     $kernel->delay_set('watch_for_next_tasks', 1); 
2135 sub get_ldap_handle {
2136         my ($session_id) = @_;
2137         my $heap;
2138         my $ldap_handle;
2140         if (not defined $session_id ) { $session_id = 0 };
2141         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
2143         if ($session_id == 0) {
2144                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
2145                 $ldap_handle = Net::LDAP->new( $ldap_uri );
2146                 if (defined $ldap_handle) {
2147                         $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!"); 
2148                 } else {
2149                         daemon_log("$session_id ERROR: creation of a new LDAP handle failed (ldap_uri '$ldap_uri')");
2150                 }
2152         } else {
2153                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
2154                 if( defined $session_reference ) {
2155                         $heap = $session_reference->get_heap();
2156                 }
2158                 if (not defined $heap) {
2159                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
2160                         return;
2161                 }
2163                 # TODO: This "if" is nonsense, because it doesn't prove that the
2164                 #       used handle is still valid - or if we've to reconnect...
2165                 #if (not exists $heap->{ldap_handle}) {
2166                         $ldap_handle = Net::LDAP->new( $ldap_uri );
2167                         $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!"); 
2168                         $heap->{ldap_handle} = $ldap_handle;
2169                 #}
2170         }
2171         return $ldap_handle;
2175 sub change_fai_state {
2176     my ($st, $targets, $session_id) = @_;
2177     $session_id = 0 if not defined $session_id;
2178     # Set FAI state to localboot
2179     my %mapActions= (
2180         reboot    => '',
2181         update    => 'softupdate',
2182         localboot => 'localboot',
2183         reinstall => 'install',
2184         rescan    => '',
2185         wake      => '',
2186         memcheck  => 'memcheck',
2187         sysinfo   => 'sysinfo',
2188         install   => 'install',
2189     );
2191     # Return if this is unknown
2192     if (!exists $mapActions{ $st }){
2193         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
2194       return;
2195     }
2197     my $state= $mapActions{ $st };
2199     my $ldap_handle = &get_ldap_handle($session_id);
2200     if( defined($ldap_handle) ) {
2202       # Build search filter for hosts
2203         my $search= "(&(objectClass=GOhard)";
2204         foreach (@{$targets}){
2205             $search.= "(macAddress=$_)";
2206         }
2207         $search.= ")";
2209       # If there's any host inside of the search string, procress them
2210         if (!($search =~ /macAddress/)){
2211             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2212             return;
2213         }
2215       # Perform search for Unit Tag
2216       my $mesg = $ldap_handle->search(
2217           base   => $ldap_base,
2218           scope  => 'sub',
2219           attrs  => ['dn', 'FAIstate', 'objectClass'],
2220           filter => "$search"
2221           );
2223           if ($mesg->count) {
2224                   my @entries = $mesg->entries;
2225                   if (0 == @entries) {
2226                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2227                   }
2229                   foreach my $entry (@entries) {
2230                           # Only modify entry if it is not set to '$state'
2231                           if ($entry->get_value("FAIstate") ne "$state"){
2232                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2233                                   my $result;
2234                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2235                                   if (exists $tmp{'FAIobject'}){
2236                                           if ($state eq ''){
2237                                                   $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
2238                                           } else {
2239                                                   $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
2240                                           }
2241                                   } elsif ($state ne ''){
2242                                           $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
2243                                   }
2245                                   # Errors?
2246                                   if ($result->code){
2247                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2248                                   }
2249                           } else {
2250                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2251                           }  
2252                   }
2253           } else {
2254                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2255           }
2257     # if no ldap handle defined
2258     } else {
2259         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2260     }
2262         return;
2266 sub change_goto_state {
2267     my ($st, $targets, $session_id) = @_;
2268     $session_id = 0  if not defined $session_id;
2270     # Switch on or off?
2271     my $state= $st eq 'active' ? 'active': 'locked';
2273     my $ldap_handle = &get_ldap_handle($session_id);
2274     if( defined($ldap_handle) ) {
2276       # Build search filter for hosts
2277       my $search= "(&(objectClass=GOhard)";
2278       foreach (@{$targets}){
2279         $search.= "(macAddress=$_)";
2280       }
2281       $search.= ")";
2283       # If there's any host inside of the search string, procress them
2284       if (!($search =~ /macAddress/)){
2285         return;
2286       }
2288       # Perform search for Unit Tag
2289       my $mesg = $ldap_handle->search(
2290           base   => $ldap_base,
2291           scope  => 'sub',
2292           attrs  => ['dn', 'gotoMode'],
2293           filter => "$search"
2294           );
2296       if ($mesg->count) {
2297         my @entries = $mesg->entries;
2298         foreach my $entry (@entries) {
2300           # Only modify entry if it is not set to '$state'
2301           if ($entry->get_value("gotoMode") ne $state){
2303             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2304             my $result;
2305             $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
2307             # Errors?
2308             if ($result->code){
2309               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2310             }
2312           }
2313         }
2314       } else {
2315                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2316           }
2318     }
2322 sub run_recreate_packages_db {
2323     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2324     my $session_id = $session->ID;
2325         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2326         $kernel->yield('create_fai_release_db', $fai_release_tn);
2327         $kernel->yield('create_fai_server_db', $fai_server_tn);
2328         return;
2332 sub run_create_fai_server_db {
2333     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2334     my $session_id = $session->ID;
2335     my $task = POE::Wheel::Run->new(
2336             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2337             StdoutEvent  => "session_run_result",
2338             StderrEvent  => "session_run_debug",
2339             CloseEvent   => "session_run_done",
2340             );
2342     $heap->{task}->{ $task->ID } = $task;
2343     return;
2347 sub create_fai_server_db {
2348         my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2349         my $result;
2351         if (not defined $session_id) { $session_id = 0; }
2352         my $ldap_handle = &get_ldap_handle();
2353         if(defined($ldap_handle)) {
2354                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2355                 my $mesg= $ldap_handle->search(
2356                         base   => $ldap_base,
2357                         scope  => 'sub',
2358                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2359                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2360                 );
2361                 if($mesg->{'resultCode'} == 0 &&
2362                         $mesg->count != 0) {
2363                         foreach my $entry (@{$mesg->{entries}}) {
2364                                 if($entry->exists('FAIrepository')) {
2365                                         # Add an entry for each Repository configured for server
2366                                         foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2367                                                 my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2368                                                 my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2369                                                 $result= $fai_server_db->add_dbentry( { 
2370                                                                 table => $table_name,
2371                                                                 primkey => ['server', 'fai_release', 'tag'],
2372                                                                 server => $tmp_url,
2373                                                                 fai_release => $tmp_release,
2374                                                                 sections => $tmp_sections,
2375                                                                 tag => (length($tmp_tag)>0)?$tmp_tag:"",
2376                                                         } );
2377                                         }
2378                                 }
2379                         }
2380                 }
2381                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2383                 # TODO: Find a way to post the 'create_packages_list_db' event
2384                 if(not defined($dont_create_packages_list)) {
2385                         &create_packages_list_db(undef, undef, $session_id);
2386                 }
2387         }       
2389         $ldap_handle->disconnect;
2390         return $result;
2394 sub run_create_fai_release_db {
2395         my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2396         my $session_id = $session->ID;
2397         my $task = POE::Wheel::Run->new(
2398                 Program => sub { &create_fai_release_db($table_name, $session_id) },
2399                 StdoutEvent  => "session_run_result",
2400                 StderrEvent  => "session_run_debug",
2401                 CloseEvent   => "session_run_done",
2402         );
2404         $heap->{task}->{ $task->ID } = $task;
2405         return;
2409 sub create_fai_release_db {
2410         my ($table_name, $session_id) = @_;
2411         my $result;
2413         # used for logging
2414         if (not defined $session_id) { $session_id = 0; }
2416         my $ldap_handle = &get_ldap_handle();
2417         if(defined($ldap_handle)) {
2418                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2419                 my $mesg= $ldap_handle->search(
2420                         base   => $ldap_base,
2421                         scope  => 'sub',
2422                         attrs  => [],
2423                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2424                 );
2425                 if(($mesg->code == 0) && ($mesg->count != 0))
2426                 {
2427                         daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,8);
2429                         # Walk through all possible FAI container ou's
2430                         my @sql_list;
2431                         my $timestamp= &get_time();
2432                         foreach my $ou (@{$mesg->{entries}}) {
2433                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2434                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2435                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2436                                         if(@tmp_array) {
2437                                                 foreach my $entry (@tmp_array) {
2438                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2439                                                                 my $sql= 
2440                                                                 "INSERT INTO $table_name "
2441                                                                 ."(timestamp, fai_release, class, type, state) VALUES ("
2442                                                                 .$timestamp.","
2443                                                                 ."'".$entry->{'release'}."',"
2444                                                                 ."'".$entry->{'class'}."',"
2445                                                                 ."'".$entry->{'type'}."',"
2446                                                                 ."'".$entry->{'state'}."')";
2447                                                                 push @sql_list, $sql;
2448                                                         }
2449                                                 }
2450                                         }
2451                                 }
2452                         }
2454                         daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",8);
2455                         if(@sql_list) {
2456                                 unshift @sql_list, "VACUUM";
2457                                 unshift @sql_list, "DELETE FROM $table_name";
2458                                 $fai_release_db->exec_statementlist(\@sql_list);
2459                         }
2460                         daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",7);
2461                 } else {
2462                         daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
2463                 }
2464                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2465         }
2466         $ldap_handle->disconnect;
2467         return $result;
2470 sub get_fai_types {
2471         my $tmp_classes = shift || return undef;
2472         my @result;
2474         foreach my $type(keys %{$tmp_classes}) {
2475                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2476                         my $entry = {
2477                                 type => $type,
2478                                 state => $tmp_classes->{$type}[0],
2479                         };
2480                         push @result, $entry;
2481                 }
2482         }
2484         return @result;
2487 sub get_fai_state {
2488         my $result = "";
2489         my $tmp_classes = shift || return $result;
2491         foreach my $type(keys %{$tmp_classes}) {
2492                 if(defined($tmp_classes->{$type}[0])) {
2493                         $result = $tmp_classes->{$type}[0];
2494                         
2495                 # State is equal for all types in class
2496                         last;
2497                 }
2498         }
2500         return $result;
2503 sub resolve_fai_classes {
2504         my ($fai_base, $ldap_handle, $session_id) = @_;
2505         if (not defined $session_id) { $session_id = 0; }
2506         my $result;
2507         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2508         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2509         my $fai_classes;
2511         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2512         my $mesg= $ldap_handle->search(
2513                 base   => $fai_base,
2514                 scope  => 'sub',
2515                 attrs  => ['cn','objectClass','FAIstate'],
2516                 filter => $fai_filter,
2517         );
2518         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2520         if($mesg->{'resultCode'} == 0 &&
2521                 $mesg->count != 0) {
2522                 foreach my $entry (@{$mesg->{entries}}) {
2523                         if($entry->exists('cn')) {
2524                                 my $tmp_dn= $entry->dn();
2525                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2526                                         - length($fai_base) - 1 );
2528                                 # Skip classname and ou dn parts for class
2529                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
2531                                 # Skip classes without releases
2532                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2533                                         next;
2534                                 }
2536                                 my $tmp_cn= $entry->get_value('cn');
2537                                 my $tmp_state= $entry->get_value('FAIstate');
2539                                 my $tmp_type;
2540                                 # Get FAI type
2541                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2542                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2543                                                 $tmp_type= $oclass;
2544                                                 last;
2545                                         }
2546                                 }
2548                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2549                                         # A Subrelease
2550                                         my @sub_releases = split(/,/, $tmp_release);
2552                                         # Walk through subreleases and build hash tree
2553                                         my $hash;
2554                                         while(my $tmp_sub_release = pop @sub_releases) {
2555                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2556                                         }
2557                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2558                                 } else {
2559                                         # A branch, no subrelease
2560                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2561                                 }
2562                         } elsif (!$entry->exists('cn')) {
2563                                 my $tmp_dn= $entry->dn();
2564                                 $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
2565                                         - length($fai_base) - 1 );
2566                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
2568                                 # Skip classes without releases
2569                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2570                                         next;
2571                                 }
2573                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2574                                         # A Subrelease
2575                                         my @sub_releases= split(/,/, $tmp_release);
2577                                         # Walk through subreleases and build hash tree
2578                                         my $hash;
2579                                         while(my $tmp_sub_release = pop @sub_releases) {
2580                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2581                                         }
2582                                         # Remove the last two characters
2583                                         chop($hash);
2584                                         chop($hash);
2586                                         eval('$fai_classes->'.$hash.'= {}');
2587                                 } else {
2588                                         # A branch, no subrelease
2589                                         if(!exists($fai_classes->{$tmp_release})) {
2590                                                 $fai_classes->{$tmp_release} = {};
2591                                         }
2592                                 }
2593                         }
2594                 }
2596                 # The hash is complete, now we can honor the copy-on-write based missing entries
2597                 foreach my $release (keys %$fai_classes) {
2598                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2599                 }
2600         }
2601         return $result;
2604 sub apply_fai_inheritance {
2605        my $fai_classes = shift || return {};
2606        my $tmp_classes;
2608        # Get the classes from the branch
2609        foreach my $class (keys %{$fai_classes}) {
2610                # Skip subreleases
2611                if($class =~ /^ou=.*$/) {
2612                        next;
2613                } else {
2614                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2615                }
2616        }
2618        # Apply to each subrelease
2619        foreach my $subrelease (keys %{$fai_classes}) {
2620                if($subrelease =~ /ou=/) {
2621                        foreach my $tmp_class (keys %{$tmp_classes}) {
2622                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2623                                        $fai_classes->{$subrelease}->{$tmp_class} =
2624                                        deep_copy($tmp_classes->{$tmp_class});
2625                                } else {
2626                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2627                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2628                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2629                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2630                                                }
2631                                        }
2632                                }
2633                        }
2634                }
2635        }
2637        # Find subreleases in deeper levels
2638        foreach my $subrelease (keys %{$fai_classes}) {
2639                if($subrelease =~ /ou=/) {
2640                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2641                                if($subsubrelease =~ /ou=/) {
2642                                        apply_fai_inheritance($fai_classes->{$subrelease});
2643                                }
2644                        }
2645                }
2646        }
2648        return $fai_classes;
2651 sub get_fai_release_entries {
2652         my $tmp_classes = shift || return;
2653         my $parent = shift || "";
2654         my @result = shift || ();
2656         foreach my $entry (keys %{$tmp_classes}) {
2657                 if(defined($entry)) {
2658                         if($entry =~ /^ou=.*$/) {
2659                                 my $release_name = $entry;
2660                                 $release_name =~ s/ou=//g;
2661                                 if(length($parent)>0) {
2662                                         $release_name = $parent."/".$release_name;
2663                                 }
2664                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2665                                 foreach my $bufentry(@bufentries) {
2666                                         push @result, $bufentry;
2667                                 }
2668                         } else {
2669                                 my @types = get_fai_types($tmp_classes->{$entry});
2670                                 foreach my $type (@types) {
2671                                         push @result, 
2672                                         {
2673                                                 'class' => $entry,
2674                                                 'type' => $type->{'type'},
2675                                                 'release' => $parent,
2676                                                 'state' => $type->{'state'},
2677                                         };
2678                                 }
2679                         }
2680                 }
2681         }
2683         return @result;
2686 sub deep_copy {
2687         my $this = shift;
2688         if (not ref $this) {
2689                 $this;
2690         } elsif (ref $this eq "ARRAY") {
2691                 [map deep_copy($_), @$this];
2692         } elsif (ref $this eq "HASH") {
2693                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2694         } else { die "what type is $_?" }
2698 sub session_run_result {
2699     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2700     $kernel->sig(CHLD => "child_reap");
2703 sub session_run_debug {
2704     my $result = $_[ARG0];
2705     print STDERR "$result\n";
2708 sub session_run_done {
2709     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2710     delete $heap->{task}->{$task_id};
2714 sub create_sources_list {
2715         my $session_id = shift;
2716         my $ldap_handle = &main::get_ldap_handle;
2717         my $result="/tmp/gosa_si_tmp_sources_list";
2719         # Remove old file
2720         if(stat($result)) {
2721                 unlink($result);
2722                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2723         }
2725         my $fh;
2726         open($fh, ">$result");
2727         if (not defined $fh) {
2728                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2729                 return undef;
2730         }
2731         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2732                 my $mesg=$ldap_handle->search(
2733                         base    => $main::ldap_server_dn,
2734                         scope   => 'base',
2735                         attrs   => 'FAIrepository',
2736                         filter  => 'objectClass=FAIrepositoryServer'
2737                 );
2738                 if($mesg->count) {
2739                         foreach my $entry(@{$mesg->{'entries'}}) {
2740                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2741                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2742                                         my $line = "deb $server $release";
2743                                         $sections =~ s/,/ /g;
2744                                         $line.= " $sections";
2745                                         print $fh $line."\n";
2746                                 }
2747                         }
2748                 }
2749         } else {
2750                 if (defined $main::ldap_server_dn){
2751                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2752                 } else {
2753                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2754                 }
2755         }
2756         close($fh);
2758         return $result;
2762 sub run_create_packages_list_db {
2763     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2764         my $session_id = $session->ID;
2766         my $task = POE::Wheel::Run->new(
2767                                         Priority => +20,
2768                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2769                                         StdoutEvent  => "session_run_result",
2770                                         StderrEvent  => "session_run_debug",
2771                                         CloseEvent   => "session_run_done",
2772                                         );
2773         $heap->{task}->{ $task->ID } = $task;
2777 sub create_packages_list_db {
2778         my ($ldap_handle, $sources_file, $session_id) = @_;
2780         # Cleanup repo basepath
2781         $repo_basepath = File::Spec->canonpath($repo_basepath);
2783         # it should not be possible to trigger a recreation of packages_list_db
2784         # while packages_list_db is under construction, so set flag packages_list_under_construction
2785         # which is tested befor recreation can be started
2786         if (-r $packages_list_under_construction) {
2787                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2788                 return;
2789         } else {
2790                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2791                 # set packages_list_under_construction to true
2792                 system("touch $packages_list_under_construction");
2793                 @packages_list_statements=();
2794         }
2796         if (not defined $session_id) { $session_id = 0; }
2797         if (not defined $ldap_handle) { 
2798                 $ldap_handle= &get_ldap_handle();
2800                 if (not defined $ldap_handle) {
2801                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2802                         unlink($packages_list_under_construction);
2803                         return;
2804                 }
2805         }
2806         if (not defined $sources_file) { 
2807                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2808                 $sources_file = &create_sources_list($session_id);
2809         }
2811         if (not defined $sources_file) {
2812                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2813                 unlink($packages_list_under_construction);
2814                 return;
2815         }
2817         my $line;
2819         open(CONFIG, "<$sources_file") or do {
2820                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2821                 unlink($packages_list_under_construction);
2822                 return;
2823         };
2825         # Read lines
2826         while ($line = <CONFIG>){
2827                 # Unify
2828                 chop($line);
2829                 $line =~ s/^\s+//;
2830                 $line =~ s/^\s+/ /;
2832                 # Strip comments
2833                 $line =~ s/#.*$//g;
2835                 # Skip empty lines
2836                 if ($line =~ /^\s*$/){
2837                         next;
2838                 }
2840                 # Interpret deb line
2841                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2842                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2843                         my $section;
2844                         foreach $section (split(' ', $sections)){
2845                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2846                         }
2847                 }
2848         }
2850         close (CONFIG);
2852         if(keys(%repo_dirs)) {
2853                 find(\&cleanup_and_extract, keys( %repo_dirs ));
2854                 &main::strip_packages_list_statements();
2855                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2856         }
2857         unlink($packages_list_under_construction);
2858         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2859         return;
2862 # This function should do some intensive task to minimize the db-traffic
2863 sub strip_packages_list_statements {
2864         my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2865         my @new_statement_list=();
2866         my $hash;
2867         my $insert_hash;
2868         my $update_hash;
2869         my $delete_hash;
2870         my $known_packages_hash;
2871         my $local_timestamp=get_time();
2873         foreach my $existing_entry (@existing_entries) {
2874                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2875         }
2877         foreach my $statement (@packages_list_statements) {
2878                 if($statement =~ /^INSERT/i) {
2879                         # Assign the values from the insert statement
2880                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2881                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2882                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2883                                 # If section or description has changed, update the DB
2884                                 if( 
2885                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2886                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2887                                 ) {
2888                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2889                                 } else {
2890                                         # package is already present in database. cache this knowledge for later use
2891                                         @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2892                                 }
2893                         } else {
2894                                 # Insert a non-existing entry to db
2895                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2896                         }
2897                 } elsif ($statement =~ /^UPDATE/i) {
2898                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2899                         /^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;
2900                         foreach my $distribution (keys %{$hash}) {
2901                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2902                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2903                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2904                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2905                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2906                                                 my $section;
2907                                                 my $description;
2908                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2909                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2910                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2911                                                 }
2912                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2913                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2914                                                 }
2915                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2916                                         }
2917                                 }
2918                         }
2919                 }
2920         }
2922         # Check for orphaned entries
2923         foreach my $existing_entry (@existing_entries) {
2924                 my $distribution= @{$existing_entry}[0];
2925                 my $package= @{$existing_entry}[1];
2926                 my $version= @{$existing_entry}[2];
2927                 my $section= @{$existing_entry}[3];
2929                 if(
2930                         exists($insert_hash->{$distribution}->{$package}->{$version}) ||
2931                         exists($update_hash->{$distribution}->{$package}->{$version}) ||
2932                         exists($known_packages_hash->{$distribution}->{$package}->{$version})
2933                 ) {
2934                         next;
2935                 } else {
2936                         # Insert entry to delete hash
2937                         @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
2938                 }
2939         }
2941         # unroll the insert hash
2942         foreach my $distribution (keys %{$insert_hash}) {
2943                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2944                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2945                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2946                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2947                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2948                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2949                                 ."'$local_timestamp')";
2950                         }
2951                 }
2952         }
2954         # unroll the update hash
2955         foreach my $distribution (keys %{$update_hash}) {
2956                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2957                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2958                                 my $set = "";
2959                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2960                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2961                                 }
2962                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2963                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2964                                 }
2965                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2966                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2967                                 }
2968                                 if(defined($set) and length($set) > 0) {
2969                                         $set .= "timestamp = '$local_timestamp'";
2970                                 } else {
2971                                         next;
2972                                 }
2973                                 push @new_statement_list, 
2974                                 "UPDATE $main::packages_list_tn SET $set WHERE"
2975                                 ." distribution = '$distribution'"
2976                                 ." AND package = '$package'"
2977                                 ." AND version = '$version'";
2978                         }
2979                 }
2980         }
2981         
2982         # unroll the delete hash
2983         foreach my $distribution (keys %{$delete_hash}) {
2984                 foreach my $package (keys %{$delete_hash->{$distribution}}) {
2985                         foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
2986                                 my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
2987                                 push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
2988                         }
2989                 }
2990         }
2992         unshift(@new_statement_list, "VACUUM");
2994         @packages_list_statements = @new_statement_list;
2998 sub parse_package_info {
2999     my ($baseurl, $dist, $section, $session_id)= @_;
3000     my ($package);
3001     if (not defined $session_id) { $session_id = 0; }
3002     my ($path) = ($baseurl =~ m%://[^/]*/(.*)$%);
3004     my $repo_path = File::Spec->join($repo_basepath, $path);
3006     $repo_dirs{ "${repo_path}/pool" } = 1;
3008     foreach $package ("Packages.gz"){
3009         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
3010         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
3011         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
3012     }
3013     
3017 sub get_package {
3018     my ($url, $dest, $session_id)= @_;
3019     if (not defined $session_id) { $session_id = 0; }
3021     my $tpath = dirname($dest);
3022     -d "$tpath" || mkpath "$tpath";
3024     # This is ugly, but I've no time to take a look at "how it works in perl"
3025     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
3026         system("gunzip -cd '$dest' > '$dest.in'");
3027         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
3028         unlink($dest);
3029         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
3030     } else {
3031         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
3032     }
3033     return 0;
3037 sub parse_package {
3038     my ($path, $dist, $srv_path, $session_id)= @_;
3039     if (not defined $session_id) { $session_id = 0;}
3040     my ($package, $version, $section, $description);
3041     my $PACKAGES;
3042     my $timestamp = &get_time();
3044     if(not stat("$path.in")) {
3045         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
3046         return;
3047     }
3049     open($PACKAGES, "<$path.in");
3050     if(not defined($PACKAGES)) {
3051         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
3052         return;
3053     }
3055     # Read lines
3056     while (<$PACKAGES>){
3057         my $line = $_;
3058         # Unify
3059         chop($line);
3061         # Use empty lines as a trigger
3062         if ($line =~ /^\s*$/){
3063             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
3064             push(@packages_list_statements, $sql);
3065             $package = "none";
3066             $version = "none";
3067             $section = "none";
3068             $description = "none"; 
3069             next;
3070         }
3072         # Trigger for package name
3073         if ($line =~ /^Package:\s/){
3074             ($package)= ($line =~ /^Package: (.*)$/);
3075             next;
3076         }
3078         # Trigger for version
3079         if ($line =~ /^Version:\s/){
3080             ($version)= ($line =~ /^Version: (.*)$/);
3081             next;
3082         }
3084         # Trigger for description
3085         if ($line =~ /^Description:\s/){
3086             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
3087             next;
3088         }
3090         # Trigger for section
3091         if ($line =~ /^Section:\s/){
3092             ($section)= ($line =~ /^Section: (.*)$/);
3093             next;
3094         }
3096         # Trigger for filename
3097         if ($line =~ /^Filename:\s/){
3098             my ($filename) = ($line =~ /^Filename: (.*)$/);
3099             # Construct real path
3100             my $repo_path = File::Spec->join($repo_basepath, $srv_path);
3101             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
3102             next;
3103         }
3104     }
3106     close( $PACKAGES );
3107     unlink( "$path.in" );
3111 sub store_fileinfo {
3112     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
3114     my %fileinfo = (
3115         'package' => $package,
3116         'dist' => $dist,
3117         'version' => $vers,
3118     );
3120     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
3124 sub cleanup_and_extract {
3125         my $fileinfo = $repo_files{ $File::Find::name };
3127         if( defined $fileinfo ) {
3128                 my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
3129                 my $sql;
3130                 my $package = $fileinfo->{ 'package' };
3131                 my $newver = $fileinfo->{ 'version' };
3133                 mkpath($dir);
3134                 system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
3136                 if( -f "$dir/DEBIAN/templates" ) {
3138                         daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 7);
3140                         my $tmpl= ""; {
3141                                 local $/=undef;
3142                                 open FILE, "$dir/DEBIAN/templates";
3143                                 $tmpl = &encode_base64(<FILE>);
3144                                 close FILE;
3145                         }
3146                         rmtree("$dir/DEBIAN/templates");
3148                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
3149                         push @packages_list_statements, $sql;
3150                 }
3151         }
3153         return;
3157 sub register_at_foreign_servers {   
3158     my ($kernel) = $_[KERNEL];
3160     # hole alle bekannten server aus known_server_db
3161     my $server_sql = "SELECT * FROM $known_server_tn";
3162     my $server_res = $known_server_db->exec_statement($server_sql);
3164     # no entries in known_server_db
3165     if (not ref(@$server_res[0]) eq "ARRAY") { 
3166         # TODO
3167     }
3169     # detect already connected clients
3170     my $client_sql = "SELECT * FROM $known_clients_tn"; 
3171     my $client_res = $known_clients_db->exec_statement($client_sql);
3173     # send my server details to all other gosa-si-server within the network
3174     foreach my $hit (@$server_res) {
3175         my $hostname = @$hit[0];
3176         my $hostkey = &create_passwd;
3178         # add already connected clients to registration message 
3179         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
3180         &add_content2xml_hash($myhash, 'key', $hostkey);
3181         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
3183         # add locally loaded gosa-si modules to registration message
3184         my $loaded_modules = {};
3185         while (my ($package, $pck_info) = each %$known_modules) {
3186                                                 next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
3187                                                 foreach my $act_module (keys(%{@$pck_info[2]})) {
3188                                                         $loaded_modules->{$act_module} = ""; 
3189                                                 }
3190         }
3192         map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
3194         # add macaddress to registration message
3195         my ($host_ip, $host_port) = split(/:/, $hostname);
3196         my $local_ip = &get_local_ip_for_remote_ip($host_ip);
3197         my $network_interface= &get_interface_for_ip($local_ip);
3198         my $host_mac = &get_mac_for_interface($network_interface);
3199         &add_content2xml_hash($myhash, 'macaddress', $host_mac);
3200         
3201         # build registration message and send it
3202         my $foreign_server_msg = &create_xml_string($myhash);
3203         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
3204     }
3205     
3206     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
3207     return;
3211 #==== MAIN = main ==============================================================
3212 #  parse commandline options
3213 Getopt::Long::Configure( "bundling" );
3214 GetOptions("h|help" => \&usage,
3215         "c|config=s" => \$cfg_file,
3216         "f|foreground" => \$foreground,
3217         "v|verbose+" => \$verbose,
3218         "no-arp+" => \$no_arp,
3219            );
3221 # Prepare UID / GID as daemon_log may need it quite early
3222 $root_uid = getpwnam('root');
3223 $adm_gid = getgrnam('adm');
3225 #  read and set config parameters
3226 &check_cmdline_param ;
3227 &read_configfile($cfg_file, %cfg_defaults);
3228 &check_pid;
3230 $SIG{CHLD} = 'IGNORE';
3232 # Just fork, if we are not in foreground mode
3233 if( ! $foreground ) { 
3234     if (! chdir('/')) {
3235         daemon_log("Can't chdir to /: $!", 1);
3236         exit( 1 );
3237     }
3238     umask( 0 );
3239     $pid = fork;
3240 } else { 
3241     $pid = $$; 
3244 if( 0 != $pid ) {
3245     # Parent: put PID into the $pid_file
3246     open( LOCK_FILE, ">$pid_file" );
3247     print LOCK_FILE "$pid\n";
3248     close( LOCK_FILE );
3249     if( !$foreground ) { 
3250         exit( 0 );
3253 else {
3254     # Child
3255     open( STDIN,  '+>/dev/null' );
3256     open( STDOUT, '+>&STDIN'    );
3257     open( STDERR, '+>&STDIN'    );
3258     if (! POSIX::setsid()) {
3259         daemon_log("Can't start a new session: $!");
3260         exit( 1 );
3261     }
3262     $poe_kernel->has_forked() if ($poe_kernel->can('has_forked'));
3266 # parse head url and revision from svn
3267 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3268 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3269 $server_headURL = defined $1 ? $1 : 'unknown' ;
3270 $server_revision = defined $2 ? $2 : 'unknown' ;
3271 if ($server_headURL =~ /\/tag\// || 
3272         $server_headURL =~ /\/branches\// ) {
3273     $server_status = "stable"; 
3274 } else {
3275     $server_status = "developmental" ;
3278 # Prepare log file and set permissons
3279 open(FH, ">>$log_file");
3280 close FH;
3281 chmod(0440, $log_file);
3282 chown($root_uid, $adm_gid, $log_file);
3283 chown($root_uid, $adm_gid, "/var/lib/gosa-si");
3285 daemon_log(" ", 1);
3286 daemon_log("$0 started!", 1);
3287 daemon_log("status: $server_status", 1);
3288 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3291     no strict "refs";
3293     if ($db_module eq "DBmysql") {
3294         # connect to incoming_db
3295         $incoming_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3297         # connect to gosa-si job queue
3298         $job_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3300         # connect to known_clients_db
3301         $known_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3303         # connect to foreign_clients_db
3304         $foreign_clients_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3306         # connect to known_server_db
3307         $known_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3309         # connect to login_usr_db
3310         $login_users_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3312         # connect to fai_server_db 
3313         $fai_server_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3315         # connect to fai_release_db
3316         $fai_release_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3318         # connect to packages_list_db
3319         $packages_list_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3321         # connect to messaging_db
3322         $messaging_db = ("GOSA::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
3324     } elsif ($db_module eq "DBsqlite") {
3325         # connect to incoming_db
3326         unlink($incoming_file_name);
3327         $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3328         
3329         # connect to gosa-si job queue
3330         unlink($job_queue_file_name);  ## just for debugging
3331         $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3332         chmod(0640, $job_queue_file_name);
3333         chown($root_uid, $adm_gid, $job_queue_file_name);
3334         
3335         # connect to known_clients_db
3336         unlink($known_clients_file_name);   ## just for debugging
3337         $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3338         chmod(0640, $known_clients_file_name);
3339         chown($root_uid, $adm_gid, $known_clients_file_name);
3340         
3341         # connect to foreign_clients_db
3342         unlink($foreign_clients_file_name);
3343         $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3344         chmod(0640, $foreign_clients_file_name);
3345         chown($root_uid, $adm_gid, $foreign_clients_file_name);
3346         
3347         # connect to known_server_db
3348         unlink($known_server_file_name);
3349         $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3350         chmod(0640, $known_server_file_name);
3351         chown($root_uid, $adm_gid, $known_server_file_name);
3352         
3353         # connect to login_usr_db
3354         unlink($login_users_file_name);
3355         $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3356         chmod(0640, $login_users_file_name);
3357         chown($root_uid, $adm_gid, $login_users_file_name);
3358         
3359         # connect to fai_server_db
3360         unlink($fai_server_file_name);
3361         $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3362         chmod(0640, $fai_server_file_name);
3363         chown($root_uid, $adm_gid, $fai_server_file_name);
3364         
3365         # connect to fai_release_db
3366         unlink($fai_release_file_name);
3367         $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3368         chmod(0640, $fai_release_file_name);
3369         chown($root_uid, $adm_gid, $fai_release_file_name);
3370         
3371         # connect to packages_list_db
3372         #unlink($packages_list_file_name);
3373         unlink($packages_list_under_construction);
3374         $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3375         chmod(0640, $packages_list_file_name);
3376         chown($root_uid, $adm_gid, $packages_list_file_name);
3377         
3378         # connect to messaging_db
3379         unlink($messaging_file_name);
3380         $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3381         chmod(0640, $messaging_file_name);
3382         chown($root_uid, $adm_gid, $messaging_file_name);
3383     }
3387 # Creating tables
3388 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3389 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3390 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3391 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3392 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3393 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3394 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3395 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3396 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3397 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3399 # create xml object used for en/decrypting
3400 $xml = new XML::Simple();
3403 # foreign servers 
3404 my @foreign_server_list;
3406 # add foreign server from cfg file
3407 if ($foreign_server_string ne "") {
3408     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3409     foreach my $foreign_server (@cfg_foreign_server_list) {
3410         push(@foreign_server_list, $foreign_server);
3411     }
3413     daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
3416 # Perform a DNS lookup for server registration if flag is true
3417 if ($dns_lookup eq "true") {
3418     # Add foreign server from dns
3419     my @tmp_servers;
3420     if (not $server_domain) {
3421         # Try our DNS Searchlist
3422         for my $domain(get_dns_domains()) {
3423             chomp($domain);
3424             my ($tmp_domains, $error_string) = &get_server_addresses($domain);
3425             if(@$tmp_domains) {
3426                 for my $tmp_server(@$tmp_domains) {
3427                     push @tmp_servers, $tmp_server;
3428                 }
3429             }
3430         }
3431         if(@tmp_servers && length(@tmp_servers)==0) {
3432             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3433         }
3434     } else {
3435         @tmp_servers = &get_server_addresses($server_domain);
3436         if( 0 == @tmp_servers ) {
3437             daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3438         }
3439     }
3441     daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
3443     foreach my $server (@tmp_servers) { 
3444         unshift(@foreign_server_list, $server); 
3445     }
3446 } else {
3447     daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
3451 # eliminate duplicate entries
3452 @foreign_server_list = &del_doubles(@foreign_server_list);
3453 my $all_foreign_server = join(", ", @foreign_server_list);
3454 daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
3456 # add all found foreign servers to known_server
3457 my $cur_timestamp = &get_time();
3458 foreach my $foreign_server (@foreign_server_list) {
3460         # do not add myself to known_server_db
3461         if (&is_local($foreign_server)) { next; }
3462         ######################################
3464     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3465             primkey=>['hostname'],
3466             hostname=>$foreign_server,
3467             macaddress=>"",
3468             status=>'not_yet_registered',
3469             hostkey=>"none",
3470             loaded_modules => "none", 
3471             timestamp=>$cur_timestamp,
3472             } );
3476 # Import all modules
3477 &import_modules;
3479 # Check wether all modules are gosa-si valid passwd check
3480 &password_check;
3482 # Prepare for using Opsi 
3483 if ($opsi_enabled eq "true") {
3484     use JSON::RPC::Client;
3485     use XML::Quote qw(:all);
3486     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3487     $opsi_client = new JSON::RPC::Client;
3491 POE::Component::Server::TCP->new(
3492         Alias => "TCP_SERVER",
3493         Port => $server_port,
3494         ClientInput => sub {
3495                 my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
3496         my $session_id = $session->ID;
3497         my $remote_ip = $heap->{'remote_ip'};
3498                 push(@msgs_to_decrypt, $input);
3499         &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 7);
3500                 $kernel->yield("msg_to_decrypt");
3501         },
3502         InlineStates => {
3503                 msg_to_decrypt => \&msg_to_decrypt,
3504                 next_task => \&next_task,
3505                 task_result => \&handle_task_result,
3506                 task_done   => \&handle_task_done,
3507                 task_debug  => \&handle_task_debug,
3508                 child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3509         }
3510 );
3512 daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
3514 # create session for repeatedly checking the job queue for jobs
3515 POE::Session->create(
3516         inline_states => {
3517                 _start => \&session_start,
3518         _stop => \&session_stop,
3519         register_at_foreign_servers => \&register_at_foreign_servers,
3520         next_task => \&next_task,
3521         task_result => \&handle_task_result,
3522         task_done   => \&handle_task_done,
3523         task_debug  => \&handle_task_debug,
3524         watch_for_next_tasks => \&watch_for_next_tasks,
3525         watch_for_new_messages => \&watch_for_new_messages,
3526         watch_for_delivery_messages => \&watch_for_delivery_messages,
3527         watch_for_done_messages => \&watch_for_done_messages,
3528                 watch_for_new_jobs => \&watch_for_new_jobs,
3529         watch_for_modified_jobs => \&watch_for_modified_jobs,
3530         watch_for_done_jobs => \&watch_for_done_jobs,
3531         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3532         watch_for_old_known_clients => \&watch_for_old_known_clients,
3533         create_packages_list_db => \&run_create_packages_list_db,
3534         create_fai_server_db => \&run_create_fai_server_db,
3535         create_fai_release_db => \&run_create_fai_release_db,
3536                 recreate_packages_db => \&run_recreate_packages_db,
3537         session_run_result => \&session_run_result,
3538         session_run_debug => \&session_run_debug,
3539         session_run_done => \&session_run_done,
3540         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3541         }
3542 );
3545 POE::Kernel->run();
3546 exit;