Code

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